Отже, маємо розгорнутий кластер 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_REPOSITORY
AWS_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
“:
Ну і на цьому поки все.
Наче все працює – можна додавати нові воркфлоу в репозиторій нашого бекенду.