In the previous article, we saw how to generate the highchart export server docker image. But, now we are going to deploy it in AWS ECS. An AWS service for running containers.
GitHub Actions
Our source code is on Git Hub. And thanks to GitHub actions we can deploy directly to AWS ECS. I will show you that it is very simple.
1. In the top menu you will find the Actions option. Go there to set up the integration with AWS ECS.
2. Github Actions has several workflows. In our case, we will use the AWS ECS one.
3. You will then be presented with a .github/workflows folder where there will be a manifest called aws.yml with the instructions to create a docker image and deploy it to an ECS cluster service following the instructions of a task definition.
5. At this point, we stopped working on GitHub because we need to create the architecture components in AWS: ELB, ECR, and ECS (Cluster, Service and task definition).
AWS Security Group
1. We need a security group to use in the creation of the following components that allow us to work with the HTTP, HTTPS, and SSH (optional) protocols.
AWS Target Groups
1. We need to create two target groups for both ports: 8080 and 7000.
The target group for port 8080:
The target group for port 8080:
Note: This configuration is for port 8080, as can be seen, it is expected to have a /health to verify the health of the service. Make sure that your API that is going to run in the container has this endpoint implemented and responding, otherwise, it will give you a lot of container restart problems.
The target group for port 7000:
AWS Elastic Load Balancer
1. We must create an ELB for our solution with 2 target groups: One for port 8080 and another for port 7000 (health check). Also, configure for the use of the HTTPs protocol and your certificate.
This part is where you use the 2 target groups created earlier.
AWS Elastic Container Registry
1. We will create a repository for our docker image, but we will sync it with the production docker registry, so if you deploy it to your development environment, the same image goes to production.
Note: The synchronization between docker registries of different regions is simple, just follow this link.
Copy the link of your docker repository because you will use it later.
AWS Elastic Container Service
1. We will create an ECS Cluster with Fargate.
2. Next it is necessary to create a task definition where we will specify the docker image to use and how many instances we want to create.
3. The file will be put in our GitHub repo for development and production environments.
Note: At this point, you will have realized that you will have to create the same components (Security group, target groups, ELB, ECS, ECR) in your production account. So you will get the task definition for production.
4. Now we have to create the service in the cluster where we will use our task definition.
But, unfortunately, we cannot create a service, associating an ELB with 2 target groups. The console web does not allow it, I am going to show you:
Fill in the following information with our task definition and define a number of 3 replicas.
Fill in the information about networking:
Fill in the information of ELB with the target groups. This is where we will see that we cannot add another target group to the ELB that we had already created and we will have to abort the creation of the service through the web console. Pretty bad.
aws ecs update-service \--cluster HighchartServerCluster \--service highchartserver-service \--load-balancers "[{\"containerName\": \"hightchartserver-app\", \"containerPort\": 8080, \"targetGroupArn\":\"arn:aws:elasticloadbalancing:eu-north-1:NNNNNNNNNNN:targetgroup/HighchartServerALBTargetGroup/ace1b83ad311d35e\"}, {\"containerName\": \"hightchartserver-app\", \"containerPort\": 7000, \"targetGroupArn\": \"arn:aws:elasticloadbalancing:eu-north-1: NNNNNNNNNNN:targetgroup/HighchartServerHealthTargetGroup/4c3c7200d3ee2998\"}]"
Final configuration of the GitHub action
1. We come to the final part. Here you just have to put the correct names of your cluster, service, your application, the reference to the task definition in dev and prod, and the use of your AWS credentials. As you can see it will only deploy to production if the deployment to development works.
# This workflow will build and push a new container image to Amazon ECR,
# and then will deploy a new task definition to Amazon ECS, when there is a push to the "main" branch.
#
# To use this workflow, you will need to complete the following set-up steps:
#
# 1. Create an ECR repository to store your images.
# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
#
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
# For example, follow the Getting Started guide on the ECS console:
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
#
# 3. Store your ECS task definition as a JSON file in your repository.
# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
# in the `containerDefinitions` section of the task definition.
#
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
# See the documentation for each action used below for the recommended IAM policies for this IAM user,
# and best practices on handling the access key credentials.
name: Deploy to Amazon ECS
on:
push:
branches: [ "main" ]
env:
AWS_REGION_DEV: eu-central-1 # set this to your preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: highchart-export-server # set this to your Amazon ECR repository name
ECS_SERVICE: highchartserver-service # set this to your Amazon ECS service name
ECS_CLUSTER: HighchartServerCluster # set this to your Amazon ECS cluster name
ECS_TASK_DEFINITION_DEV: task-definition-dev.json # set this to the path to your Amazon ECS task definition
# file, e.g. .aws/task-definition.json
AWS_REGION_PROD: eu-north-1
ECS_TASK_DEFINITION_PROD: task-definition.json
CONTAINER_NAME: hightchartserver-app # set this to the name of the container in the
# containerDefinitions section of your task definition
permissions:
contents: read
jobs:
deploy-to-dev:
name: Deploy to DevTest
runs-on: ubuntu-latest
environment: DevTest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials for DevTest
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION_DEV }}
- name: Login to Amazon ECR DevTest
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR DevTest
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
# Build a docker container and
# push it to ECR so that it can
# be deployed to ECS.
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: Fill in the new image ID in the Amazon ECS task definition DevTest
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION_DEV }}
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition DevTest
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 }}
wait-for-service-stability: true
deploy-to-prod:
name: Deploy to Production
needs: [deploy-to-dev]
runs-on: ubuntu-latest
environment: Production
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials for Production
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION_PROD }}
- name: Login to Amazon ECR Production
id: login-ecr-prod
uses: aws-actions/amazon-ecr-login@v1
- name: Get image ID from Amazon ECR Production (Sync is enabled)
id: build-image-prod
env:
ECR_REGISTRY: ${{ steps.login-ecr-prod.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
# Verify Image ID
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- name: Fill in the new image ID in the Amazon ECS task definition Production
id: task-def-prod
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION_PROD }}
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image-prod.outputs.image }}
- name: Deploy Amazon ECS task definition Production
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def-prod.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
Final Test
To see that everything works correctly you can do a curl or postman to the ELB or URL. If you get the image, you already have a workflow ready with GitHub actions deploying to AWS ECS.
Until next time:
If you need consultancy or advice to turn your ideas into reality, do not hesitate to contact ExoreactionEnjoy!
Joe