This repository contains two scripts for deploying and updating applications on AWS ECS:
aws-ecs-setup.sh
: Used to set up the initial AWS infrastructure and deploy the application for the first time.continuous-deploy.sh
: Used to update the deployed application by building and pushing new Docker images.- Use
<language>/init.sql
to seed Postgres database.
Before using the scripts, ensure the following tools are installed and configured:
-
Follow the AWS CLI installation guide.
-
Verify the installation:
aws --version
Example output:
aws-cli/2.x.x Python/3.x.x Linux/Unix
-
Configure AWS CLI:
aws configure
Provide:
- Access Key ID: Your AWS access key.
- Secret Access Key: Your AWS secret key.
- Default Region: e.g.,
us-west-1
. - Output Format: e.g.,
json
.
-
Follow the Docker installation guide.
-
Verify the installation:
docker --version
Example output:
Docker version 20.x.x, build xxxxx
-
Ensure you can run Docker commands as your current user:
docker info
Make sure your application has an endpoint at this route "/api/health" in order for the ECS health check to work. The route can return anything and doesn't have to be a "real" route.
This script sets up AWS infrastructure for deploying a containerized application to ECS. It creates:
- VPC, Subnet, Internet Gateway, and Security Group
- ECS Cluster, Task Definition, and Service
- ECR Repository for Docker images
- CloudWatch Log Group
Run this script once to set up the infrastructure and deploy the application for the first time.
-
Make the script executable:
chmod +x aws-ecs-setup.sh
-
Update script variables:
LANGUAGE="<language>" # python, net, java, or node
DOCKER_FILE="<dockerfile>" # ex. "Dockerfile.python"
REGION=<aws region> #ex. "us-west-2"
NAME=<name prefix> # ex. "python" but can be anything
CONTAINER_PORT=<container port> # ex. 8000, 3001, etc.
HOST_PORT=<host port> # must match CONTAINER_PORT
-
Run the script:
./aws-ecs-setup.sh
-
The script outputs important information such as:
- VPC ID
- Subnet ID
- ECS Cluster and Service Name
- ECR Repository URI
- Public IP Address of the deployed container
-
Build Docker image locally:
DOCKER_FILE="Dockerfile.java" CONTAINER_NAME="testharness-java" IMAGE_TAG="1.0.0" LANGUAGE="java" docker build --pull --rm -f "$DOCKER_FILE" -t $CONTAINER_NAME:$IMAGE_TAG ./$LANGUAGE
This script is used for continuous deployment of updated versions of the application to ECS. It:
- Builds the Docker image.
- Pushes the image to ECR.
- Updates the ECS Service with the new image.
Run this script whenever you want to deploy a new version of the application.
-
Make the script executable:
chmod +x continuous-deploy.sh
-
Update script variables match aws-ecs-setup.sh variables:
LANGUAGE=<match aws-ecs-setup.sh value>
DOCKER_FILE=<match aws-ecs-setup.sh value>
REGION=<match aws-ecs-setup.sh value>
NAME=<match aws-ecs-setup.sh value>
CONTAINER_PORT=<match aws-ecs-setup.sh value>
HOST_PORT=<match aws-ecs-setup.sh value>
-
Run the script:
./continuous-deploy.sh
-
The script will:
- Build the Docker image using the latest code.
- Push the new image to the ECR repository.
- Update the ECS service with the new image.
The public IP is output at the end.
Creating VPC...
VPC ID: vpc-0f75e2c1c10f0a65d
Creating Subnet...
Subnet ID: subnet-022bc5238c54fabd2
Creating Internet Gateway...
Internet Gateway ID: igw-09476c73f5bde71d4
Attaching Internet Gateway to VPC...
Creating Route Table...
Route Table ID: rtb-090652a5b4b0d67ff
Creating route to Internet Gateway...
Associating Route Table with Subnet...
Modifying Subnet to assign public IPs...
Creating Security Group...
Security Group ID: sg-0f16b4598b0497dba
Authorizing inbound traffic on port 8000...
Checking if ECR repository exists...
Creating ECR repository...
ECR repository 'python-registry' created.
ECR Repository URI: 397188165174.dkr.ecr.us-west-1.amazonaws.com/python-registry
Checking if IAM role 'ecsTaskRole' exists...
IAM role 'ecsTaskRole' already exists.
Logging in to ECR...
WARNING! Your password will be stored unencrypted in /home/ola/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
DOCKER_FILE: Dockerfile.python
CONTAINER_NAME: python-test-harness
IMAGE_TAG: f79f044
Building Docker image...
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
Install the buildx component to build images with BuildKit:
https://docs.docker.com/go/buildx/
Sending build context to Docker daemon 10.29kB
Step 1/17 : FROM postgres:17
17: Pulling from library/postgres
Digest: sha256:fe4efc6901dda0d952306fd962643d8022d7bb773ffe13fe8a21551b9276e50c
Status: Image is up to date for postgres:17
---> 810c36706d00
Step 2/17 : RUN apt-get update && apt-get install -y python3 python3-pip python3-venv procps && apt-get clean && rm -rf /var/lib/apt/lists/*
---> Using cache
---> 8f27c20fef3d
Step 3/17 : RUN python3 --version
---> Using cache
---> 1534400f1e3e
Step 4/17 : COPY requirements.txt .
---> Using cache
---> c56377ba3ed4
Step 5/17 : RUN python3 -m venv /app/venv
---> Using cache
---> 969ba1906d4e
Step 6/17 : RUN /app/venv/bin/python -m pip install --upgrade pip && /app/venv/bin/pip install --no-cache-dir -r requirements.txt
---> Using cache
---> 5d71c72d4752
Step 7/17 : ENV PATH="/app/venv/bin:$PATH"
---> Using cache
---> dd14486852ea
Step 8/17 : WORKDIR /app
---> Using cache
---> 939390dc3c97
Step 9/17 : ENV POSTGRES_USER=postgres
---> Using cache
---> 0a3b33a93008
Step 10/17 : ENV POSTGRES_PASSWORD=postgres
---> Using cache
---> 9a3a1145fb02
Step 11/17 : ENV POSTGRES_DB=users
---> Using cache
---> e3b00d298c9f
Step 12/17 : COPY . .
---> Using cache
---> 0841a5e1f589
Step 13/17 : COPY init.sql /docker-entrypoint-initdb.d/init.sql
---> Using cache
---> 8adac8a525b3
Step 14/17 : COPY entrypoint.sh /usr/local/bin/
---> Using cache
---> 08c069088b5c
Step 15/17 : RUN chmod +x /usr/local/bin/entrypoint.sh
---> Using cache
---> 2aa3173ad1c6
Step 16/17 : ENTRYPOINT ["entrypoint.sh"]
---> Using cache
---> bb81f81d0c5c
Step 17/17 : EXPOSE 8000
---> Using cache
---> 727169123bac
Successfully built 727169123bac
Successfully tagged python-test-harness:f79f044
Tagging and pushing Docker image to ECR...
The push refers to repository [397188165174.dkr.ecr.us-west-1.amazonaws.com/python-registry]
a006fab3896c: Pushed
c056e713fe88: Pushed
0a1fd053ab0f: Pushed
27d0456b65fd: Pushed
a6a285f742a1: Pushed
0a0ae44a27ef: Pushed
65f98df6b00c: Pushed
74274d92bd72: Pushed
97d0da1ce974: Pushed
0b8ece3d93b7: Pushed
f1f11505df06: Pushed
88e07dfeb8ef: Pushed
bf0c9622ebbe: Pushed
4d96fc7cd897: Pushed
7b9b519c5526: Pushed
7635ed1a550f: Pushed
9d9b007dba3e: Pushed
4759ed09c338: Pushed
5e84af0b2f05: Pushed
2ee17811b681: Pushed
37430cc3024b: Pushed
c0f1022b22a9: Pushed
f79f044: digest: sha256:df55e5f8dbaceabef7745b775fe4ec7587a6345075221d09fdcfcf12007a3260 size: 4920
Checking if ECS cluster 'python-cluster' exists...
Creating ECS cluster...
ECS cluster 'python-cluster' created.
Checking if CloudWatch log group '/ecs/python' exists...
CloudWatch log group '/ecs/python' already exists.
Registering ECS task definition...
Checking if ECS service 'python-service' exists...
Creating ECS service...
ECS service 'python-service' created.
ECS service and task created successfully.
Waiting for ECS task to start...
Task not found. Retrying in 5 seconds...
Task not found. Retrying in 5 seconds...
Task not found. Retrying in 5 seconds...
Task not found. Retrying in 5 seconds...
Task ARN found: arn:aws:ecs:us-west-1:397188165174:task/python-cluster/640ed129b6a74eebbb66add70f931e2c
Waiting for ECS task to reach RUNNING state...
Task status is 'PROVISIONING'. Retrying in 5 seconds...
Task status is 'PENDING'. Retrying in 5 seconds...
Task status is 'PENDING'. Retrying in 5 seconds...
Task status is 'PENDING'. Retrying in 5 seconds...
Task status is 'PENDING'. Retrying in 5 seconds...
Task is in RUNNING state.
Task ARN: arn:aws:ecs:us-west-1:397188165174:task/python-cluster/640ed129b6a74eebbb66add70f931e2c
ENI ID: eni-0ec329c8b17a991e3
Public IP Address of Deployed Container: 54.183.166.190
Ensure you are in the root directory of the project where the docker-compose.yaml
file is located:
cd multi-container-deployment
Run the following command to start all services defined in the docker-compose.yaml
file:
docker-compose up --build
This will:
- Build Docker images for each service if they don’t already exist.
- Create and start containers for PostgreSQL, the .NET Web API, and the React UI.
- Stream logs for all running containers to the terminal.
Once all containers are up and running, access the application components at the following URLs:
- React UI: http://localhost:3000
- .NET Web API: http://localhost:8080/api/users
- Run
aws-ecs-setup.sh
once to set up the AWS infrastructure and deploy the application. - Use
continuous-deploy.sh
to build, push, and deploy updates to ECS.