This blog post will explain what is required to build an Application Load Balanced Fargate Service in AWS CDK.
Application Load Balanced Fargate Service architecture diagram
Before we can start to build a Fargate service we need to set up a virtual private cloud (VPC). A VPC is a logically isolated virtual network that allows you to launch your AWS resources such as Fargate services.
You can specify an IP address range for the VPC, add subnets, associate security groups, and configure route tables. For this blog post, we’ll create a VPC with 9 subnets divided over 3 Availability Zones (AZs) in AWS CDK.
The following shows an example of how to create a VPC.
// Create a VPC with 9x subnets divided over 3 AZ's
const myvpc = new ec2.Vpc(this, 'SkeletonVpc', {
cidr: '172.31.0.0/16',
natGateways: 1,
maxAzs: 3,
subnetConfiguration: [
{
cidrMask: 20,
name: 'public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 20,
name: 'application',
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
},
{
cidrMask: 20,
name: 'data',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
});
Once the VPC is built we create the ECS Cluster which is required to manage the Fargate service. The following code snippet allows us to create the ECS Cluster:
// Create an ECS cluster
const cluster = new ecs.Cluster(this, 'service-cluster', {
clusterName: 'service-cluster',
containerInsights: true,
vpc: vpc,
});
As you can see in the snippet we reference the SkeletonVpc
. This tells AWS CDK to deploy the ECS Cluster in the VPC we created earlier.
Once we’ve deployed the dependent resources, we can continue with the actual Fargate resource.
The next snippet uses the official AWS CDK Construct library for higher-level ECS Constructs (aws-ecs-patterns) to create the Application Load Balanced Fargate Service.
// Create a Fargate container image
const image = ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample');
// Create higher level construct containing the Fargate service with a load balancer
new ecspatterns.ApplicationLoadBalancedFargateService(
this,
'amazon-ecs-sample',
{
cluster,
circuitBreaker: {
rollback: true,
},
cpu: 512,
desiredCount: 1,
taskImageOptions: {
image: image,
containerPort: 80,
logDriver: ecs.LogDrivers.awsLogs({
streamPrefix: id,
logRetention: logs.RetentionDays.ONE_YEAR,
}),
},
}
);
Let’s explain what has been built.
cluster
– hosts the Fargate services or tasks.
circuitBreaker
– Enabling this allows you to roll back your ECS tasks automatically in CloudFormation after a threshold of failed deployments (health check failed) exceeds the limit.
cpu
– sets the number of CPU units that are used by the Fargate task, the default is 256.
desiredCount
– The desired number of instantiations of the task definition to keep running on the service.
new ecspatterns.ApplicationLoadBalancedFargateService
– here the Application Load Balanced Fargate Service resource is instantiated from the ECS patterns library. We reference the cluster we created in the previous section to let AWS CDK know where to deploy the Fargate Service.
In the taskImageOptions
we define the details for the container such as the image. We create the image from the asset folder in our project by doing: const image = ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample');
. AWS CDK will automatically pull the amazon-ecs-sample image from Amazon ECR and use that to deploy the Fargate service.
The containerPort: 80
tells which port to expose from the Fargate service.
The logDriver
is not required, AWS CDK will automatically create it for you, but by default, it doesn’t set retention on the logs. So I’ve included an example to show how you can set a retention rate of one year on the AWS Logs of the Fargate Service by doing: logRetention: logs.RetentionDays.ONE_YEAR
.
The code for this article is available on my GitHub repository
So to finish up we’ve:
Using “higher-level” constructs or so-called “patterns, such as the aws-ecs-patterns
we’ve used in this example does a lot of the heavy lifting in creating a lot of AWS resources and including sensible defaults.
Using these patterns can save a lot of “Development” time which is definitely a huge advantage. A downside of using such patterns is that you have less customizability because of the sensible defaults that were already baked into the library.