As I continue to dive deeper into backend development and cloud infrastructure, I've realized that understanding how to effectively deploy and manage containers is a game-changer. Mastering cloud orchestration tools is also a huge differentiator for fresh graduates stepping into the fast-paced tech industry. Recently, I completed a comprehensive lab on Amazon Elastic Container Service (ECS), and I want to share a walkthrough of that experience.
If you are familiar with Docker and basic networking (like TCP ports and Load Balancers), this guide will help you understand how AWS manages containerized applications at scale.
Here are a few snapshots from the lab environment to give you an idea of the interface and objectives:
The Architecture Overview
Before deploying anything, it's crucial to understand the environment. For this walkthrough, the foundational infrastructure was already provisioned.
Here is the architecture diagram of the environment we are working with:
Amazon VPC: Configured with two public subnets across separate Availability Zones for high availability.
Network Load Balancer (NLB): Routes external internet traffic to our containers.
Auto Scaling Group: Manages the underlying EC2 instances that will host our containers.
Amazon ECS Cluster: The logical grouping of our EC2 instances and tasks.
Step 1: Registering a Task Definition
In ECS, a Task Definition acts as the blueprint for your application. It’s a JSON file that tells ECS exactly how to run your Docker container, specifying details like CPU/memory allocation, port mappings, and volume mounts.
For this lab, I created a task definition family named yourApp-demo. Using a JSON configuration, I defined two containers:
simple-app: Uses the httpd:2.4 image, maps port 80, and mounts a shared volume to the Apache document root.
busybox: Uses a basic busybox image and runs a shell script to continuously generate an index.html file (saying "Congratulations!") into that shared volume.
This setup perfectly demonstrates how sidecar containers can interact via shared volumes.
Step 2: Creating an ECS Service
With the blueprint ready, the next step is to actually run it. An ECS Service ensures that your desired number of tasks are constantly running and automatically replaces any that fail.
Here is how I configured the service:
Service Name: myFirstService
Launch Type: EC2 (I opted for EC2 over Fargate, meaning the tasks run on instances we manage via the Auto Scaling Group).
Service Type: Replica (Maintains a specific number of instances; I started with a desired task count of 1).
Load Balancing: Connected to the pre-provisioned Network Load Balancer on port 80 to expose the app to the internet.
After waiting a few minutes for the deployment to complete, I grabbed the NLB's DNS name, pasted it into my browser, and successfully viewed the "Congratulations!" sample app page!
Step 3: Zero-Downtime Application Updates
One of the most powerful features of ECS is its ability to handle rolling updates smoothly. If you need to push a new version of your backend code, you don't want your users to experience downtime.
To test this, I simulated an application update:
Created a New Revision: I duplicated the original task definition but tweaked the shell script in the busybox container to output "Thank You!" instead of "Congratulations!".
Updated the Service: I navigated back to myFirstService and updated it to use the (LATEST) revision of the task definition.
What happens behind the scenes?
ECS gracefully spins up a new task with the updated definition. Once it registers as healthy, ECS drains the connections from the old task and shuts it down. Refreshing the browser seamlessly revealed the new "Thank You!" message without any service interruption.
Step 4: Scaling the Service Capacity
Handling traffic spikes is a core requirement for modern applications. ECS makes dynamic scaling incredibly straightforward.
To scale up:
I updated myFirstService again.
Changed the Desired tasks from 1 to 2.
By checking the deployment events, I could watch the ECS scheduler spring into action, provisioning a second task and distributing it appropriately across the cluster to ensure high availability. If traffic were to drop, scaling down is just as easy—ECS would gracefully terminate the excess tasks to save resources.
Final Thoughts
Stepping through this lab provided a fantastic, practical look at how Amazon ECS bridges the gap between raw Docker containers and enterprise-grade deployment. By mastering Task Definitions, Services, Rolling Updates, and Scaling, building a robust, highly available backend becomes a much more manageable reality.
If you're looking to automate this further in a production environment, exploring the underlying AWS CloudFormation templates used to build this architecture is a great next step!
i wanted to know if i can additionally change the code and costumize it so,
This JSON code is an Amazon Elastic Container Service (ECS) Task Definition. Think of it as a blueprint that tells AWS exactly how to run and configure your application’s Docker containers.
What makes this specific code interesting is that it perfectly demonstrates the Sidecar Pattern. Instead of putting everything into one massive container, it splits the workload between two containers that work together and share a storage volume.
Here is a breakdown of how it works and how you can customize it for your own projects.
🔍 Deconstructing the Code
1. The Main Server (simple-app)
The Job: This container is running an Apache HTTP web server ("image": "httpd:2.4").
Networking: It maps port 80 on the host to port 80 on the container (portMappings), which is the standard port for web traffic.
Storage: It mounts a volume named my-vol to the /usr/local/apache2/htdocs directory. This is the exact folder where Apache looks for files to serve to visitors.
2. The Sidekick (busybox)
The Job: This container uses a lightweight Linux image ("image": "busybox"). It doesn't serve web traffic; its only job is to run a script in the background.
The Script: The command section contains a shell script loop that continuously writes an HTML file—complete with a "Congratulations!" message and the current date/time—and saves it as index.html.
The Connection: Notice the "volumesFrom" block? It connects to the simple-app container's volume. By writing that index.html file into the shared space, the Apache server automatically serves it to anyone who visits the website.
🛠️ How to Customize It
You can modify this JSON to fit almost any architecture. Here are a few ways to tailor it:
Swap the Web Server
If you prefer a different web server, you can easily change the image in the simple-app definition. For example, you could swap "image": "httpd:2.4" to "image": "nginx:latest". You would just need to update the containerPath to Nginx's default web directory (/usr/share/nginx/html).
Deploy a Custom Backend
If you are pushing further into backend development, you probably won't be using a shell script to generate static HTML. You can completely replace this dual-container setup with a single container running your own code.
- Change the
"image"to point to your own Docker image hosted on Amazon ECR or Docker Hub (e.g., a Python container running a Django or FastAPI backend). - Remove the
busyboxcontainer entirely. - Update the
containerPortto match whatever port your backend framework listens on (like 8000 or 5000).
Adjust Compute Resources
Right now, the containers are allocated very few resources ("memory": 300, "cpu": 10). If your application does heavy data processing, you will need to scale these up.
-
CPU: Measured in CPU units (1024 units = 1 vCPU). You might bump this to
256or512. -
Memory: Measured in MiB. You could increase this to
512or1024.
Add Environment Variables
Real-world applications usually need configuration data, like database passwords or API keys. You can inject these by filling out the currently empty "environment": [] array:
"environment": [
{"name": "DATABASE_URL", "value": "your_db_connection_string"},
{"name": "ENVIRONMENT", "value": "production"}
]













Top comments (0)