1. Answers
  2. Using aws ecs with cloudfront

How Do I Set Up an AWS ECS Service Fronted by CloudFront?

Introduction

Setting up an AWS ECS (Elastic Container Service) service fronted by CloudFront involves several critical steps. This setup allows you to deploy and manage containerized applications with the scalability and reliability of AWS infrastructure. The main objective of this guide is to walk you through the process of creating an ECS cluster, defining a task definition, setting up the service, and configuring a CloudFront distribution to route traffic efficiently to your ECS service.

Step-by-Step Explanation

Below is a detailed breakdown of each step involved in the setup process, along with the complete TypeScript program:

  1. VPC and Subnets: Start by defining the networking resources, such as a Virtual Private Cloud (VPC) and subnets, which are essential for hosting ECS services.

  2. Cluster: Create an ECS cluster to host your services. This cluster will manage the deployment and scaling of your containerized applications.

  3. Task Definition: Define a task with the necessary container definitions, specifying the Docker image that your application will use.

  4. Service: Set up an ECS service to run the defined task. This service ensures that the specified number of tasks are running and healthy.

  5. Load Balancer: Create an Application Load Balancer (ALB) to route incoming traffic to your ECS service. The ALB will distribute traffic across the tasks running in your ECS cluster.

  6. CloudFront Distribution: Configure a CloudFront Distribution to route traffic from the internet to your ALB, providing a global content delivery network for your application.

Detailed Configuration

Here’s the complete TypeScript code to implement the setup:

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";

// VPC and Subnets
const example = new aws.ec2.Vpc("example", {cidrBlock: "10.0.0.0/16"});
const exampleSubnet: aws.ec2.Subnet[] = [];
for (const range = {value: 0}; range.value < 2; range.value++) {
    exampleSubnet.push(new aws.ec2.Subnet(`example-${range.value}`, {
        vpcId: example.id,
        cidrBlock: std.cidrsubnetOutput({
            input: example.cidrBlock,
            newbits: 8,
            netnum: range.value,
        }).apply(invoke => invoke.result),
        availabilityZone: [
            "us-west-2a",
            "us-west-2b",
        ][range.value],
    }));
}
const exampleInternetGateway = new aws.ec2.InternetGateway("example", {vpcId: example.id});
const albSg = new aws.ec2.SecurityGroup("alb_sg", {
    name: "alb_sg",
    vpcId: example.id,
    ingress: [{
        fromPort: 80,
        toPort: 80,
        protocol: "tcp",
        cidrBlocks: ["0.0.0.0/0"],
    }],
    egress: [{
        fromPort: 0,
        toPort: 0,
        protocol: "-1",
        cidrBlocks: ["0.0.0.0/0"],
    }],
});
const ecsSg = new aws.ec2.SecurityGroup("ecs_sg", {
    name: "ecs_sg",
    vpcId: example.id,
    ingress: [{
        fromPort: 80,
        toPort: 80,
        protocol: "tcp",
        securityGroups: [albSg.id],
    }],
    egress: [{
        fromPort: 0,
        toPort: 0,
        protocol: "-1",
        cidrBlocks: ["0.0.0.0/0"],
    }],
});
// ECS Cluster
const exampleCluster = new aws.ecs.Cluster("example", {name: "example"});
// ECS Task Definition
const exampleTaskDefinition = new aws.ecs.TaskDefinition("example", {
    family: "task_definition",
    networkMode: "awsvpc",
    requiresCompatibilities: ["FARGATE"],
    cpu: "256",
    memory: "512",
    containerDefinitions: JSON.stringify([{
        name: "app",
        image: "nginx",
        portMappings: [{
            containerPort: 80,
            hostPort: 80,
        }],
    }]),
});
const exampleTargetGroup = new aws.lb.TargetGroup("example", {
    name: "example-tg",
    port: 80,
    protocol: "HTTP",
    vpcId: example.id,
    healthCheck: {
        path: "/",
        interval: 30,
        timeout: 5,
        healthyThreshold: 2,
        unhealthyThreshold: 2,
    },
});
// Load Balancer
const exampleLoadBalancer = new aws.lb.LoadBalancer("example", {
    name: "example-lb",
    internal: false,
    loadBalancerType: "application",
    securityGroups: [albSg.id],
    subnets: exampleSubnet.map(__item => __item.id),
});
const exampleListener = new aws.lb.Listener("example", {
    loadBalancerArn: exampleLoadBalancer.arn,
    port: 80,
    protocol: "HTTP",
    defaultActions: [{
        type: "forward",
        targetGroupArn: exampleTargetGroup.arn,
    }],
});
// ECS Service
const exampleService = new aws.ecs.Service("example", {
    name: "example-service",
    cluster: exampleCluster.id,
    taskDefinition: exampleTaskDefinition.arn,
    desiredCount: 1,
    launchType: "FARGATE",
    networkConfiguration: {
        subnets: exampleSubnet.map(__item => __item.id),
        securityGroups: [ecsSg.id],
        assignPublicIp: true,
    },
    loadBalancers: [{
        targetGroupArn: exampleTargetGroup.arn,
        containerName: "app",
        containerPort: 80,
    }],
}, {
    dependsOn: [exampleListener],
});
// CloudFront Distribution
const exampleDistribution = new aws.cloudfront.Distribution("example", {
    origins: [{
        domainName: exampleLoadBalancer.dnsName,
        originId: "alb_origin",
        customOriginConfig: {
            httpPort: 80,
            httpsPort: 443,
            originProtocolPolicy: "http-only",
            originSslProtocols: ["TLSv1.2"],
        },
    }],
    enabled: true,
    defaultRootObject: "index.html",
    defaultCacheBehavior: {
        allowedMethods: [
            "DELETE",
            "GET",
            "HEAD",
            "OPTIONS",
            "PATCH",
            "POST",
            "PUT",
        ],
        cachedMethods: [
            "GET",
            "HEAD",
        ],
        targetOriginId: "alb_origin",
        forwardedValues: {
            queryString: false,
            cookies: {
                forward: "none",
            },
        },
        viewerProtocolPolicy: "allow-all",
        minTtl: 0,
        defaultTtl: 3600,
        maxTtl: 86400,
    },
    restrictions: {
        geoRestriction: {
            restrictionType: "none",
        },
    },
    viewerCertificate: {
        cloudfrontDefaultCertificate: true,
    },
});
export const cloudfrontDomainName = exampleDistribution.domainName;

Key Points

  • Networking: Establish a VPC and subnets to provide a secure and isolated network for your ECS services.
  • ECS Cluster: Utilize the cluster to manage and scale your containerized applications.
  • Task Definition: Specify the container configurations and resources needed for your application.
  • Application Load Balancer: Distribute incoming traffic across your ECS tasks for high availability.
  • CloudFront: Use CloudFront to deliver content with low latency and high transfer speeds.

Conclusion

By following these steps, you can successfully set up an AWS ECS service with CloudFront, providing a robust, scalable, and globally distributed infrastructure for your applications. This configuration ensures that your applications are delivered efficiently and securely to users worldwide.

Deploy this code

Want to deploy this code? Sign up for a free Pulumi account to deploy in a few clicks.

Sign up

New to Pulumi?

Want to deploy this code? Sign up with Pulumi to deploy in a few clicks.

Sign up