Architecture Diagram:
This is a guide on how to Developing and Deploying a Basic Web Application on Amazon ECS
- Gain hands-on experience with containerization using Docker.
- Understand how to use Amazon ECR to store and manage container images.
- Learn the basics of creating and managing an Amazon ECS cluster.
- Learn about ECS and its role in orchestrating containerized applications, including task definitions, clusters, and services.
Whether you are new to Amazon ECS or looking to level up your skills, this repository has you covered.
Note
: that Tutorial using AIM with Administrative User
.
Assuming you have an AWS account
, Docker Engine AWS CLI follow these steps:
Note
: that Tutorial using EC2 with ubuntu-jammy-22.04-amd64-server
Operation System
Docker version 24.0.5
, build 24.0.5-0
ubuntu1~22.04.1
For other Operation System Follow the Official Docker Documunts here.
Below are the steps to follow:
- Step 1: Create a NodeJS Server Web Application
- Step 2: Create a Dockerfile
- Step 3: Build, Run Docker Image and Test Locally.
- Step 4: Build and Push Docker Image to Amazon ECR Repository
- Step 5: Create an Amazon VPC
- Step 6: Set Up Amazon ECS
- Step 7: Create a Task Definition
- Step 8: Deploy the Application
- Step 9: Test The Application
- Step 10: Cleanup
Create a basic Node.js Server Web Application
.
server.js:
Create a Dockerfile
to containerize the Flask application.
Dockerfile:
- Open a terminal or command prompt and navigate to the directory containing the Dockerfile and run the following command:
docker build -t <image-name> .
- Run the newly built image.
docker run -p <container-Port>:<host-Port> <image-name>
-
Replace:
<image-name>
with your image-nameex: hello-world
.
<container-Port>
with your container-Port define within Dockerfileex: 3000
.
<host-Port>
with the application port to listen on host-Portex: 3000
. -
Access
http://localhost:3000
orhttp://127.0.0.1:3000/
in your web browser to see the"Hello, this is a basic web application running on Amazon ECS!"
message
- Create an ECR repository to store your Docker images.
- Make note of the repository URI.
- Verify that your IAM user or role has the necessary permissions to access the ECR repository.
The required permissions include:
ecr:GetAuthorizationToken
ecr:BatchCheckLayerAvailability
You can attach the AmazonEC2ContainerRegistryPowerUser
policy to your IAM user or role to grant these permissions.
- Authenticate a private registry.
aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com
- Replace:
<your-region>
with your desired region.
<aws_account_id>
with your aws_account_id
aws ecr create-repository \
--repository-name <repository-name> \
--region <your-region>
- Replace:
<repository-name>
with your repository-name.
<your-region>
with your desired region.
- Tag the image to push to your private repository.
- List the images you have stored locally to identify the image to tag and push.
docker images
- Tag the image to push to
Amazon ECR private
repository.
docker tag <image-name:tag> <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/<repository-name:tag>
- Push the image.
docker push <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/<repository-name:tag>
- Replace:
<image-name:tag>
with your image-name:tagex: hello-world:latest
<aws_account_id>
with your aws_account_id.
<your-region>
with your desired region.
<repository-name>
with your repository-name.
Note
: ECR repository URI=<aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/<repository-name:tag>
Follow the Official Documunts Amazon Quick start: Publishing to Amazon ECR Pprivate repository
using the AWS CLI here.
- Authenticate a public registry.
aws ecr-public get-login-password --region <your-region> | docker login --username AWS --password-stdin public.ecr.aws
- Open a terminal or command prompt and navigate to the ECR directory and run the following command:.
aws ecr-public create-repository \
--repository-name <repository-name> \
--catalog-data file://repositorycatalogdata.json \
--region <your-region>
- Replace:
<repository-name>
with your repository-name.
<your-region>
with your desired region.
- Tag and Push an image to
Amazon ECR Public
repository.
List the images you have stored locally to identify the image to tag and push.
docker images
- Tag the image to push to your repository with your
public repository URI
which was in the response to thecreate-repository
call you made in the previous step.
docker tag <image-name:tag> public.ecr.aws/<registry_alias>/<repository-name>
- Push the image to the Amazon ECR.
docker push public.ecr.aws/<registry_alias>/<repository-name>
- Pull the image from the Amazon ECR.
docker pull public.ecr.aws/g7p1j8g3/hello-flask:v.1
- Replace:
<registry_alias>
with your registry_alias.
<repository-name>
with your repository-name.
Follow the Official Amazon Quick start: Publishing to Amazon ECR Public
using the AWS CLI here.
- you can create VPC using
AWS CLI
orcloudformation
- Create an Amazon VPC in
two availability zone
withtwo Public
andtwo Private subnets
and configuresecurity group
rules to allow traffic on Port 3000usingAWS CLI
.
- Open a terminal or command prompt and run the following command:.
aws ec2 create-vpc \
--cidr-block <CIDR block> \
--region <your-region> \
--tag-specification ResourceType=vpc,Tags='<Tags>'
- Replace:
<CIDR block>
with your desired CIDR block.ex: 10.0.0.0/16
.
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS-VPC"},{Key=Owner,Value="Network Team"}]'
.
- Retrieve the VPC ID:
aws ec2 describe-vpcs \
--query 'Vpcs[0].VpcId'
- outputs
"vpc-0fc3b7820d89cea5a"
- Make note and write down
VpcId
:
- allows communication between your VPC and the internet.
aws ec2 create-internet-gateway \
--region <your-region> \
--tag-specification ResourceType=internet-gateway,Tags='<Tags>'
- Retrieve the Internet Gateway ID:
aws ec2 describe-internet-gateways \
--query 'InternetGateways[0].InternetGatewayId'
- outputs
"igw-024ee93e80e7ca1ec"
- Make note and write down
InternetGatewayId
:
aws ec2 attach-internet-gateway \
--internet-gateway-id <Internet-Gateway-ID> \
--vpc-id <VPC_ID>
- Replace:
<VPC_ID>
with your Retrieved VPC ID .
<Internet-Gateway-ID>
with your Retrieved INTERNET GATEWAY ID .
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS Internet-Gateway"},{Key=Owner,Value="Network Team"}]'
.
- Create two Subnets on one availability zone .
aws ec2 create-subnet \
--vpc-id <VPC_ID> \
--cidr-block <CIDR block> \
--region <your-region> \
--availability-zone <your-az-1> \
--tag-specification ResourceType=subnet,Tags='<Tags>'
- Create two Subnets on second availability zone .
aws ec2 create-subnet \
--vpc-id <VPC_ID> \
--cidr-block <CIDR block> \
--region <your-region> \
--availability-zone <your-az-2> \
--tag-specification ResourceType=subnet,Tags='<Tags>'
- Replace:
<VPC_ID>
with your Retrieved VPC ID .
<CIDR block>
with your desired CIDR block.ex: 10.0.1.0/24
.
<your-region>
with your desired region.
<your-az-1>,<your-az-2> with your desired **availability zones**
ex: us-east-1c.<br>
with your desired **Tags**
ex: Tags='[{Key=Name,Value="ECS private subnets_1"},{Key=Owner,Value="Network Team"}]'`.
Make note and write down:
- Subnets IDs in the VPC <PrivateSubnet01>, <PrivateSubnet02>, <PublicSubnet01>, <PublicSubnet02>
.
- Allocates two Elastic IP address
aws ec2 allocate-address \
--domain vpc <VPC_ID> \
--region <your-region> \
--tag-specification ResourceType=elastic-ip,Tags='<Tags>'
- Replace:
<VPC_ID>
with your Retrieved VPC ID .
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS-elastic-ip"},{Key=Owner,Value="Network Team"}]'
.
- Creates
two
NAT gateway for each Public Subnet
aws ec2 create-nat-gateway \
--subnet-id <subnet-id> \
--allocation-id <Elastic IP-id> \
--region <your-region> \
--tag-specification ResourceType=natgateway,Tags='<Tags>'
- Replace:
<subnet-id>
with your Retrieved public subnet-id .
<Elastic IP-id>
with your Retrieved region.
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS-NAT-Gateway"},{Key=Owner,Value="Network Team"}]'
.
- Creates
four
Route Table for your VPC
aws ec2 create-route-table \
--vpc-id <VPC-ID> \
--region <your-region> \
--tag-specification ResourceType=route-table,Tags='<Tags>'
- Replace:
<VPC-ID>
with your Retrieved .
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS-pub-sub-1-route-table"},{Key=Owner,Value="Network Team"}]'
.
- Associates each public subnet in your VPC to your public route table
- Associates each private subnet in your VPC to your each private route table
aws ec2 associate-route-table \
--route-table-id <route-table-id> \
--subnet-id <subnet-id>
- Replace:
<subnet-id>
with your Retrieved subnet-id .
<route-table-id>
with your Retrieved Route Table ID.
- Creates a route betwen
private route
table andNAT gateway
aws ec2 create-route \
--route-table-id <route-table-id> \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id <Internet-Gateway-ID>
- Replace:
<route-table-id>
with your Retrieved Route Table ID.
<gateway-id>
with your Retrieved NAT Gateway ID.
aws ec2 create-security-group \
--group-name <security-group-name> \
--description <Description> \
--vpc-id <VPC_ID> \
--region <your-region> \
--tag-specification ResourceType=security-group,Tags='<Tags>'
- Replace:
<security-group-name>
with your desired security-group-name .
<Description>
with your security group descriptionex: "Security group for port 3000"
.
<VPC_ID>
with your Retrieved VPC ID .
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS-security-group"},{Key=Owner,Value="security Team"}]'
.
aws ec2 authorize-security-group-ingress \
--group-id <security-group-id> \
--protocol <protocol> \
--port <port> \
--cidr <cidr> \
--region <your-region> \
--tag-specification ResourceType=security-group-rule,Tags='<Tags>'
- Replace:
<security-group-id>
with your Retrieved security group ID .
<protocol>
with your desired protocolex: protocol name (tcp , udp , icmp , icmpv6 ) or number
Protocol_Numbers.
<port>
with port for the application to listen on.ex: 3000
.
<cidr>
with your desired IPv4 CIDR rangeex: 0.0.0.0/0
.
<your-region>
with your desired region.
<Tags>
with your desired Tagsex: Tags='[{Key=Name,Value="ECS-security-group"},{Key=Owner,Value="security Team"}]'
.
-
Create an Amazon VPC in
two availability zone
withtwo Public
andtwo Private subnets
for Amazon ECS . -
Open a terminal or command prompt and navigate to the cloudformation directory and run the following command:.
aws cloudformation create-stack \ --region <your-region> \ --stack-name <stack-name> \ --template-body file://amazon-ECS-vpc-private-subnets.yaml
-
Replace:
<your-region>
with your region.
<stack-name>
with your stack-name. -
Navigate to the
cloud-formation outputs
Make note and write down:
- Subnets IDs in the VPC<PrivateSubnet01>, <PrivateSubnet02>, <PublicSubnet01>, <PublicSubnet02>
- SecurityGroups<Security group>
-
aws ecs create-cluster \
--cluster-name <cluster-name> \
--region <your-region> \
--tags <tag>
- Replace:
<cluster-name>
with your cluster name.
<your-region>
with your desired region.
<tag>
your tag : Add tags for better organizationex: Replace <tag> with Key=name,Value=ECS-project key=dep,value=dev.
Follow the Official Creating a cluster with a Fargate Linux task using the AWS CLI here
aws ecs register-task-definition \
--family <task-name> \
--execution-role-arn "arn:aws:iam::<aws_account_id>:role/ecsTaskExecutionRole" \
--network-mode <network-mode> \
--region <your-region> \
--cpu "<cpu>" \
--memory "<memory>" \
--container-definitions '[{"name":"<container-name>","image":"<aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/<image-name>:<tag>","essential":true,"portMappings":[{"containerPort":<container-Port>,"protocol":"tcp"}]}]' \
--tags <tag>
- Replace:
<task-name>
with your task-name .
<aws_account_id>
with your aws_account_id.
<your-region>
with your desired region.
<network-mode>
with your desired network-modeThe valid values arenone
,bridge
,awsvpc
, andhost
. If no network mode is specified, the default isbridge
.
<cpu>
with your desired cpuex: "256"
.
<memory>
with your desired memoryex: "512"
.
<container-Port>
with your the application port to listen onex: 3000
.
<image-name>:<tag>
with your image-name:tagex: hello-world:latest
.
<container-name>
with the container-name .
Note
: ECR repository URI= <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/<image-name:tag>
.
Follow the Official Amazon ECR image and task definition here and here
aws ecs create-service \
--cluster <cluster-name> \
--service-name <service-name> \
--task-definition <task-name> \
--desired-count 1 \
--launch-type <launch-type> \
--region <your-region> \
--network-configuration "awsvpcConfiguration={subnets=[<PublicSubnet01>,<PublicSubnet02>,<PrivateSubnet01>,<PrivateSubnet02>],securityGroups=[<Security-GroupIds>],assignPublicIp=ENABLED}" \
--tags <tag>
- Replace:
<cluster-name>
with your cluster name.
<service-name>
with your service name.
<task-name>
with your task-name .
<desired-count>
with your desired instantiations-number of the specified task definition to place and keep running in your service.
<your-region>
with your desired region.
<launch-type>
with your desired launch-type that match network-mode The Possible values are :EC2
,FARGATE
andEXTERNAL
.
<subnets-id>
with your subnets<security-Group>
with your security Groups<tag>
your tag : Add tags for better organizationex: Replace <tag> with Key=name,Value=ECS-project key=dep,value=dev.
- Open the AWS console here
- In the navigation pane, choose Clusters Amazon Elastic Container Service.
- Choose the cluster where you ran the service.Cluster overview
- In the Cluster overview Choose Services tab, under Service name, choose the service you created in Step 7 Services.
- Choose the Tasks tab, and then choose the task in your service.Tasks.
- On the task page, in the Configuration section, under Public IP, choose Open address Public IP.
- On your browser edit IP and add
:3000
- Now you can see
"Hello, this is a basic web application running on Amazon ECS!"
Deployment
- Cleaning up your resources is an important part of managing your cloud environment.
- By following these steps, you can ensure that your resources are always clean and tidy and it will also help you to avoid unnecessary costs.
aws ecs deregister-task-definition \
--task-definition <task-name>
- Replace:
<task-name>
with your task-name .
aws ecs delete-task-definitions \
--task-definition <task-name>
- Replace:
<task-name>
with your task-name .
aws ecs delete-service \
--cluster <cluster-name> \
--service <service-name> \
--force
- Replace:
<cluster-name>
with your cluster name.
<service-name>
with your service name.
<task-name>
with your task-name .
aws ecs delete-cluster \
--cluster <cluster-name>
- Replace:
<cluster-name>
with your cluster name.
aws cloudformation delete-stack \
--stack-name <stack-name>
- Replace:
<stack-name>
with your stack-name.
- To stop an EC2 instance:
aws ec2 stop-instances \
--instance-ids <instance-id>
- To terminate an EC2 instance:
aws ec2 terminate-instances \
--instance-ids <instance-id>
-
Replace:
<instance-id>
with your instance-ids. -
Remember to always follow the best practices and guidelines provided by your cloud provider to ensure a smooth and efficient cleanup process.
-
Regularly reviewing and optimizing your resource utilization can not only save costs but also improve the overall performance and efficiency of your cloud infrastructure.
Keep in mind that this example is minimal and focuses on the basic steps.
In a production scenario, you would likely include morefeatures
,security measures
, andconfigurations
.