Amazon ECS notes
These are my notes for creating a Docker image, pushing it to Amazon ECR (Elastic Container Registry), and deploying it to Amazon ECS (Elastic Container Service) using AWS Fargate (serverless for containers) using command line tools.
Create docker image on local machine ¶
install docker (macOS)
brew install homebrew/cask/docker
create directory
mkdir /tmp/my-project cd /tmp/my-project
create
/tmp/my-project/Dockerfile
:FROM python:3.9-alpine3.13 WORKDIR /app RUN echo 'Hello' > ./index.html EXPOSE 80 CMD ["python", "-m", "http.server", "80"]
create Docker image
docker build -t my-image .
test running the Docker image locally
docker run -p 80:80 my-image
go to http://localhost in the browser and see the text "Hello"
Install and configure AWS command line tools ¶
install AWS command line tools
brew install awscli
run
aws configure
and enter:- AWS Access Key ID
- AWS Secret Access Key
This creates the file
~/.aws/credentials
Create ECR repository and push image to it ¶
From https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html#use-ecr
create an Amazon ECR repository using
aws ecr create-repository
aws ecr create-repository --repository-name my-repository --region us-east-1
output:
{ "repository": { "repositoryArn": "arn:aws:ecr:us-east-1:AAAAAAAAAAAA:repository/my-repository", "registryId": "AAAAAAAAAAAA", "repositoryName": "my-repository", "repositoryUri": "AAAAAAAAAAAA.dkr.ecr.us-east-1.amazonaws.com/my-repository", "createdAt": "2021-03-17T10:48:18-07:00", "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": false }, "encryptionConfiguration": { "encryptionType": "AES256" } } }
Take note of the "registryId" and use it in place of "AAAAAAAAAAAA" below.
tag the docker image with the
repositoryUri
docker tag my-image AAAAAAAAAAAA.dkr.ecr.us-east-1.amazonaws.com/my-repository
log in to the Amazon ECR registry using
aws ecr get-login-password
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin AAAAAAAAAAAA.dkr.ecr.us-east-1.amazonaws.com
push the docker image to the Amazon ECR repository
docker push AAAAAAAAAAAA.dkr.ecr.us-east-1.amazonaws.com/my-repository
see the image in AWS console https://console.aws.amazon.com/ecr/repositories?region=us-east-1
Install ECS command line tools¶
- install
ecs-cli
. Note there isecs-cli
in addition toaws ecs
tools. The reason is probably similar to why some services are named "Amazon Service" and some are named "AWS Service". (It seems likeecs-cli
provides higher level commands.)brew install amazon-ecs-cli
Create Amazon ECS Fargate cluster ¶
From https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-cli-tutorial-fargate.html
- create a cluster using
ecs-cli up
output:ecs-cli up --cluster my-cluster --launch-type FARGATE --region us-east-1
Take note of the VPC (virtual private cloud), and two subnet IDs to use later. See the cluster in the AWS console UI: https://console.aws.amazon.com/ecs/home?region=us-east-1#/clustersINFO[0001] Created cluster cluster=my-cluster region=us-east-1 INFO[0002] Waiting for your cluster resources to be created... INFO[0002] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS VPC created: vpc-BBBBBBBBBBBBBBBBB Subnet created: subnet-CCCCCCCCCCCCCCCCC Subnet created: subnet-DDDDDDDDDDDDDDDDD Cluster creation succeeded.
Gather parameters required to deploy to ECS cluster ¶
From https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-cli-tutorial-fargate.html
Create task execution IAM role¶
create a file
/tmp/my-project/task-execution-assume-role.json
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
create the task execution role using
aws iam create-role
aws iam create-role --role-name my-task-execution-role --assume-role-policy-document file:///tmp/my-project/task-execution-assume-role.json --region us-east-1
attach the task execution role policy using
aws iam attach-role-policy
aws iam attach-role-policy --role-name my-task-execution-role --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy --region us-east-1
Get security group ID¶
get the default security group ID for the virtual private cloud (VPC) created when creating the ECS cluster using
aws ec2 describe-security-groups
. Replace "vpc-BBBBBBBBBBBBBBBBB" with your VPC IDaws ec2 describe-security-groups --filters Name=vpc-id,Values=vpc-BBBBBBBBBBBBBBBBB --region us-east-1
output:
{ "SecurityGroups": [ { "Description": "default VPC security group", "GroupName": "default", "IpPermissions": [ { "IpProtocol": "-1", "IpRanges": [], "Ipv6Ranges": [], "PrefixListIds": [], "UserIdGroupPairs": [ { "GroupId": "sg-EEEEEEEEEEEEEEEEE", "UserId": "AAAAAAAAAAAA" } ] } ], "OwnerId": "AAAAAAAAAAAA", "GroupId": "sg-EEEEEEEEEEEEEEEEE", "IpPermissionsEgress": [ { "IpProtocol": "-1", "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ], "Ipv6Ranges": [], "PrefixListIds": [], "UserIdGroupPairs": [] } ], "VpcId": "vpc-BBBBBBBBBBBBBBBBB" } ] }
Take note of the "GroupId" to be used later
Deploy to Amazon ECS cluster¶
From https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-cli-tutorial-fargate.html
create
/tmp/my-project/ecs-params.yml
replacing "subnet-CCCCCCCCCCCCCCCCC", "subnet-DDDDDDDDDDDDDDDDD", and "sg-EEEEEEEEEEEEEEEEE" with appropriate IDs from above. ECS Parameters docsversion: 1 task_definition: task_execution_role: my-task-execution-role ecs_network_mode: awsvpc task_size: mem_limit: 0.5GB cpu_limit: 256 run_params: network_configuration: awsvpc_configuration: subnets: - "subnet-CCCCCCCCCCCCCCCCC" - "subnet-DDDDDDDDDDDDDDDDD" security_groups: - "sg-EEEEEEEEEEEEEEEEE" assign_public_ip: ENABLED
create
/tmp/my-project/docker-compose.yml
replacing AAAAAAAAAAAA with the registryId:version: '3' services: web: image: 'AAAAAAAAAAAA.dkr.ecr.us-east-1.amazonaws.com/my-repository' ports: - '80:80'
deploy to the ECS cluster using
ecs-cli compose service up
. This creates a task definition and service. This uses thedocker-compose.yml
file in the current directory.ecs-cli compose --cluster my-cluster --project-name my-project --ecs-params ecs-params.yml --region us-east-1 service up --launch-type FARGATE
see the service in the web UI: https://console.aws.amazon.com/ecs/home?region=us-east-1#/clusters/my-cluster/services
Hit the server in the browser¶
configure security group to allow inbound access on port 80 using
aws ec2 authorize-security-group-ingress
aws ec2 authorize-security-group-ingress --group-id sg-EEEEEEEEEEEEEEEEE --protocol tcp --port 80 --cidr 0.0.0.0/0 --region us-east-1
get the IP address using
ecs-cli compose service ps
ecs-cli compose --cluster my-cluster --project-name my-project --region us-east-1 service ps
output:
Name State Ports TaskDefinition Health my-cluster/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/web RUNNING FF.FF.FF.FF:80->80/tcp my-project:1 UNKNOWN
Take note of the IP address under "Ports"
visit in the browser: http://FF.FF.FF.FF replacing "FF.FF.FF.FF" with your IP address
Destroy¶
delete the ECS service using
ecs-cli compose service down
ecs-cli compose --cluster my-cluster --project-name my-project --region us-east-1 service down
delete the ECS cluster using
ecs-cli down
ecs-cli down --force --cluster my-cluster --region us-east-1
delete the ECR repository using
aws ecr delete-repository
aws ecr delete-repository --repository-name my-repository --region us-east-1 --force