DevOps Hero Part 2: Continuous Delivery of ECS Services via GitHub Actions

Terris Linenbach
2 min readApr 20, 2024

The previous installment introduced a CloudFormation template that builds everything needed to host a web application from scratch on AWS. Next let’s deploy an image to the service’s ECR repository and restart the service whenever changes are pushed to the main branch.

This article assumes you already have a Dockerfile for building the image.

  1. In the AWS console, go to IAM Users and create an aws-cli user that has AwsAdministrator permissions
  2. Also in the AWS console, go to the ECS cluster, and update the “api” service’s settings. Change the “Desired count” to 1. After saving this change, the service will fail to start because there are no images in the ECR repository. The following steps will fix that so an image will be pushed to the ECR repository when any commit is pushed to the main branch.
  3. Go to the repository on GitHub
  4. Go to Settings
  5. Select “Secrets and Variables” and then select “Actions”
  6. Create an environment named “dev”
  7. In the “dev” environment, using the information from step 1, add:

a) One secret: AWS_SECRET_ACCESS_KEY
b) Two variables: AWS_ACCESS_KEY_ID and AWS_REGION

8. Add deploy.yaml to the .github/workflows directory:

name: Deploy
# See https://tinyurl.com/4xmtyyhm

on:
push:
branches:
- main

env:
SERVICE: api
CLUSTER: DevGuy
CLUSTER_LOWERCASE: devguy
IMAGE_TAG: main

jobs:
build-and-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
environment: dev

steps:
# Do this now to fail early
- name: Configure AWS credentials
id: aws-creds
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ vars.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}

# Do this now to fail early
- name: Log in to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
mask-password: false

- name: Check out the branch
id: checkout
uses: actions/checkout@v4

- name: Build and tag a Docker image
id: build-tag
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker build \
--build-arg AWS_ACCESS_KEY_ID=${{ vars.AWS_ACCESS_KEY_ID }} \
--build-arg AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
--build-arg AWS_REGION=${{ vars.AWS_REGION }} \
-t ${{ env.ECR_REGISTRY }}/${{ env.CLUSTER_LOWERCASE }}-${{ env.SERVICE }}:${{ env.IMAGE_TAG }} .

- name: Push image to ECR repository
id: push-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker push ${{ env.ECR_REGISTRY }}/${{ env.CLUSTER_LOWERCASE }}-${{ env.SERVICE }}:${{ env.IMAGE_TAG }}

- name: Restart the service
id: restart-service
run: |
aws ecs update-service --cluster ${{ env.CLUSTER }} \
--service ${{ env.CLUSTER }}-${{ env.SERVICE }} \
--region ${{ vars.AWS_REGION }} \
--force-new-deployment --no-cli-pager

In the next installment of this series, we will enable developers to connect directly to the VPC over VPN.

--

--