# Choose your region, and store it in this environment variable
export AWS_DEFAULT_REGION=ap-southeast-1
echo "export AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> ~/.bashrc"
# Install software
sudo yum -y install jq gettext
sudo curl -so /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-latest
sudo chmod +x /usr/local/bin/ecs-cli
This installs some handy text parsing utilities, and the latest ecs-cli.
cd ~/environment
git clone https://github.com/brentley/container-demo.git
cd ~/environment
git clone https://github.com/brentley/ecsdemo-frontend.git
git clone https://github.com/brentley/ecsdemo-nodejs.git
git clone https://github.com/brentley/ecsdemo-crystal.git
cd ~/environment/container-demo
aws cloudformation deploy --stack-name container-demo --template-file cluster-fargate-private-vpc.yml --capabilities CAPABILITY_IAM
aws cloudformation deploy --stack-name container-demo-alb --template-file alb-external.yml
At a high level, we are building what you see in the diagram. We will have 3 availability zones, each with a public and private subnet. The public subnets will hold service endpoints, and the private subnets will be where our workloads run. Where the image shows an instance, we will have containers on AWS Fargate.
export clustername=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`ClusterName`].OutputValue' --output text)
export target_group_arn=$(aws cloudformation describe-stack-resources --stack-name container-demo-alb | jq -r '.[][] | select(.ResourceType=="AWS::ElasticLoadBalancingV2::TargetGroup").PhysicalResourceId')
export vpc=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`VpcId`].OutputValue' --output text)
export ecsTaskExecutionRole=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`ECSTaskExecutionRole`].OutputValue' --output text)
export subnet_1=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`PrivateSubnetOne`].OutputValue' --output text)
export subnet_2=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`PrivateSubnetTwo`].OutputValue' --output text)
export subnet_3=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`PrivateSubnetThree`].OutputValue' --output text)
export security_group=$(aws cloudformation describe-stacks --stack-name container-demo --query 'Stacks[0].Outputs[?OutputKey==`ContainerSecurityGroup`].OutputValue' --output text)
cd ~/environment
This creates our infrastructure, and sets several environment variables we will use to automate deploys.
ecs-cli configure --region $AWS_DEFAULT_REGION --cluster $clustername --default-launch-type FARGATE --config-name container-demo
We set a default region so we can reference the region when we run our commands.
aws ec2 authorize-security-group-ingress --group-id "$security_group" --protocol tcp --port 3000 --cidr 0.0.0.0/0
We know that our containers talk on port 3000, so authorize that traffic on our security group:
cd ~/environment/ecsdemo-frontend
envsubst < ecs-params.yml.template >ecs-params.yml
ecs-cli compose --project-name ecsdemo-frontend service up \
--create-log-groups \
--target-group-arn $target_group_arn \
--private-dns-namespace service \
--enable-service-discovery \
--container-name ecsdemo-frontend \
--container-port 3000 \
--cluster-config container-demo \
--vpc $vpc
Here, we change directories into our frontend application code directory.
The envsubst
command templates our ecs-params.yml
file with our current values.
We then launch our frontend service on our ECS cluster (with a default launchtype
of Fargate)
Note: ecs-cli will take care of building our private dns namespace for service discovery, and log group in cloudwatch logs.
ecs-cli compose --project-name ecsdemo-frontend service ps \
--cluster-config container-demo
We should have one task registered.
alb_url=$(aws cloudformation describe-stacks --stack-name container-demo-alb --query 'Stacks[0].Outputs[?OutputKey==`ExternalUrl`].OutputValue' --output text)
echo "Open $alb_url in your browser"
This command looks up the URL for our ingress ALB, and outputs it. You should be able to click to open, or copy-paste into your browser.
#substitute your task id from the ps command
ecs-cli logs --task-id a06a6642-12c5-4006-b1d1-033994580605 \
--follow --cluster-config container-demo
To view logs, find the task id from the earlier ps
command, and use it in this
command. You can follow a task's logs also.
ecs-cli compose --project-name ecsdemo-frontend service scale 3 \
--cluster-config container-demo
ecs-cli compose --project-name ecsdemo-frontend service ps \
--cluster-config container-demo
We can see that our containers have now been evenly distributed across all 3 of our availability zones.
cd ~/environment/ecsdemo-nodejs
envsubst <ecs-params.yml.template >ecs-params.yml
ecs-cli compose --project-name ecsdemo-nodejs service up \
--create-log-groups \
--private-dns-namespace service \
--enable-service-discovery \
--cluster-config container-demo \
--vpc $vpc
Just like earlier, we are now bringing up one of our backend API services. This service is not registered with any ALB, and instead is only reachable by private IP in the VPC, so we will use service discovery to talk to it.
ecs-cli compose --project-name ecsdemo-nodejs service scale 3 \
--cluster-config container-demo
We can see that our containers have now been evenly distributed across all 3 of our availability zones.
cd ~/environment/ecsdemo-crystal
envsubst <ecs-params.yml.template >ecs-params.yml
ecs-cli compose --project-name ecsdemo-crystal service up \
--create-log-groups \
--private-dns-namespace service \
--enable-service-discovery \
--cluster-config container-demo \
--vpc $vpc
Just like earlier, we are now bringing up one of our backend API services. This service is not registered with any ALB, and instead is only reachable by private IP in the VPC, so we will use service discovery to talk to it.
ecs-cli compose --project-name ecsdemo-crystal service scale 3 \
--cluster-config container-demo
We can see that our containers have now been evenly distributed across all 3 of our availability zones.
You should now have 3 services, each running 3 tasks, spread across 3 availability zones. Additionally you should have zero instances to manage. :)
cd ~/environment/ecsdemo-frontend
ecs-cli compose --project-name ecsdemo-frontend service down --cluster-config container-demo
cd ~/environment/ecsdemo-nodejs
ecs-cli compose --project-name ecsdemo-nodejs service down --cluster-config container-demo
cd ~/environment/ecsdemo-crystal
ecs-cli compose --project-name ecsdemo-crystal service down --cluster-config container-demo
ecs-cli down --force --cluster-config container-demo
aws cloudformation delete-stack --stack-name container-demo-alb
aws cloudformation wait stack-delete-complete --stack-name container-demo-alb
aws cloudformation delete-stack --stack-name container-demo
aws cloudformation delete-stack --stack-name amazon-ecs-cli-setup-private-dns-namespace-$clustername-ecsdemo-frontend