Skip to main content

Search the Dev Center

Search tutorials, templates, examples, and more.

Container Service on AWS

The AWS Container Service template scaffolds a Pulumi project that deploys a containerized service to AWS. The architecture includes an Amazon ECS cluster for orchestration, AWS Fargate for serverless compute, an Application Load Balancer for internet ingress, and an Amazon Elastic Container Registry (ECR) repository for the container image. The template ships with an Nginx Dockerfile so the project deploys end to end out of the box.

Architecture diagram of the AWS Container Service template AWS Application Load Balancer ECS Cluster Fargate Service Elastic ContainerRegistry

Before you begin#

For this walkthrough, you'll need:

Create a new project#

Create a new project from the template:

Terminal window
mkdir my-container-service; cd my-container-service
pulumi new container-aws-typescript

The pulumi new command walks you through creating and configuring the new project and stack. A stack is an instance of a project, and you can have many of them (dev, staging, prod), each with its own configuration settings. Depending on the language, you may also be asked for your preferred package manager or toolchain.

This template’s settings include:

KeyDescriptionDefault
aws:regionThe AWS region to deploy intous-west-2
containerPortThe port to expose on the container80
cpuThe amount of CPU to allocate for the container512
memoryThe amount of memory to allocate for the container128
You can change values like these later with pulumi config set :
Terminal window
pulumi config set aws:region <value>
pulumi config set containerPort <value>
pulumi config set cpu <value>
pulumi config set memory <value>

Review the code#

When setup completes, you’ll see a few files:

  • Pulumi.yaml contains the project name and the language it’s written in.
  • Pulumi.dev.yaml contains the dev stack’s settings, including the values you just entered.
  • index.ts contains the program code that declares your cloud resources.

Take a moment to open them up and have a look. Here’s what’s in index.ts:

index.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
const config = new pulumi.Config();
const containerPort = config.getNumber("containerPort") || 80;
const cpu = config.getNumber("cpu") || 512;
const memory = config.getNumber("memory") || 128;
// An ECS cluster to deploy into
const cluster = new aws.ecs.Cluster("cluster", {});
// An ALB to serve the container endpoint to the internet
const loadbalancer = new awsx.lb.ApplicationLoadBalancer("loadbalancer", {});
// An ECR repository to store the application's container image
const repo = new awsx.ecr.Repository("repo", {
forceDelete: true,
});
// Build and publish the application's container image from ./app to the ECR repository
const image = new awsx.ecr.Image("image", {
repositoryUrl: repo.url,
context: "./app",
platform: "linux/amd64",
});
// Deploy an ECS Service on Fargate to host the application container
const service = new awsx.ecs.FargateService("service", {
cluster: cluster.arn,
assignPublicIp: true,
taskDefinitionArgs: {
container: {
name: "app",
image: image.imageUri,
cpu: cpu,
memory: memory,
essential: true,
portMappings: [{
containerPort: containerPort,
targetGroup: loadbalancer.defaultTargetGroup,
}],
},
},
});
// The URL at which the container's HTTP endpoint will be available
export const url = pulumi.interpolate`http://${loadbalancer.loadBalancer.dnsName}`;
// The name of the ECS cluster
export const clusterName = cluster.name;

Deploy the project#

Deploy with pulumi up. It starts with a preview of what it’s about to create and asks before it changes anything. Choose yes to deploy.

Pulumi builds the container image with Docker on your machine, so Docker needs to be running.

Terminal window
pulumi up

Pulumi adds a random suffix to most resource names, so the ECS cluster cluster becomes something like cluster-4f8a2c1. This is autonaming. It lets you deploy the same program to more than one stack, and lets Pulumi create a replacement before it deletes the old resource, without names colliding.

When it finishes, Pulumi prints the stack’s outputs, values it only knows once the resources exist:

url

The HTTP URL for the container’s endpoint.

clusterName

The name of the ECS cluster.

Read one any time with pulumi stack output <name>, or pass it to another command. This opens the service in your browser:

Terminal window
open $(pulumi stack output url)

To check it from the terminal, fetch the page and print its title:

Terminal window
curl -s $(pulumi stack output url) | grep "<title>"
<title>Welcome to nginx!</title>

If the title says 503, the load balancer is still checking the new task. Wait a minute and run it again.

Need help?

If pulumi up reports No valid credential sources found or ExpiredToken, sign in again:

Terminal window
aws login

Still stuck? Let us know.

Customize the project#

Projects created with this template expose the following configuration settings:

containerPort

Specifies the port mapping for the container. Defaults to port 80.

cpu

Specifies the amount of CPU to use with each task or each container within a task. Defaults to 512.

memory

Specifies the amount of memory to use with each task or each container within a task. Defaults to 128.

All of these settings are optional and may be adjusted either by editing the stack configuration file directly (by default, Pulumi.dev.yaml) or by changing their values with pulumi config set.

For example, give the container 256 MiB of memory:

Terminal window
pulumi config set memory 256
pulumi up

pulumi up registers a new task definition and replaces the running task with one that has the new limit. To see it, ask the stack’s ECS cluster for its task’s memory:

Terminal window
export AWS_DEFAULT_REGION=$(pulumi config get aws:region)
CLUSTER=$(pulumi stack output clusterName)
aws ecs describe-tasks --cluster "$CLUSTER" \
--tasks $(aws ecs list-tasks --cluster "$CLUSTER" --query "taskArns" --output text) \
--query "tasks[].containers[].memory" --output text
256

Using your own container image#

If you already have a container image you’d like to build your container service with, replace the Dockerfile and application code in the app folder, then redeploy with pulumi up.

To see what else you can add, browse the AWS provider in the Pulumi Registry.

Cleaning up#

You can cleanly destroy the stack and all of its infrastructure with pulumi destroy:

Terminal window
pulumi destroy

Learn more#

Related

The infrastructure as code platform for any cloud.