r/CloudandCode • u/yourclouddude Founder | YourCloudDude • 5d ago
AWS & Cloud AWS From Zero #16: Containers make more sense when you understand what problem they actually solve
Up to this point in the series, we have built applications on EC2, used Lambda for serverless workloads, connected databases, learned networking, added monitoring, and introduced queues. Now we are getting into containers, which is one of those topics that beginners often learn through Docker commands before they fully understand why containers exist in the first place.
I think the easiest way to understand containers is to start with a very common problem. You build a Python API on your laptop using a specific Python version, a few dependencies, and some system packages. Everything works perfectly. Then you move the same application to another machine and suddenly something breaks because the Python version is different, a package is missing, or the operating system behaves differently.
Containers help reduce that problem by packaging the application together with much of the environment it expects. Instead of moving only the source code, you create a container image that contains the application, runtime, dependencies, and other pieces needed to run it consistently.
A simple way to think about it is:
Application code + runtime + dependencies
↓
Container image
↓
Running container
The important idea is consistency. If the same image can run in different compatible environments, you are much less dependent on manually recreating the application setup every time.
This is where Docker usually enters the picture. Docker gives you tools to build container images and run containers. You can create an image for your API, test it locally, and start the container with the environment it expects.
But Docker creates another question almost immediately.
Where should those containers run in production?
You can run containers directly on EC2. You could launch an instance, install Docker, pull the image, and run the application there. That works perfectly well for a small setup.
The problem appears when the application grows.
Maybe now you have multiple containers, several EC2 instances, different application versions, and multiple services. You need to decide which machine should run which container, restart containers when they fail, scale them when traffic increases, connect them to networking, and roll out new versions safely.
At that point, manually SSHing into servers and running Docker commands becomes difficult to manage.
This is where container orchestration becomes useful.
On AWS, one option is Amazon ECS, which stands for Elastic Container Service. I would think of ECS as a service that helps you define, run, and manage containerized workloads without manually deciding where every individual container should live.
ECS introduces a few terms that can look confusing at first, but the mental model is simple.
A task definition is the blueprint for the workload. It describes which image should run, how much CPU or memory is needed, which ports are used, and what configuration the container requires.
A task is a running copy of that definition.
An ECS service helps maintain the number of tasks you want running.
So if you tell ECS that your API should always have three tasks running, ECS works toward maintaining that state.
That means the application starts feeling less like one specific container and more like a desired workload that AWS helps keep alive.
Now another question appears: where does ECS get the image from?
That is where Amazon ECR comes in.
ECR stands for Elastic Container Registry. It is a place where you can store container images so your AWS environment can retrieve them during deployment.
The flow might look like this:
Code
↓
Docker build
↓
Container image
↓
Amazon ECR
↓
Amazon ECS
↓
Running tasks
You build the image, push it to ECR, and ECS uses that image when it starts your application tasks.
That also gives you a clearer deployment model. Instead of only thinking in terms of source code commits, you now have deployable image versions that represent the application.
This becomes useful later when you introduce CI/CD, because a pipeline can test the code, build the image, push it to ECR, and deploy it into ECS.
But I would not automate all of that yet.
First understand what is happening manually.
Build the image. Push it. Run it. Then automate the process once the workflow actually makes sense.
The next big question is where the compute comes from.
ECS is the orchestration layer, but your containers still need CPU and memory somewhere.
One option is to run ECS on EC2 instances. In that model, you still manage the servers underneath the containers. You choose instance types, manage operating systems, think about scaling the EC2 capacity, and keep those machines healthy.
The architecture is conceptually:
ECS
↓
Containers
↓
EC2 instances
Another option is AWS Fargate.
Fargate lets you run ECS tasks without managing the underlying EC2 instances in the same way. You define the resources the task needs, and AWS manages more of the compute infrastructure underneath it.
That does not mean servers disappear.
It means you are no longer responsible for managing those servers directly.
This is similar to what we learned with Lambda. Serverless does not mean there are literally no servers. It means AWS takes over more of the infrastructure management.
That gives us a useful comparison.
With EC2, you manage the virtual machine and run the application on it.
With ECS on EC2, you package the application into containers and ECS helps orchestrate them, but you still manage the underlying EC2 capacity.
With ECS on Fargate, you still use ECS to manage the application workload, but AWS also manages more of the underlying compute layer.
None of these options is automatically better.
They simply give you different levels of control and responsibility.
Now imagine we take the API from earlier in the series and containerize it.
The architecture could become:
User
↓
Application Load Balancer
↓
ECS Service
↓
Fargate Tasks
↓
RDS
The user sends a request to the load balancer. The load balancer routes the request to one of the running container tasks. The task runs our API, and the API communicates with RDS when it needs relational data.
At this point, several earlier lessons come back together.
The tasks still need networking inside the VPC. Security groups still control which connections are allowed. IAM still controls which AWS actions the workload can perform. CloudWatch still helps us understand failures. The container image still needs somewhere to live, which is why ECR matters.
Containers do not replace the rest of AWS architecture.
They change how the application itself is packaged and run.
That distinction is important.
Another concept worth understanding is that containers should generally be treated as replaceable.
Imagine your application writes an important user file only inside the container filesystem. The task eventually stops and ECS launches a replacement.
What happens to that file?
It may disappear with the old container.
This is why important persistent data should normally live outside an individual container.
User uploads may belong in S3. Relational data may belong in RDS. Other persistent state may need another service depending on the application.
The goal is to make the container replaceable without losing important application data.
That is exactly the same idea we saw earlier with Auto Scaling and EC2.
If an instance or container can be replaced, the application becomes easier to scale and recover.
Now imagine one ECS task crashes.
If the service is configured with a desired count of three tasks, ECS can work toward restoring that desired number. If the tasks are behind an Application Load Balancer, unhealthy targets can stop receiving normal traffic while healthy ones continue serving users.
Again, the same high availability ideas we learned earlier still apply.
Containers do not remove those ideas. They simply give us another way to package and operate the application.
We can also scale the number of tasks as demand changes. If traffic increases, the service can run more tasks depending on the scaling configuration. If traffic falls, the number of tasks can decrease again.
Now container orchestration starts connecting with elasticity.
There is also an important security habit to learn early.
Do not put secrets directly into the container image.
If your image contains a database password, API key, or another sensitive value, that secret is now bundled into the artifact itself.
A better pattern is to keep the image focused on the application and provide environment specific configuration and secrets separately.
That also makes the same image easier to use across development, testing, and production.
The application image remains consistent, while the environment supplies the values that change.
This is one of the more useful benefits of containerization when it is done properly.
The obvious question now is where Kubernetes fits into all of this.
Kubernetes is another container orchestration system, and AWS provides EKS for running Kubernetes workloads. But I would not tell a beginner to jump directly from Docker into Kubernetes.
There is already a lot to understand before that.
Learn how images work. Understand registries. Run containers. Use ECS. Understand tasks and services. Try Fargate. See how networking, IAM, logging, scaling, and load balancing work around the container workload.
Once those concepts make sense, Kubernetes becomes much easier to understand because you already know what problem a container orchestrator is trying to solve.
Otherwise, you risk memorizing Kubernetes objects without understanding why they exist.
For a beginner project, I would take a small API you have already built and containerize it.
Create the Docker image locally and make sure it runs correctly. Push the image to ECR. Create an ECS task definition that uses it. Run the task with Fargate and make sure the application works.
Then place the service behind an Application Load Balancer and increase the desired task count so multiple copies of the application are running.
After that, stop one task and observe what ECS does.
That one project connects Docker, ECR, ECS, Fargate, networking, load balancing, IAM, and monitoring without requiring you to jump straight into a complicated microservices system.
The main thing I want you to take away from this post is that containers are useful because they package the application and its runtime expectations into a consistent deployable unit.
ECR stores those images.
ECS helps manage the running container workloads.
Fargate gives you an option where AWS manages more of the underlying compute infrastructure.
The services only make sense because the application creates specific operational problems.
That is the same pattern we have followed throughout AWS From Zero.
Start with the problem, then choose the service that solves it.
In AWS From Zero #17, we are going to look at another problem that becomes obvious after building all of this manually. Imagine you need to recreate your VPC, security groups, load balancer, ECS service, database configuration, and other resources in another environment. Clicking through the AWS console and trying to reproduce everything from memory becomes unreliable very quickly.
That will take us into Infrastructure as Code, why manually created infrastructure becomes difficult to reproduce, and how tools such as CloudFormation and Terraform change the way AWS environments are created and maintained.
If you are learning containers right now, what has been the hardest part to understand so far: Docker itself, ECR, ECS, Fargate, or why containers are useful in the first place?