Отже, маємо розгорнутий кластер Kubernetes – див. серію Terraform: створення EKS, частина 1 – VPC, Subnets та Endpoints.
Маємо GitHub Actions workflow для його деплою – див. GitHub Actions: деплой Dev/Prod оточень з Terraform.
Прийшов час почати деплоїти наш бекенд в Kubernetes.
Тут знов використаємо GitHub Actions – будемо білдити Docker-образ з API-сервісом бекенду, зберігати його в AWS Elastic Container Service, а потім деплоїти Helm-чарт, якому у values передамо новий Docker Tag.
Робоче оточення поки одне, Dev, пізніше додамо ще Staging та Production. Крім того, треба мати можливість задеплоїти feature-environment – на той же Dev EKS, але з кастомними значеннями деяких змінних.
Проте зараз будемо робити в тестовому репозиторії “atlas-test” і з тестовим Helm-чартом.
Зміст
Release flow planning
Як будемо релізити?
Поки вирішили по такій схемі:
Тобто:
- девелопер створює бранч, пише код, тестує локально в Docker Compose
- після завершення роботи над фічею – створює Pull Request з лейблою “deploy”
- workflow Deploy Feature Env
- тригер: створення Pull Request з лейблою “deploy“
- білдить Docker-образ і тегає його з
git commit sha --short - пушить його в ECR
- створює feature-оточення в GitHub
- деплоїть в Kubernetes Dev в feature-env namespace, де девелопер може додатково потестити свої зміни в умовах, наближених до реальних
- workflow Deploy Feature Env
- після мержу PR в master-гілку:
- workflow Deploy Dev:
- тригер: push to master або вручну
- білдить Docker-образ і тегає його з
git commit sha --short - деплой на Dev
- якщо деплой пройшов (Helm не видав помилок, Pods запустились, тобто перевірки readiness та liveness Probes пройшли) – створюємо Git Tag
- тегаємо вже існуючий Docker образ з цим тегом
- workflow Deploy Stage:
- тригер: Git Tag created
- деплоїться існуючий Docker образ з цим тегом
- запускаються integration tests (mobile, web)
- якщо тести пройшли – то створюємо GitHub Release – chanelog, etc
- workflow Deploy Prod:
- тригер: Release created
- деплоїться існуючий образ з тегом цього релізу
- виконуються тести
- workflow Deploy Dev:
- ручний деплой
- з будь якого існуючого образу на Dev або Staging
Сьогодні зробимо два workflow – Deploy Dev, і Deploy та Destroy Feature-env.
Setup AWS
Для початку нам треба мати ECR та IAM Role.
ECR -для зберігання образів, які будемо деплоїти, а IAM Role буде використовувати GitHub Action для доступу до ECR та логіну в EKS під час деплою.
Репозиторій в ECR у нас вже є, теж з назвою “atlas-test“, створено поки що руками – пізніше перенесемо менеджмент ECR в Terraform.
А от AWS IAM-ролі для проектів в GitHub, які будуть деплоїтись в Kubernetes можемо зробити відразу на етапі створення EKS-кластеру.
Terraform: створення IAM Role
Для деплою з GitHub в AWS ми використовуємо OpenID Connect, тобто аутентифікований юзер GitHub (або в нашому випадку – GitHub Actions Runner) може прийти в AWS, і там виконати AssumeRole, а потім з політиками цієї ролі пройти авторизацію в AWS – перевірку того, що він там може робити.
Щоб деплоїти з GitHub в EKS нам потрібни політики на:
eks:DescribeClusterтаeks:ListClusters: щоб авторизуватись в EKS-кластеріecr: push та read образів з ECR-репозиторію
Крім того, для цієї ролі задамо обмеження на те, з якого репозиторію GitHub можна буде виконати AssumeRole.
В проекті EKS додамо змінну github_projects з типом list, в якій будуть всі GitHub-проекти, яким ми будемо дозволяти деплоїти в цей кластер, поки він тут буде один:
...
variable "github_projects" {
type = list(string)
default = [
"atlas-test"
]
description = "GitHub repositories to allow access to the cluster"
}
Описуємо сам роль, де в циклі for_each перебираємо всі елементи списку github_projects:
data "aws_caller_identity" "current" {}
resource "aws_iam_role" "eks_github_access_role" {
for_each = var.github_projects
name = "${local.env_name}-github-${each.value}-access-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity"
Effect = "Allow"
Sid = ""
Principal = {
Federated : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
}
Condition: {
StringLike: {
"token.actions.githubusercontent.com:sub": "repo:GitHubOrgName/${each.value}:*"
},
StringEquals: {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
})
inline_policy {
name = "${local.env_name}-github-${each.value}-access-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"eks:DescribeCluster*",
"eks:ListClusters"
]
Effect = "Allow"
Resource = module.eks.cluster_arn
},
{
Action = [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
]
Effect = "Allow"
Resource = "*"
},
]
})
}
tags = {
Name = "${local.env_name}-github-${each.value}-access-policy"
}
}
Тут:
- в
assume_role_policyдозволяємоAssumeRoleWithWebIdentityцієї ролі для token.actions.githubusercontent.com і репозиторію repo:GitHubOrgName:atlas-test - в
inline_policy:- дозволяємо
eks:DescribeClusterкластеру, який деплоїться - дозволяємо
eks:ListClustersвсіх кластерів - дозволяємо операції в ECR на всі репозиторії
- дозволяємо
На ECR було б краще обмежити конкретними репозиторіями, але це поки в тесті, і ще не відомо який неймінг репозиторієв буде.
Див. Pushing an image, AWS managed policies for Amazon Elastic Container Registry та Amazon EKS identity-based policy examples.
Далі треба додати ці створені ролі в aws_auth_roles, де зараз вже маємо одну роль:
...
aws_auth_roles = [
{
rolearn = aws_iam_role.eks_masters_access_role.arn
username = aws_iam_role.eks_masters_access_role.arn
groups = ["system:masters"]
}
]
...
В locals будуємо новий list(map(any)) – github_roles, а потім в aws_auth_roles за допомогою flatten() створюємо новий list, в який включаємо eks_masters_access_role та ролі із github_roles:
...
locals {
vpc_out = data.terraform_remote_state.vpc.outputs
github_roles = [ for role in aws_iam_role.eks_github_access_role : {
rolearn = role.arn
username = role.arn
groups = ["system:masters"]
}]
aws_auth_roles = flatten([
{
rolearn = aws_iam_role.eks_masters_access_role.arn
username = aws_iam_role.eks_masters_access_role.arn
groups = ["system:masters"]
},
local.github_roles
])
}
...
Поки тут використовуємо system:masters, бо це все ще в розробці, і RBAC поки не налаштовую. Але див. User-defined cluster role binding should not include system:masters group as a subject.
Деплоїмо, та перевіряємо aws-auth ConfigMap, де тепер маємо новий об’єкт в mapRoles:
Workflow: Deploy Dev manually
Тепре, маючи ролі, можемо робити Workflow.
Почнемо з ручного деплою на Dev, бо він самий простий. А потім вже маючи працючий білд і процеси – будемо робити решту.
Triggers
По яких умовах будемо запускати білд?
workflow_dispatch:- з будь-якого бранча або тега
pushв master: в репозиторії бекенду master-бранч у нас має обмеження на push тільки з Pull Request, тож інших пушів тут не буде
Ще можна робити додаткову перевірку в джобах, на кшталт:
- name: Build
if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true
Див. Trigger workflow only on pull request MERGE.
Environments та Variables
На рівні репозиторію додаємо змінні – переходимо в Settings > Secrets and variables > Actions:
ECR_REPOSITORYAWS_REGION
Створюємо GitHub Environment “dev“, і йому задаємо:
-
AWS_IAM_ROLE: arn беремо з outputs деплою EKS з Terraform AWS_EKS_CLUSTER: ім’я беремо з outputs деплою EKS з TerraformENVIRONMENT: “dev“
Job: Docker Build
Тестити будемо з мінімальним Dockerfile – створюємо його в корні репозиторію:
FROM alpine
Створюємо директорію .github/workflows:
$ mkdir -p .github/workflows
І в ній файл .github/workflows/deploy-dev.yml:
name: Deploy to EKS
on:
workflow_dispatch:
push:
branches: [ master ]
permissions:
id-token: write
contents: read
jobs:
build-docker:
name: Build Docker image
runs-on: ubuntu-latest
environment: dev
steps:
- name: "Setup: checkout code"
uses: actions/checkout@v3
- name: "Setup: Configure AWS credentials"
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ vars.AWS_IAM_ROLE }}
role-session-name: github-actions-test
aws-region: ${{ vars.AWS_REGION }}
- name: "Setup: Login to Amazon ECR"
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: 'true'
- name: "Setup: create commit_sha"
id: set_sha
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: "Build: create image, set tag, push to Amazon ECR"
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
IMAGE_TAG: ${{ steps.set_sha.outputs.sha_short }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f Dockerfile .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
В ньому маємо джобу, яка запускається з GitHub Environment == dev, і:
- з
actions/checkoutзачекаутить код на GitHub Runner - з
aws-actions/configure-aws-credentialsзалогіниться в AWS виконавши AssumeRole ролі, яку ми створили раніше - з
aws-actions/amazon-ecr-loginзалогіниться в AWS ECR (мені стало цікаво як жеж він логиниться, але – 54.000 строк коду на JS!) - згенерує
output sha_short, в який внесе Commit ID - і виконає
docker buildтаdocker push
Пушимо, мержимо в мастер, і запускаємо білд:
Перевіряємо образ в ECR:
Job: Helm deploy
Наступний шаг – це задеплоїти Helm-чарт в EKS.
Швиденько зробимо тестовий чарт:
$ mkdir -p helm/templates
В директорії helm створюємо файл Chart.yaml:
apiVersion: v2 name: test-chart description: A Helm chart type: application version: 0.1.0 appVersion: "1.16.0"
Файл templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
spec:
replicas: 1
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: test-container
image: {{ .Values.image.repo}}:{{ .Values.image.tag }}
imagePullPolicy: Always
Та файл values.yaml:
image: repo: 492***148.dkr.ecr.us-east-1.amazonaws.com/atlas-test tag: latest
Передача змінних між GitHub Action Jobs
Далі питання по самому Workflow, а саме – як передати Docker tag, який ми створили в Job build-docker?
Можно зробити деплой з Helm в тій самій джобі, і тоді зможемо використати ту ж змінну IMAGE_TAG.
А можемо зробити окремою job, і передати значення тегу між джобами.
Великого сенсу розділяти на дві джоби тут нема, але по-перше це в білді виглядає більш логічно, по-друге – хочеться спробувати всякі штуки в GitHub Actions, тому давайте передамо значення змінної між джобами.
Для цього в першій джобі додаємо outputs:
...
jobs:
build-docker:
name: Build Docker image
runs-on: ubuntu-latest
environment: dev
outputs:
image_tag: ${{ steps.set_sha.outputs.sha_short }}
...
А потім його використаємо в новій джобі з Helm, в якій передаємо як values: image.tag:
...
deploy-helm:
name: Deploy Helm chart
runs-on: ubuntu-latest
environment: dev
needs: build-docker
steps:
- name: "Setup: checkout code"
uses: actions/checkout@v3
- name: "Setup: Configure AWS credentials"
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ vars.AWS_IAM_ROLE }}
role-session-name: github-actions-test
aws-region: ${{ vars.AWS_REGION }}
- name: "Setup: Login to Amazon ECR"
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: 'true'
- name: Deploy Helm
uses: bitovi/[email protected]
with:
cluster-name: ${{ vars.AWS_EKS_CLUSTER }}
namespace: ${{ vars.ENVIRONMENT }}-testing-ns
name: test-release
# may enable roll-back on fail
#atomic: true
values: image.tag=${{ needs.build-docker.outputs.image_tag }}
timeout: 60s
helm-extra-args: --debug
Пушимо зміни, і перевіряємо деплой:
Job: створення Git Tag
Наступним кроком треба створити Git Tag. Тут можемо використати Action github-tag-action, який під капотом виконує перевірку заголовку комміту, і в залежності від нього – інкрементить major, minor або patch версію тегу.
Тож давайте спершу поглянемо на Commit Message Format, хоча взагалі-то це тема, яку можна було б винести окремим постом.
Git commit message format
Див. Understanding Semantic Commit Messages Using Git and Angular.
Отже, якщо кратко, то заголовок пишемо в форматі “type(scope): subject“, тобто, наприклад – git commit -m "ci(actions): add new workflow".
При цьому type умовно можна поділити на development та production, тобто – зміни, якві відносяться до розробки/розробників, або зміни, які відносяться до production-оточення та end-юзерів:
- development:
build(ранішеchore): зміни, якві відносяться до білду та пакетів (npm build,npm install, etc)ci: зміни CI/CD (workflow-файли,terraform apply, etc)docs: зміни в документації проектуrefactor: рефакторінг коду – нові назви змінних, спрощення кодуstyle: зміни коду в відступах, коми, лапки-дужки і т.д.test: зміни в тестах коду – юніт-тести, інтеграційні, etc
- production:
feat: нові фічі, функціональністьfix: баг-фіксиperf: зміни, які стосуються performance
Тож додаємо нову job:
...
create_tag:
name: "Create Git version tag"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: deploy-helm
permissions:
contents: write
outputs:
new_tag: ${{ steps.tag_version.outputs.new_tag }}
steps:
- name: "Checkout"
uses: actions/checkout@v3
- name: "Misc: Bump version and push tag"
id: tag_version
uses: mathieudutour/[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag_prefix: eks-
В джобі вказуємо needs, щоб запускати тільки після деплою Helm, додаємо permissions, щоб github-tag-action мав змогу додавати теги в репозиторії, і додаємо tag_prefix, бо в репозіторії бекенду, де все це потім буде працювати, вже є стандартні теги з префіксом v. А токен в secrets.GITHUB_TOKEN є по дефолту в саміх Action.
Пушимо з комітом ga -A && gm "ci(actions): add git tag job" && gp, і маємо новий тег:
Workflow: Deploy Feature Environment
Окей – у нас є білд Docker, є деплой Helm-чарту. Все деплоїться на Dev-оточення, все працює.
Давайте додамо ще один workflow – для деплою на EKS Dev, але вже не як dev-оточення, а у тимчасовий Kubernetes Namespace, щоб девелопери мали змогу потестити свої фічі незалежно від Dev-оточення.
Для цього нам потрібно:
- тригерити workflow при створенні Pull Request з лейблою “deploy”
- створити custom name для нового Namespace – використаємо Pull Request ID
- збілдити Docker
- задеплоїти Helm-чарт у новий Namespace
Створюємо новий файл – create-feature-env-on-pr.yml.
В ньому буде три джоби:
- Docker build
- Deploy feature-env
- Destroy feature-env
Умови запуску Jobs з if та github.event context
Docker build та Deploy мають запускатись, коли Pull Request створено і якщо він має лейблу “deploy”, а Destroy – коли Pull Request з лейблою “deploy” закрито.
Для тригеру workflow задаємо умову on.pull_request – тоді будемо мати PullRequestEvent з набором полів, які можемо перевірити.
Єдине, що в документації чомусь не вказано поле label, про що говорилось ще в 2017 (!) році, але на ділі вона є.
Тут дуже може допомогти додатковий step, в якому можна вивести весь payload.
Створюємо бранч для тестів, і у файлі create-feature-env-on-pr.yml додаємо першу джобу:
name: "Create feature environment on PR"
on:
pull_request:
types: [ opened, edited, closed, reopened, labeled, unlabeled, synchronize ]
permissions:
id-token: write
contents: read
concurrency:
group: deploy-${{ github.event.number }}
cancel-in-progress: false
jobs:
print-event:
name: Print event
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github.event) }}
run: |
echo "$GITHUB_CONTEXT"
Пушимо в репозиторій, створюємо Pull Request, створюємо нову label:
І наш workflow запустився:
І видав нам всі дані, які маємо в event:
Тепер, маючи їх, можемо подумати про те, як будемо перевіряти умови для запуску jobs:
- Створити Environment, якщо Pull Request:
opened,editied,reopened,synchronize(якщо в source-бранч додано новий коміт)- але Pull Request може бути без лейбли – тоді нам деплоїти не треба
- до вже існуючого Pull Request може бути додана лейбла – тоді event буде
labeled, деплоїмо
- Видалити Environment, якщо Pull Request: closed
- але може бути без лейбли – тоді джобу на видалення запускати не треба
- але у вже існуючого Pull Request може бути прибрана лейбла “deploy” – тоді event буде
unlabeled, видаляємо Environment
Умови запуску Workflow у нас зараз виглядають так:
on:
pull_request:
types: [opened, edited, closed, reopened, labeled, unlabeled, synchronize]
Job: Deploy при створенні Pull Request з лейблою “deploy”
Отже, вище вже побачили, що при створенні Pull Request з лейблою “deploy” маємо "action": "opened" та pull_request.labels[].name: "deploy":
Тоді можемо перевірити умову як:
if: contains(github.event.pull_request.labels.*.name, 'deploy')
Див. contains.
Але якщо івент був на закриття Pull Request – то він все одно буде мати “deploy” лейблу, і тригерне нашу джобу.
Тому додаємо ще одну умову:
if: contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed'
Тож тестова джоба, яка буде “деплоїти”, може виглядати так:
...
jobs:
print-event:
name: Print event
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github.event) }}
run: |
echo "$GITHUB_CONTEXT"
deploy:
name: Deploy
if: contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Run deploy
run: echo "This is deploy"
Пушимо, створюмо PR, перевіряємо, і бачимо, що джоба запустилась два рази:
Бо при створенні PR з лейблою маємо два івенти – власне створення PR, тобто івент “opened“, та додавання лейбли – event “labeled“.
Взагалі виглядає трохи криво, як на мене. Але, мабуть, GitHub не зміг обійтися одним івентом в такому випадку.
Тому ми можемо просто прибрати opened з тригерів on.pull_request – і при створенні PR з лейблою джоба затригериться тільки на івент “labeled“.
Ще одна річ, яка виглядає кривувато, хоча тут ми її не використовуємо, але:
- для перевірки string в
contains()ми використовуємо формуcontains(github.event.pull_request.labels.*.name, 'deploy')– спочатку вказуємо об’єкт, в якому шукаємо, потім строка, яку шукаємо - але щоб перевірити декілька strings – формат буде
contains('["labeled","closed"]', github.event.action)– тобто спочатку список строк для перевірки, потім об’єкт, в якому їх шукаємо
Окей, йдемо далі: ми можемо мати ще одну умову для запуску – коли для вже створеного PR без лейбли “deploy” який не тригернув нашу джобу, була додана лейбла “deploy” – і тоді нам треба запустити джобу.
Це можемо перевірити з такою умовою:
github.event.action == 'labeled' && github.event.label.name == 'deploy'
А щоб вибрати одну із умов – першу, чи цю – використовуємо оператор “або” – “||“, тобто наш if буде виглядати так:
if: | (contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed') || (github.event.action == 'labeled' && github.event.label.name == 'deploy')'
Після чого для тесту створюємо PR без лейбли – і спрацює тільки джоба “Print event”:
Потім додаємо лейблу “deploy” – і маються стригеритись обидві джоби:
Job: Destroy при закритті Pull Request з лейблою “deploy”
Залишилось зробити job для видалення feature-env – коли Pull Request з лейблою “deploy” закривається.
Умови тут схожі з тими, що ми робили для деплою:
...
destroy:
name: Destroy
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action == 'closed') ||
(github.event.action == 'unlabeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
steps:
- name: Run destroy
run: echo "This is dstroy"
- запуск джоби, якщо
action == 'closed' і labels.*.name == 'deploy'АБО - запуск джоби, якщо
action == 'unlabeled' і event.label.name == 'deploy'
Перевіряємо – створюємо PR з лейблою “deploy” – запускається джоба Deploy, мержимо цей PR – і маємо джобу Destroy:
Тож повністю файл зараз виглядає так:
name: "Create feature environment on PR"
on:
pull_request:
types: [ opened, edited, closed, reopened, labeled, unlabeled, synchronize ]
permissions:
id-token: write
contents: read
concurrency:
group: deploy-${{ github.event.number }}
cancel-in-progress: false
jobs:
print-event:
name: Print event
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github.event) }}
run: |
echo "$GITHUB_CONTEXT"
deploy:
name: Deploy
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed') ||
(github.event.action == 'labeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
steps:
- name: Run deploy
run: echo "This is deploy"
destroy:
name: Destroy
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action == 'closed') ||
(github.event.action == 'unlabeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
steps:
- name: Run destroy
run: echo "This is dstroy"
Добре – наче все працює. Якщо що – то вже по ходу діла поправимо умови, але в цілому ідея іх використання така.
Створення Feature Environment
Тут вже в принципі все нам відомо – використовуємо джоби, які робили для Deploy Dev, тільки міняємо пару параметрів.
В тому ж файлі create-feature-env-on-pr.yml описуємо джоби.
В першій, Docker build, нічого не міняється – тільки додаємо if:
...
build-docker:
name: Build Docker image
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed') ||
(github.event.action == 'labeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
timeout-minutes: 30
environment: dev
outputs:
image_tag: ${{ steps.set_sha.outputs.sha_short }}
steps:
- name: "Setup: checkout code"
uses: actions/checkout@v3
- name: "Setup: Configure AWS credentials"
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ vars.AWS_IAM_ROLE }}
role-session-name: github-actions-test
aws-region: ${{ vars.AWS_REGION }}
- name: "Setup: Login to Amazon ECR"
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: 'true'
- name: "Setup: create commit_sha"
id: set_sha
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: "Build: create image, set tag, push to Amazon ECR"
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
IMAGE_TAG: ${{ steps.set_sha.outputs.sha_short }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f Dockerfile .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
А в джобу Helm-деплой теж додамо if та новий step – “Misc: Set ENVIRONMENT variable“, який із значення github.event.number буде створювати нове значення для змінної ENVIRONMENT, яку ми передаємо в ім’я неймспейсу, в який буде деплоїтись чарт:
...
deploy-helm:
name: Deploy Helm chart
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed') ||
(github.event.action == 'labeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
timeout-minutes: 15
environment: dev
needs: build-docker
steps:
- name: "Setup: checkout code"
uses: actions/checkout@v3
- name: "Setup: Configure AWS credentials"
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ vars.AWS_IAM_ROLE }}
role-session-name: github-actions-test
aws-region: ${{ vars.AWS_REGION }}
- name: "Setup: Login to Amazon ECR"
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: 'true'
- name: "Misc: Set ENVIRONMENT variable"
id: set_stage
run: echo "ENVIRONMENT=pr-${{ github.event.number }}" >> $GITHUB_ENV
- name: "Deploy: Helm"
uses: bitovi/[email protected]
with:
cluster-name: ${{ vars.AWS_EKS_CLUSTER }}
namespace: ${{ env.ENVIRONMENT }}-testing-ns
name: test-release
#atomic: true
values: image.tag=${{ needs.build-docker.outputs.image_tag }}
timeout: 60s
helm-extra-args: --debug
І останнім додаємо нову джобу, яка буде видаляти Helm-реліз, та окремий step на видалення неймспейсу, бо сам Helm при uninstall такого не вміє, див. Add option to delete the namespace created during install. Для цього використовуємо ianbelcher/eks-kubectl-action:
...
destroy-helm:
name: Uninstall Helm chart
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action == 'closed') ||
(github.event.action == 'unlabeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
timeout-minutes: 15
environment: dev
steps:
- name: "Setup: checkout code"
uses: actions/checkout@v3
- name: "Setup: Configure AWS credentials"
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ vars.AWS_IAM_ROLE }}
role-session-name: github-actions-test
aws-region: ${{ vars.AWS_REGION }}
- name: "Misc: Set ENVIRONMENT variable"
id: set_stage
run: echo "ENVIRONMENT=pr-${{ github.event.number }}" >> $GITHUB_ENV
- name: "Destroy: Helm"
uses: bitovi/[email protected]
with:
cluster-name: ${{ vars.AWS_EKS_CLUSTER }}
namespace: ${{ env.ENVIRONMENT }}-testing-ns
action: uninstall
name: test-release
#atomic: true
timeout: 60s
helm-extra-args: --debug
- name: "Destroy: namespace"
uses: ianbelcher/eks-kubectl-action@master
with:
cluster_name: ${{ vars.AWS_EKS_CLUSTER }}
args: delete ns ${{ env.ENVIRONMENT }}-testing-ns
Пушимо, створюємо Pull Request, і маємо деплой:
Перевіряємо Namespace:
$ kk get ns pr-5-testing-ns NAME STATUS AGE pr-5-testing-ns Active 95s $ kk -n pr-5-testing-ns get pod NAME READY STATUS RESTARTS AGE test-deployment-77f6dcbd95-cg2gf 0/1 CrashLoopBackOff 3 (35s ago) 78s
Мержимо Pull Request – і видаляємо Helm release та Namespace:
Перевіряємо:
$ kk get ns pr-5-testing-ns Error from server (NotFound): namespaces "pr-5-testing-ns" not found
Bonus: GitHub Deployments
У GitHub є нова фіча – Deployments, яка ще в Beta, але вже можна користуватись:
Ідея Deployments в тому, що для кожного GitHub Environment можна побачити список всіх деплоїв до ньго – зі статусами та комітами:
Для роботи з Deployments використаємо Action bobheadxi/deployments, який приймає input з іменем env. Якщо в env передається не існуючий в репозиторії Environment – то він буде створений.
Крім того, тут маємо набір steps – start, finish, deactivate-env та delete-env.
Під час деплою нам треба викликати start і передати ім’я оточення, по завершенні деплою – викликати finish, щоб передати статус деплою, а при видаленні оточення – викликати delete-env.
До джоби deploy-helm у воркфлоу create-feature-env-on-pr.yml додаємо permissions і нові степи.
Степ “Misc: Create a GitHub deployment” – перед викликом “Deploy: Helm“, а степ “Misc: Update the GitHub deployment status” – після виконаня Helm install:
...
permissions:
id-token: write
contents: read
deployments: write
...
deploy-helm:
name: Deploy Helm chart
if: |
(contains(github.event.pull_request.labels.*.name, 'deploy') && github.event.action != 'closed') ||
(github.event.action == 'labeled' && github.event.label.name == 'deploy')
runs-on: ubuntu-latest
timeout-minutes: 15
needs: build-docker
...
- name: "Misc: Set ENVIRONMENT variable"
id: set_stage
run: echo "ENVIRONMENT=pr-${{ github.event.number }}" >> $GITHUB_ENV
- name: "Misc: Create a GitHub deployment"
uses: bobheadxi/deployments@v1
id: deployment
with:
step: start
token: ${{ secrets.GITHUB_TOKEN }}
env: ${{ env.ENVIRONMENT }}
- name: "Deploy: Helm"
uses: bitovi/[email protected]
with:
cluster-name: ${{ vars.AWS_EKS_CLUSTER }}
namespace: ${{ env.ENVIRONMENT }}-testing-ns
name: test-release
#atomic: true
values: image.tag=${{ needs.build-docker.outputs.image_tag }}
timeout: 60s
helm-extra-args: --debug
- name: "Misc: Update the GitHub deployment status"
uses: bobheadxi/deployments@v1
if: always()
with:
step: finish
token: ${{ secrets.GITHUB_TOKEN }}
status: ${{ job.status }}
env: ${{ steps.deployment.outputs.env }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
...
Пушимо, і маємо нове оточення “pr-7“:
Ну і на цьому поки все.
Наче все працює – можна додавати нові воркфлоу в репозиторій нашого бекенду.

























