Use Github actions for Trunk Based Development to deploy AWS ECS Service
Madan Kumar
Posted on August 27, 2023
Trunk based development as a branching model is preferred compared to something like Git / GitHub Flow due its simplicity. But creating a CI/CD pipeline is more challenging since we deploy to every environment from the same branch.
Introduction to Trunk-Based Development
Trunk-based development involves frequent & small code check-in to a repository, typically known as the "trunk". This approach contrasts traditional development models (like Git Flow), which often involve long, isolated development cycles followed by large code merges. In trunk-based development, teams are encouraged to commit their code to the trunk frequently, often multiple times per day. This allows for continuous integration and continuous deployment (CI/CD), as well as easier collaboration and code reviews. You also don't have branches per environment like a development branch that is deploying to a development environment. You integrate all code on the "trunk".
Creating GitHub Actions Workflows
I have two environment development & Production
- Configure Open ID Connect to get short-lived credentials for my AWS account and store the ARN in Action Secret as . To setup follow this https://aws.amazon.com/blogs/security/use-iam-roles-to-connect-github-actions-to-actions-in-aws/
- Create Personal Access Token for Git, this is used for creating tags & Release and store the value in Action Secret as
- create directory called deployments in the root folder of git. deployments (this will hold the task definition files required for AWS)
GitHub Action Workflow
dev-deploy-cicd.yml
We trigger the pipeline on pushes to main (our "trunk"). Remember, we don't have different branches per environment. We deploy to all our environments from this branch.
Below action will be triggered when a PR or Push happens to main branch and does the following
- Build and push a new docker image to ECR
- update the task definition with the new image tag
- Update the services with the latest task definition and redeployment
- Create Draft Release and Tag it using semantic versioning
name: CICD API
on:
push:
branches:
- main
env:
AWS_ROLE: ${{ secrets.DEV_AWS_GITHUB_ACTION_ROLE }}
AWS_REGION: # set this to preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: # set this to Amazon ECR repository name
ECS_SERVICE: # set this to Amazon ECS service name
ECS_CLUSTER: # set this to Amazon ECS cluster name
ECS_TASK_DEFINITION: deployments # set this to the path Amazon ECS task definition
ECS_CLUSTER_ENV: # set this to Amazon ECS cluster Environment
CONTAINER_NAME: # containerDefinitions section of your task definition
permissions:
id-token: write
contents: read
jobs:
deploy-develop:
name: Build & Deploy
runs-on: ubuntu-latest
environment: develop
steps:
- name: Source Code Checkout
uses: actions/checkout@v3
with:
submodules: recursive
token: ${{ secrets.ORG_GH_ACCESS_TOKEN }}
- name: Configure AWS Credentials Using Role
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ env.AWS_ROLE }}
aws-region: ${{ env.AWS_REGION }}
#mask-aws-account-id: 'false'
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: 'true'
- name: Generate Short GIT Commit HASH
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ steps.vars.outputs.sha_short }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"
- name: Update Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}/task-definitions-${{ env.ECS_CLUSTER_ENV }}.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}-${{ env.ECS_CLUSTER_ENV}}
#wait-for-service-stability: true
- name: Bump version and push tag
id: semantic
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.ORG_GH_ACCESS_TOKEN }}
- name: Create Release
uses: softprops/action-gh-release@v1
#if: startsWith(github.ref, 'refs/tags/v*')
with:
token: ${{ secrets.ORG_GH_ACCESS_TOKEN }}
tag_name: ${{ steps.semantic.outputs.new_tag }}
draft: true
prerelease: true
generate_release_notes: true
prod-release.yml
Below action will be triggered when a Draft Release was changed to Publish.
name: Production Release
on:
release:
types: [published]
env:
AWS_ROLE: ${{ secrets.PROD_AWS_GITHUB_ACTION_ROLE }}
AWS_REGION: # set this to preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: # set this to Amazon ECR repository name
ECS_SERVICE: # set this to Amazon ECS service name
ECS_CLUSTER: # set this to Amazon ECS cluster name
ECS_TASK_DEFINITION: # set this to the path Amazon ECS task definition
ECS_CLUSTER_ENV: # set this to Amazon ECS cluster Environment
CONTAINER_NAME: # containerDefinitions section of your task definition
permissions:
id-token: write
contents: read
jobs:
deploy-production:
name: Build & Deploy
runs-on: ubuntu-latest
environment: production
steps:
- name: Source Code Checkout
uses: actions/checkout@v3
with:
submodules: recursive
token: ${{ secrets.ORG_GH_ACCESS_TOKEN }}
- name: Configure AWS Credentials Using Role
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ env.AWS_ROLE }}
aws-region: ${{ env.AWS_REGION }}
#mask-aws-account-id: 'false'
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
with:
mask-password: 'true'
- name: Generate Short GIT Commit HASH
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ steps.vars.outputs.sha_short }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"
- name: Update Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}/task-definitions-${{ env.ECS_CLUSTER_ENV }}.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}-${{ env.ECS_CLUSTER_ENV}}
#wait-for-service-stability: true
Posted on August 27, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.