When building and deploying containerized applications on AWS, Amazon ECS (Elastic Container Service) provides a powerful and flexible solution. AWS CDK (Cloud Development Kit) simplifies the process of creating ECS infrastructure by providing a programmatic approach to define cloud resources in code. This blog post walks through how to rapidly deploy ECS infrastructure using the aws-cdk-lib/aws-ecs-patterns
module, which abstracts complex tasks like load balancing, service management, and autoscaling.
We’ll cover how to create an ECS Fargate service, set up an Application Load Balancer (ALB), enable auto-scaling, and integrate AWS Secrets Manager for securely managing sensitive data, all with just a few lines of TypeScript code.
Prerequisites
Before getting started, make sure you have the following:
- Node.js and npm installed.
- AWS CLI configured with appropriate permissions to create resources. You can configure it with:
aws configure
- AWS CDK installed globally:
npm install -g aws-cdk
- Docker installed (optional but useful for container builds).
Setting Up the Project
To begin, create a new directory for your CDK project and initialize it with TypeScript:
mkdir ecs-fargate
cd ecs-fargate
cdk init app --language typescript
This will generate the basic project structure for an AWS CDK application. Next, install the necessary dependencies for ECS, VPC, ECR, and other related services:
npm install @aws-cdk/aws-ec2 @aws-cdk/aws-ecs @aws-cdk/aws-ecr @aws-cdk/aws-ecs-patterns @aws-cdk/aws-iam @aws-cdk/aws-elasticloadbalancingv2 @aws-cdk/aws-certificatemanager @aws-cdk/aws-secretsmanager
Now you're ready to start defining your infrastructure!
Using aws-cdk-lib/aws-ecs-patterns
The aws-cdk-lib/aws-ecs-patterns
module simplifies ECS service setup by providing high-level abstractions for common ECS patterns. It includes constructs for easy configuration of services like Application Load Balanced Fargate services, which handle the complexity of setting up load balancers, auto-scaling, and other integrations.
For example, the ApplicationLoadBalancedFargateService
construct automatically creates an ECS Fargate service with an Application Load Balancer (ALB) in front of it, managing much of the networking and routing for you.
Here’s a quick look at some key benefits of using the ApplicationLoadBalancedFargateService
construct from the aws-cdk-lib/aws-ecs-patterns
module:
- Simplified Infrastructure Setup: You don't need to manually define an Application Load Balancer or its target group—everything is handled for you.
- Built-in Auto-Scaling: Easily configure auto-scaling for your Fargate tasks based on CPU or memory usage.
- HTTPS Setup: You can easily set up SSL certificates via ACM (AWS Certificate Manager) and configure the load balancer to use HTTPS.
For more details on other architectural patterns, you can refer to the official AWS CDK ECS Patterns Documentation.
Defining the ECS Fargate Infrastructure
Here’s an example that demonstrates how to quickly deploy ECS infrastructure with AWS CDK using TypeScript.
Example: ECS Fargate Service with Application Load Balancer
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as ecs_patterns from 'aws-cdk-lib/aws-ecs-patterns';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
interface EcsFargateStackProps extends cdk.StackProps {
vpcId: string; // VPC Id
ecrRepositoryName: string; // ECR repository name
acmCertificateArn: string; // ACM certificate ARN for HTTPS
demoServiceSecretArn: string; // Secret ARN for demoService
}
export class EcsFargateStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: EcsFargateStackProps) {
super(scope, id, props);
// Lookup existing VPC using VPC ID from props
const vpc = ec2.Vpc.fromLookup(this, 'EcsVpc', {
vpcId: props.vpcId,
});
// Reference the existing ECR repository
const ecrRepo = ecr.Repository.fromRepositoryName(this, 'ExistingEcrRepository', props.ecrRepositoryName);
// ECS Cluster
const cluster = new ecs.Cluster(this, 'EcsCluster', {
clusterName: 'demo-service-EcsFargateCluster',
vpc,
});
// Task Definition
const taskDefinition = new ecs.FargateTaskDefinition(this, 'FargateTaskDef', {
memoryLimitMiB: 512,
cpu: 256,
});
// Reference the secret from AWS Secrets Manager
const taskSecret = secretsmanager.Secret.fromSecretCompleteArn(this, 'FargateTaskSecret', props.demoServiceSecretArn);
// Add a container to the task definition
const container = taskDefinition.addContainer('AppContainer', {
image: ecs.ContainerImage.fromEcrRepository(ecrRepo, 'latest'),
logging: ecs.LogDriver.awsLogs({ streamPrefix: 'ecs-app' }),
environment: {
DEBUG: 'true', // Simple environment variable
},
secrets: {
SECRET_KEY: ecs.Secret.fromSecretsManager(taskSecret, 'SECRET_KEY'), // Referencing secret for SECRET_KEY
},
});
container.addPortMappings({
containerPort: 5000, // Container runs on port 5000
});
// ACM Certificate for HTTPS
const certificate = acm.Certificate.fromCertificateArn(this, 'AcmCertificate', props.acmCertificateArn);
// Application Load Balanced Fargate Service
const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'FargateService', {
cluster,
taskDefinition,
enableExecuteCommand: true, // Enable execute command
certificate,
publicLoadBalancer: true,
loadBalancerName: 'demo-service-alb',
sslPolicy: elbv2.SslPolicy.RECOMMENDED,
serviceName: 'demo-service-EcsFargateService',
redirectHTTP: true,
protocol: elbv2.ApplicationProtocol.HTTPS, // Default HTTPS
});
// Adjust target group port to match the container port
fargateService.targetGroup.configureHealthCheck({
path: '/',
port: '5000', // Health check targets container port 5000
});
// Autoscaling
const scaling = fargateService.service.autoScaleTaskCount({
minCapacity: 1,
maxCapacity: 3,
});
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 80,
});
scaling.scaleOnMemoryUtilization('MemoryScaling', {
targetUtilizationPercent: 80,
});
// Outputs
new cdk.CfnOutput(this, 'ClusterName', {
value: cluster.clusterName,
description: 'ECS Cluster Name',
});
new cdk.CfnOutput(this, 'ServiceName', {
value: fargateService.service.serviceName,
description: 'ECS Service Name',
});
}
}
Breakdown of the Code:
VPC and ECS Cluster: We use
ec2.Vpc.fromLookup()
to reference an existing VPC based on the VPC ID passed in as a parameter, and create an ECS cluster within that VPC.ECR Repository: The Docker image for the application is stored in an existing ECR repository. We reference this repository using
ecr.Repository.fromRepositoryName()
.Task Definition: We define an ECS Fargate task with specified memory and CPU limits, referencing a Docker image from ECR. We also integrate environment variables and sensitive data from AWS Secrets Manager.
Load Balancer and HTTPS: The
ApplicationLoadBalancedFargateService
construct fromaws-cdk-lib/aws-ecs-patterns
automatically provisions an ALB, configuring SSL termination using an ACM certificate for HTTPS.Auto-Scaling: We configure auto-scaling for the Fargate service based on CPU and memory utilization, ensuring the service can dynamically adjust to demand.
Outputs: Finally, we output the ECS cluster name and service name, so they can be easily referenced.
Deploying the Stack
Step 1: Install Dependencies
Ensure that all dependencies are installed:
npm install
Step 2: Bootstrap the CDK Environment
Run the bootstrap command to prepare your AWS account for CDK:
cdk bootstrap --profile your-aws-profile
Step 3: Synthesize the Stack
Synthesize the CloudFormation template to verify the stack:
cdk synth --context vpcId=vpc-xxxxxxxx --context ecrRepositoryName=demo-service --context acmCertificateArn=arn:aws:acm:region:account-id:certificate/certificate-id --context demoServiceSecretArn=arn:aws:secretsmanager:region:account-id:secret:secret-id --profile your-aws-profile
Step 4: Deploy the Stack
Deploy the ECS Fargate stack to AWS:
cdk deploy --context vpcId=vpc-xxxxxxxx --context ecrRepositoryName=demo-service --context acmCertificateArn=arn:aws:acm:region:account-id:certificate/certificate-id --context demoServiceSecretArn=arn:aws:secretsmanager:region:account-id:secret:secret-id --profile your-aws-profile
Step 4: Verify the Deployment
After the stack is successfully deployed, AWS will automatically provision the ECS service, ALB, and other necessary resources. You can access the service via the load balancer’s public endpoint.
Conclusion
In this guide, we’ve rapidly provisioned ECS infrastructure using AWS CDK and TypeScript. By leveraging the ApplicationLoadBalancedFargateService
construct from aws-cdk-lib/aws-ecs-patterns
, we were able to simplify the creation of a fully managed ECS service that includes an ALB, auto-scaling, and HTTPS support.
This approach significantly reduces the complexity of deploying containerized applications on AWS, allowing you to focus on developing your application instead of managing infrastructure.
For more detailed information, check out the official AWS CDK ECS Patterns Documentation.
GitHub Repository
You can find the complete code for this deployment in my GitHub repository.
Top comments (0)