ECR, ECS, Fargate, and Container Operations
Amazon ECR, Amazon ECS, and AWS Fargate form a managed container path: store an image, describe how it should run, schedule copies of it, and let AWS provide the compute layer. The practical outcome is a repeatable way to run containerized services without managing container hosts, while still controlling image provenance, network placement, IAM permissions, logging, scaling, and deployment behavior.
In this part of the AWS Cloud Engineering course, containers sit between lower-level compute and event-driven serverless patterns. The goal is not just to start a container. The goal is to understand which AWS component owns each step from build to request handling, so you can diagnose failures and make deliberate trade-offs when a service must be patched, scaled, rolled back, or isolated.
How the Pieces Fit
ECR is the image registry. A repository stores tagged container images and their content-addressed digests. A tag such as prod is mutable unless you configure it otherwise; a digest such as sha256:... identifies one exact manifest. ECR can scan images, encrypt storage, enforce lifecycle policies, replicate images across Regions, and expose a private registry endpoint to ECS tasks.
ECS is the orchestrator. A cluster is a logical scheduling boundary. A task definition is a versioned blueprint containing one or more containers, CPU and memory requirements, port mappings, environment variables, logging configuration, health checks, and IAM roles. A task is a running copy of a task definition. A service keeps a desired number of tasks running and replaces unhealthy tasks. With an Application Load Balancer, ECS also registers and deregisters task IPs in target groups during deployments.
Fargate is the serverless compute engine for ECS tasks. With the Fargate launch type, you do not provision EC2 instances or container agents. Each task receives isolated compute resources, an elastic network interface in your VPC, and the IAM credentials assigned to its task role. You still choose subnets, security groups, CPU, memory, platform capabilities, log destinations, and rollout behavior.
Request and Deployment Flow
A typical service begins when CI builds an image and pushes it to ECR. ECS does not run source code; it pulls the image referenced by the task definition. When you update an ECS service to a new task definition revision, the ECS service scheduler starts replacement tasks, waits for container and load balancer health checks, shifts capacity according to the deployment configuration, and stops old tasks after the new tasks are healthy. If tasks cannot become healthy, the service event stream, stopped task reason, CloudWatch Logs, and target group health reason usually identify the failing layer.
There are two IAM roles to keep distinct. The task execution role lets the ECS agent pull private images from ECR, write logs, and fetch supported secrets at startup. The task role is delivered to application code inside the container and should contain only the AWS API permissions the application needs. Mixing these roles commonly leads either to startup failures or to containers receiving broader application permissions than necessary.
Configuration Anatomy
The smallest useful ECS service design specifies an ECR image, task definition, cluster, service, networking, and logs. The task definition declares CPU and memory at task level for Fargate, container images, port mappings, essential containers, health checks, and the awslogs log driver. The service declares desired count, launch type or capacity provider strategy, subnets, security groups, load balancer target group, deployment minimum and maximum percentages, and optional auto scaling policies.
Use image digests or immutable tags for release certainty. Use separate repositories or clear tag conventions for different applications. Put runtime configuration in environment variables or secrets, not in baked images. Prefer private subnets for application tasks that sit behind a load balancer. Grant outbound access through a NAT gateway or VPC endpoints for ECR, CloudWatch Logs, and any dependency the task must reach.
Example 1: Build and Store the Image
This Dockerfile creates a minimal static web container. It is intentionally small so the registry and deployment mechanics are visible. The expected behavior is an image that serves files from Nginx on port 80 when run locally or by ECS.
FROM nginx:stable-alpine
COPY public/ /usr/share/nginx/html/
EXPOSE 80
HEALTHCHECK CMD wget -qO- http://127.0.0.1/ || exit 1
After the image builds, authenticate Docker to ECR, create the repository if needed, tag the image with both a human-readable version and the repository URI, then push it. The deterministic output from the final command includes a pushed layer summary and a digest. Record that digest in your release notes because it identifies the exact image ECS should run.
set -euo pipefail
AWS_REGION="us-east-1"
APP_NAME="course-web"
VERSION="v1"
ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
REPO_URI="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${APP_NAME}"
aws ecr describe-repositories --repository-names "${APP_NAME}" --region "${AWS_REGION}" >/dev/null 2>&1 || \
aws ecr create-repository --repository-name "${APP_NAME}" --region "${AWS_REGION}" >/dev/null
aws ecr get-login-password --region "${AWS_REGION}" | \
docker login --username AWS --password-stdin "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
docker build -t "${APP_NAME}:${VERSION}" .
docker tag "${APP_NAME}:${VERSION}" "${REPO_URI}:${VERSION}"
docker push "${REPO_URI}:${VERSION}"
Example 2: Describe a Fargate Task
The task definition is the contract ECS uses to start containers. This fragment shows the key fields rather than a full account-specific document. The container listens on port 80, writes logs to CloudWatch Logs, and has a health check that ECS can use to classify the task. The execution role is for startup operations; the task role would be used by the application after it starts.
{
"family": "course-web",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::111122223333:role/courseWebTaskRole",
"containerDefinitions": [
{
"name": "web",
"image": "111122223333.dkr.ecr.us-east-1.amazonaws.com/course-web:v1",
"essential": true,
"portMappings": [{ "containerPort": 80, "protocol": "tcp" }],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/course-web",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "wget -qO- http://127.0.0.1/ || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 20
}
}
]
}
If the image exists and the roles are valid, registering this definition creates revision course-web:1 or the next available revision. Registering again creates a new immutable revision; existing tasks keep using the revision they were started with until the service replaces them.
Example 3: Run a Service on Fargate
A service keeps the desired count running. This command assumes the cluster, subnets, security group, target group, and log group already exist. The expected result is two running tasks, each with its own elastic network interface, registered as healthy targets after the application responds on the configured health path.
set -euo pipefail
aws ecs create-service \
--cluster course-containers \
--service-name course-web \
--task-definition course-web:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-0123456789abcdef0,subnet-0fedcba9876543210],securityGroups=[sg-0123456789abcdef0],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:111122223333:targetgroup/course-web/abc123,containerName=web,containerPort=80" \
--deployment-configuration "minimumHealthyPercent=100,maximumPercent=200" \
--region us-east-1
The deployment configuration allows ECS to start up to four tasks during replacement while keeping at least two healthy tasks available. That is a reliability trade-off: it avoids intentional capacity dips, but it needs enough subnet IP capacity and Fargate quota to run old and new tasks at the same time.
Design Choices and Trade-Offs
Fargate removes host patching, AMI selection, cluster bin packing, and daemon maintenance. In return, you accept Fargate’s supported CPU and memory combinations, networking model, startup behavior, and pricing unit. ECS on EC2 can be cheaper for consistently high utilization or specialized host requirements, but it gives you more operational ownership.
Mutable image tags are convenient for development but risky for production because the same task definition can pull different image bytes at different times. Immutable tags or digests make rollback and audit easier. Aggressive ECR lifecycle policies reduce storage cost, but deleting image versions that are still referenced by old task definitions can break emergency rollback.
Choose health checks carefully. A container health check validates the process inside the task. A load balancer health check validates network routing and application response from outside the task. Both are useful, but a slow-starting application needs a realistic grace period or ECS may replace tasks that would have become healthy.
Failure Modes and Troubleshooting
Image pull failure. The symptom is a stopped task with messages such as CannotPullContainerError. Common causes are a missing image tag, no ECR permission on the execution role, blocked egress to ECR, or no VPC endpoint/NAT path from private subnets. Diagnose with aws ecs describe-tasks, confirm the repository tag or digest with aws ecr describe-images, and check the task execution role for ECR and CloudWatch Logs permissions. Correct by pushing the referenced image, fixing the role, or restoring network access to ECR and logs.
Service never reaches steady state. The symptom is a loop of new tasks starting and old tasks stopping. Causes include failing health checks, an application listening on a different port than the task definition, security groups blocking the load balancer, or insufficient startup grace. Inspect ECS service events, target group health descriptions, and container logs. Correct the port mapping, security group rules, health check path, or grace period before forcing another deployment.
Deployment stalls. The symptom is a service stuck with old and new task sets or repeated placement failures. Causes include too few free IP addresses in the selected subnets, Fargate quota limits, or deployment percentages that require more temporary capacity than available. Check service events for placement errors, count available subnet IPs, and review service quotas. Correct by adding subnets, lowering desired count temporarily, adjusting deployment percentages, or requesting quota increases.
Security, Performance, and Reliability
Security begins with image supply chain control. Scan ECR images, use least-privilege repository policies, avoid embedding credentials in layers, and keep base images current. Runtime isolation depends on task role scope, security group boundaries, secret handling, and whether tasks run in private subnets. Logs should contain request identifiers and failure categories, not secret values.
Performance depends on image size, startup work, CPU and memory selection, dependency latency, and load balancer behavior. Smaller images usually pull faster and reduce deployment time. Under-sized CPU can make health checks time out during bursts; over-sized tasks waste spend. Reliability comes from desired count across multiple Availability Zones, health checks that reflect real readiness, deployment rollback plans, and alarms on task count, target health, error rate, latency, and saturation.
Hands-On Lab
Prerequisites: AWS CLI configured for a sandbox account, Docker, permission to use ECR, ECS, IAM, CloudWatch Logs, VPC, and Elastic Load Balancing, plus an existing VPC with at least two subnets. Use a non-production account because the lab creates billable resources.
- Create a small web application directory containing
public/index.htmland the Dockerfile from Example 1. - Run the ECR build and push commands, then record the repository URI and image digest from the push output.
- Create a CloudWatch log group named
/ecs/course-web. - Create or reuse an ECS cluster named
course-containers. - Create an execution role with the managed ECS task execution permissions, and create a narrowly scoped task role for the application.
- Register a task definition using the structure in Example 2, replacing account, Region, role ARNs, and image URI.
- Create an Application Load Balancer, target group, and security groups so the load balancer can reach task port 80.
- Create the ECS service from Example 3 using your real subnet, security group, and target group identifiers.
- Verify with
aws ecs describe-servicesthatrunningCountreaches2, with target group health showing healthy targets, and with a browser orcurlreturning the page through the load balancer DNS name. - Change the page text, build and push
v2, register a new task definition revision, update the service, and watch ECS replace tasks without dropping below the configured healthy count.
Cleanup: scale the service to zero, delete the service, delete the load balancer and target group, deregister unused task definition revisions if appropriate, delete the ECR repository only after confirming no rollback depends on its images, and remove lab IAM roles and log groups.
Assessment Exercises
- A service references
course-web:prod, and the tag is overwritten after deployment. Explain why two tasks with the same task definition might run different image bytes, and propose a safer release reference. - A Fargate task can pull its image and write logs, but the application receives
AccessDeniedwhen reading an S3 object. Which role should you inspect first, and why? - Your service deployment needs desired count
6,minimumHealthyPercent=100, andmaximumPercent=200. How many tasks might ECS try to run during replacement, and what infrastructure limit could block it? - A task is healthy by container health check but unhealthy in the load balancer target group. List two likely causes and the AWS commands or console views you would use to separate them.
- Design an ECR lifecycle policy for a production service. What images must be retained to support rollback, and what cost pressure does the policy address?
Summary
ECR stores versioned container artifacts, ECS turns task definitions into scheduled tasks and services, and Fargate supplies isolated compute without host management. Reliable container operations come from immutable release references, separated execution and application roles, correct VPC routing, realistic health checks, observable service events, and a cleanup path that does not destroy rollback evidence before it is no longer needed.
