Containers on AWS: ECR, ECS and EKS
Store an image in ECR, run a Fargate task on ECS, and choose EKS only when Kubernetes is a real requirement.
By the end of this lesson you'll be able to
0 of 4 completeThree layers, three responsibilities
ECR stores OCI container images. ECS schedules containers as tasks. Fargate supplies the compute capacity for an ECS task without making you patch or scale EC2 hosts. These are separate responsibilities, so secure and observe each one separately.
ECR: the image boundary
Create one repository per image or per carefully chosen ownership boundary. Enable scan-on-push, use immutable tags for releases, and add a lifecycle policy so old digests do not accumulate forever. A tag is a pointer; a digest identifies the exact image.
The task in this lesson used the public nginx image so the exercise did not need to build or push an image. In a real pipeline, authenticate to ECR, push a content-addressed image, and deploy the digest rather than trusting a mutable latest tag.
ECS task definitions
A task definition is versioned. It declares the image, CPU, memory, ports, logging, network mode and IAM roles. The execution role lets the ECS agent pull an image and write logs. The task role is different: it is the identity available to application code. Keep the two roles separate and grant each only what it needs.
Fargate task and service
A task is one running copy of a task definition. A service is the controller for a desired number of long-running tasks. The mission uses one task and no service, so it is easy to see the lifecycle and clean it up. For a web application, put a service behind a load balancer, use private subnets where possible, and allow inbound traffic only from the load balancer security group.
ECS or EKS?
Choose ECS when AWS-native scheduling and a smaller operational surface fit the team. Choose EKS when Kubernetes compatibility, its ecosystem or an existing platform standard justifies the cluster lifecycle. EKS adds control-plane integration, node or Fargate capacity, IAM mappings, upgrades and more policy surface. Do not select it merely because the word “container” appears in the requirement.
ECS capacity providers
Capacity providers answer a placement question: where should ECS put a task? FARGATE runs on AWS-managed
capacity, FARGATE_SPOT trades interruption risk for a lower price, and EC2 places tasks on instances that your
team patches and scales. Keep interruption-tolerant workers separate from stateful or latency-sensitive services.
Task definitions: the runtime contract
The task definition is the versioned contract between the scheduler and the container. awsvpc gives each task an
elastic network interface and security group. CPU and memory are task-level limits. A port mapping gives the proxy or
load balancer a named destination. A health check lets ECS replace a process that is alive but not serving.
The execution role is used by the ECS agent to pull images and write logs. The task role is assumed by application
code. They are intentionally different. In the practice definitions, the web task has the execution role and the api
task also has academy-ecs-task-role for ECS Exec and application permissions.
The logging choice matters too. awslogs sends each container stream to CloudWatch Logs. FireLens is useful when a
central log router must enrich or route records to another destination. Secrets should come from Secrets Manager or
SSM references, not from a baked image or plain environment variable.
Service discovery and Service Connect
Cloud Map gives services names inside a namespace. Service Connect builds on that registry with managed proxies, client aliases and consistent service-to-service telemetry. Use plain Cloud Map when clients can resolve and call the registered endpoint themselves. Use Service Connect when ECS services need a uniform, named mesh-like connection between tasks without building that proxy layer by hand.
The web container calls http://api:80/; Service Connect maps that alias to the api service. This keeps the web task
independent of task IP addresses.
Put a service behind an ALB
An ECS service keeps the desired number of tasks running. An ALB target group checks the task health and routes the request to a healthy ENI. The ALB security group should accept the listener port; the task security group should accept the container port only from the ALB security group.
The live practice ALB returned real responses:
$ curl http://academy-ecs-alb-981205580.ap-south-1.elb.amazonaws.com/ web ok from ip-172-31-8-246.ap-south-1.compute.internal $ curl http://academy-ecs-alb-981205580.ap-south-1.elb.amazonaws.com/api/ {"service":"api","version":"v1","host":"ip-172-31-13-164.ap-south-1.compute.internal"}
curl http://academy-ecs-alb-981205580.ap-south-1.elb.amazonaws.com/curlmake an HTTP request
Deployments and automatic rollback
Rolling deployment replaces tasks while respecting minimumHealthyPercent and maximumPercent. A deployment
circuit breaker watches whether the new task set can become healthy. With rollback enabled, ECS returns to the last
completed deployment when the new revision cannot start. Blue/green with CodeDeploy is a better fit when two task sets
must coexist and traffic must shift in controlled steps; it adds a second target group and lifecycle hooks.
The practice service was updated to an image tag that does not exist. ECS recorded a real pull failure:
(service api) was unable to place a task. Reason: CannotPullContainerError: pull image manifest has been retried 7 time(s): failed to resolve ref public.ecr.aws/nginx/nginx:does-not-exist: not found.
Scale, inspect and choose the platform
Target tracking keeps a service metric near a target. The practice web service used ECS average CPU at 50 percent, with two to four tasks. Choose a target that protects latency and downstream capacity; scaling is not a substitute for backpressure.
ECS Exec opens a shell through Systems Manager without exposing SSH. It needs the task role's ssmmessages actions,
the Session Manager plugin and a task launched with execute-command enabled. App Runner is a simpler managed web
deployment when you bring a source repository or image and do not need ECS's task and network controls. EKS is the
right boundary when Kubernetes APIs and ecosystem compatibility are requirements; it is not a default for every
container.
Run one disposable Fargate task
Create an ECR repository, define a small nginx task, run it on Fargate, inspect its state, then delete every temporary resource. No image is pushed in this mission; the task pulls a public image.
A RUNNING task followed by confirmed deletion of the task, cluster, repository and temporary execution roleReveal one safe solution
$ export AWS_REGION=ap-south-1 $ export REPO="academy-containers-$RANDOM" $ export CLUSTER="academy-fargate-$RANDOM" $ export FAMILY="academy-nginx-$RANDOM" $ export ROLE="academy-ecs-exec-$RANDOM" $ export VPC=$(aws ec2 describe-vpcs --filters Name=is-default,Values=true --query 'Vpcs[0].VpcId' --output text) $ export SUBNET=$(aws ec2 describe-subnets --filters Name=vpc-id,Values="$VPC" --query 'Subnets[?DefaultForAz==`true`].SubnetId | [0]' --output text) $ export SG=$(aws ec2 describe-security-groups --filters Name=vpc-id,Values="$VPC" --query 'SecurityGroups[?GroupName==`default`].GroupId | [0]' --output text) $ aws ecr create-repository --repository-name "$REPO" --image-scanning-configuration scanOnPush=true $ aws ecs create-cluster --cluster-name "$CLUSTER" --capacity-providers FARGATE $ cat > trust.json <<'JSON' {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]} JSON $ aws iam create-role --role-name "$ROLE" --assume-role-policy-document file://trust.json $ aws iam attach-role-policy --role-name "$ROLE" --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy $ aws ecs register-task-definition --family "$FAMILY" --network-mode awsvpc --requires-compatibilities FARGATE --cpu 256 --memory 512 --execution-role-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/$ROLE" --container-definitions '[{"name":"web","image":"public.ecr.aws/docker/library/nginx:alpine","essential":true,"portMappings":[{"containerPort":80,"protocol":"tcp"}]}]' $ TASK=$(aws ecs run-task --cluster "$CLUSTER" --task-definition "$FAMILY" --launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[$SUBNET],securityGroups=[$SG],assignPublicIp=ENABLED}" --query 'tasks[0].taskArn' --output text) $ aws ecs wait tasks-running --cluster "$CLUSTER" --tasks "$TASK" $ aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$TASK" --query 'tasks[0].{Status:lastStatus,Definition:taskDefinition,Started:startedAt}' --output table ----------------------------------------------- | DescribeTasks | +----------+----------------------------------+ | Status | RUNNING | | Definition | ...:task-definition/academy-nginx | +----------+----------------------------------+ $ aws ecs stop-task --cluster "$CLUSTER" --task "$TASK" $ aws ecs wait tasks-stopped --cluster "$CLUSTER" --tasks "$TASK" $ aws ecs deregister-task-definition --task-definition "$FAMILY":1 $ aws ecs delete-cluster --cluster "$CLUSTER" $ aws ecr delete-repository --repository-name "$REPO" --force $ aws iam detach-role-policy --role-name "$ROLE" --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy $ aws iam delete-role --role-name "$ROLE" $ aws ecs describe-clusters --clusters "$CLUSTER" --query 'clusters' --output json [] $ aws ecr describe-repositories --repository-names "$REPO" An error occurred (RepositoryNotFoundException) when calling the DescribeRepositories operation
aws ecr create-repository --repository-name "$REPO" --image-scanning-configuration scanOnPush=trueawsthe AWS command line
cat > trust.json <<'JSON'catprint a file>write output to a file, replacing it
aws ecs describe-tasks --cluster "$CLUSTER" --tasks "$TASK" --query 'tasks[0].{Status:lastStatus,Definition:taskDefinition,Started:startedAt}' --output table--querypick fields from the JSON response--outputoutput format
The output shape above is from the real practice-account run; account-specific identifiers are intentionally omitted. If a command fails, stop and inspect before repeating it. Confirm the task is STOPPED before deleting its cluster, and confirm both the repository and role are gone.
Try it yourself
0 of 4 steps doneRun each task in your own account or terminal, then tick it off.
Quick check
What does an ECR repository store?
Official reading for this lesson
Or get every quiz answer right and it completes itself.