Automate Amazon ECS cluster creation with a custom docker image using AWS CDK

Search for a command to run...

No comments yet. Be the first to comment.
AWS EC2 Linux instances can be accessed directly via SSH if you have the associated SSH key pair. Users can choose an existing SSH key or generate a new one while launching an EC2 instance. However, maintaining the SSH keys and sharing them across te...

AWS CDK rolled out a change in their version 2.50.0 as per this GitHub discussion which caused a dependency on the task definition family for ECS services instead of referencing the task definition revision. This caused deployment issues for users tr...

Amazon EC2 instances provide great resources when it comes to managing servers and running your applications on the web. With the ever-growing capability, AWS offers various options to run our servers online. But, how do we ensure that we take the be...

One of the best practices to improve application lifecycle is by having multiple stages of quality assessment before the final product is released to the public. This can be accomplished by using multiple accounts with cloud providers, such as AWS, d...

The Serverless Image Handler solution helps to render and return images spontaneously on your websites and mobile applications using Sharp. Sharp is a high-performance Node.js image processing tool. It helps to convert large images in common formats ...

Docker containers help us to create portable and isolated environments for deploying our applications. Since containers provide the flexibility to run applications efficiently across computing environments, containerizing applications is a good practice to avoid wastage of resources and also to scale applications faster.
AWS Elastic container services (ECS) is a container orchestration service that allows you to run containerized applications on AWS. It is a highly scalable, high-performance container orchestration service that allows you to easily run applications on managed clusters. ECS is a fully managed service that makes it easy for you to run, stop, and manage Docker containers.
AWS CDK is a software development framework that helps to define cloud infrastructure as code and provision it through AWS CloudFormation.
In this article, we will learn how to automate the creation of ECS clusters with a custom docker image using AWS CDK.
You will need the following to follow along with this tutorial:
Find the code for the entire project here
For the purpose of this article, we will create a Docker image that will install apache web server on an Ubuntu environment.
Let's start by choosing Ubuntu as our base image.
FROM ubuntu:latest
Next, let's install the apache web server
RUN apt-get update && apt-get install -y apache2
We will now define which port the container should expose
EXPOSE 80
Finally, let's start the web server
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
So, our Dockerfile will look like this:
FROM ubuntu:latest
# install apache2
RUN apt-get update && apt-get install -y apache2
# port number the container should expose
EXPOSE 80
# run apache2
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
Let's go ahead and create a new directory to initialize a new CDK project.
You can use the following command to initialize a CDK project
cdk init app --language=[csharp|fsharp|go|java|javascript|python|typescript]
Great! This will set up a set of folders for you. The lib folder will be where most of the fun will happen. Let's get started!

This is the stack where you will build the docker image, push it to ECR and create ECS using the docker image.
We will need to import the following dependencies.
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import { DockerImageAsset } from 'aws-cdk-lib/aws-ecr-assets';
import * as path from 'path';
import * as ecrdeploy from 'cdk-ecr-deployment';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as alb from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
Next, we will start with building the docker image.
export class WebserverEcsStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Build docker image
const asset = new DockerImageAsset(this, 'MyBuildImage', {
directory: path.join("image", ''),
});
For our ECS cluster to use the docker image, we will have to push the image to a location where ECS can access it. For this, we will use an ECR repository.
ECR repository is a fully managed container registry that provides high-performance hosting to deploy your applications easily.
Note that when you're building the docker image, you should have docker running in your system. CDK uses docker to build the image and push it to ECR.
// Create ECR repository
const ecrRepo = new ecr.Repository(this, 'WebserverEcrRepo', {
repositoryName: 'webserver-ecr-repo',
});
// Deploy docker image to ECR
new ecrdeploy.ECRDeployment(this, 'DeployImage', {
src: new ecrdeploy.DockerImageName(asset.imageUri),
dest: new ecrdeploy.DockerImageName(`${cdk.Aws.ACCOUNT_ID}.dkr.ecr.ap-south-1.amazonaws.com/webserver-ecr-repo:latest`),
});
Our ECR repository will be now ready with the docker image. We will now move on and create an ECS cluster to run our container in it.
// Create ECS cluster
const cluster = new ecs.Cluster(this, 'WebserverEcsCluster', {
clusterName: 'webserver-ecs-cluster',
});
ECS clusters need a task definition to run docker containers. You can configure how your container should run in a task definition.
Read more about task definition here
// Create ECS task definition
const taskDefinition = new ecs.FargateTaskDefinition(this, 'WebserverTaskDef', {
memoryLimitMiB: 512,
cpu: 256,
runtimePlatform: {
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: ecs.CpuArchitecture.ARM64,
},
});
Next, we will specify more details about our container to the task definition using a container definition. This can come in handy if you want to run multiple containers.
// Create ECS container definition
const containerDefinition = taskDefinition.addContainer('WebserverContainer', {
image: ecs.ContainerImage.fromEcrRepository(ecrRepo),
memoryLimitMiB: 512,
cpu: 256,
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'webserver-ecs',
}),
portMappings: [
{
containerPort: 80,
hostPort: 80,
},
],
});
Awesome! Now our task definition will be ready to initiate our cluster. Let's move ahead and use the above task definition to create our ECS service.
ECS service can be used to run and manage a specified number of instances of a task definition simultaneously in an ECS cluster.
// Create ECS service
const service = new ecs.FargateService(this, 'WebserverService', {
cluster: cluster,
taskDefinition: taskDefinition,
desiredCount: 1,
serviceName: 'webserver-service',
assignPublicIp: true,
});
Great! Now we will have a running ECS service.
We can attach a Load balancer so that it can distribute the incoming traffic evenly to all the instances of our cluster. This will be helpful to serve a large group of clients as it improves the availability of our application.
// Create ECS load balancer
const loadBalancer = new alb.ApplicationLoadBalancer(this, 'WebserverLoadBalancer', {
vpc: cluster.vpc,
internetFacing: true,
});
// Create ECS listener
const listener = loadBalancer.addListener('WebserverListener', {
port: 80,
});
// Create ECS target group
const targetGroup = listener.addTargets('WebserverTargetGroup', {
port: 80,
targets: [service],
healthCheck: {
path: '/',
interval: cdk.Duration.seconds(60),
timeout: cdk.Duration.seconds(5),
healthyHttpCodes: '200',
},
});
// Create ECS security group
const securityGroup = new ec2.SecurityGroup(this, 'WebserverSecurityGroup', {
vpc: cluster.vpc,
allowAllOutbound: true,
});
// Create ECS security group ingress rule
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP access from the Internet');
// Create ECS security group egress rule
securityGroup.addEgressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP access to the Internet');
}
}
Great job! Our cluster is now ready to be up and running.
Deploy the stack by using cdk deploy.
Let's now move ahead and see if our cluster is working as expected.
Go to Elastic Container Services in AWS and click on clusters. You will find that our cluster is created.
You might have to wait for some time before the cluster goes into a running state.

Now go to the networking section to find the endpoint of the load balancer.

Click on the endpoint to see if we can reach the web server.

Awesome! We can now see that the Apache web server is displayed which means that our container is up and running inside the ECS cluster.
This article helps you to automate the creation of an ECS cluster and run your containerized application using a custom docker image in it. This would come in handy if you want to deploy your containerized application in a single AWS account.
However, if you want to deploy your application cross-account, I'd suggest creating a pipeline for the deployment of the same.
Hope you gained some insights from this article. Follow me for more cloud and DevOps related content.
See you in cloud!
Vishnu S.