Amazon ECS and Amazon EKS are the two container orchestrators tested on DVA-C02; AWS Fargate is the serverless compute engine that runs tasks/pods on either one without you managing EC2 instances; Amazon ECR is the private image registry that feeds both. The exam mostly lives in ECS/Fargate territory — as a Developer, you write task definitions, wire IAM roles correctly, and reason about networking and deployment strategy far more often than you administer a Kubernetes control plane.
Nearly every container question on DVA-C02 tests one of four things: (1) do you know the difference between the task role (permissions the running application code uses to call AWS APIs) and the task execution role (permissions the ECS agent uses to pull the image and write logs before your code even starts), (2) can you explain why Fargate tasks are forced into awsvpc networking mode and what that means for security groups and ALB target types, (3) can you pick ECS vs EKS vs Fargate vs EC2 launch type for a described scenario based on operational-overhead and portability signals, and (4) do you understand how a rolling vs blue/green deployment actually behaves during a release.
| Component | What It Is |
|---|---|
| Cluster | A logical grouping of tasks/services (ECS) or the managed Kubernetes control plane plus its nodes (EKS). Not itself compute — just a namespace/boundary. |
| Task Definition | A JSON blueprint (ECS) describing one or more containers: image, CPU/memory, port mappings, environment variables, IAM roles, logging config, networking mode. Immutable once registered — updates create a new revision. |
| Task | A running instance of a task definition — one or more containers scheduled together on the same host/ENI. |
| Service | Maintains a desired count of tasks, handles replacement of failed tasks, integrates with load balancers and Service Auto Scaling, and drives deployments (rolling/blue-green). |
| ECR Repository | A versioned store for container images, private by default, integrated with IAM for push/pull authorization and with ECS/EKS for image pulls. |
| Pod (EKS) | Kubernetes's smallest deployable unit — one or more containers sharing network/storage, the conceptual equivalent of an ECS task. |
Developer builds a Docker image locally or in CI, authenticates to ECR (aws ecr get-login-password), tags and pushes the image to an ECR repository.
A task definition references the ECR image URI, sets CPU/memory, declares the task role and task execution role, port mappings, and a log configuration (typically awslogs driver to CloudWatch Logs).
An ECS service on the Fargate launch type is created referencing the task definition, desired task count, subnets/security groups (awsvpc mode), and optionally an ALB target group (target type ip).
The ECS scheduler asks Fargate for the requested vCPU/memory capacity; Fargate provisions isolated compute per task (no visible EC2 instance), attaches an ENI in your VPC subnet for each task, and the execution role is used to pull the image from ECR and start the container(s).
Each task's private IP (not an instance ID) registers with the ALB target group as an ip-type target; health checks begin; traffic is routed once healthy.
Service Auto Scaling (target tracking on CPU/memory/ALB request count) adjusts desired count; new deployments roll tasks per the configured deployment strategy (rolling by default, blue/green via CodeDeploy if configured).
| DVA-C02 Domain | Where Containers Show Up |
|---|---|
| Domain 1: Development with AWS Services | Writing/updating task definitions, using the ECS/ECR/EKS SDKs and CLI, structuring container-based application code, environment variable and secrets injection |
| Domain 2: Security | Task role vs execution role, least-privilege IAM for containers, security groups per task (awsvpc), ECR repository policies and image scanning |
| Domain 3: Deployment | ECS deployment strategies (rolling, blue/green via CodeDeploy), CodePipeline/CodeBuild integration for container CI/CD, ECR lifecycle policies |
| Domain 4: Troubleshooting & Optimization | Diagnosing task placement/scheduling failures, reading CloudWatch Container Insights and task stopped-reason codes, right-sizing CPU/memory, choosing EC2 vs Fargate for cost/operational tradeoffs |
Containers are one of the most heavily-weighted topic clusters on DVA-C02 — expect several scenario questions built directly around ECS task definitions and IAM roles.
I need to run a containerized application in AWS
Do I need Kubernetes-specific tooling, existing K8s manifests, or multi-cloud/on-prem portability?
Do I want to manage/patch/scale the underlying EC2 instances myself?
Amazon ECR (private, IAM-integrated) — or a public/third-party registry if the workload allows it
Fargate always uses awsvpc mode — each task gets its own ENI and security group. EC2 launch type can use awsvpc, bridge, or host.
ALB (target type ip for Fargate) for HTTP(S), Cloud Map for internal service discovery, Service Auto Scaling with target tracking for elasticity
awsvpc networking mode — one ENI + one security group per taskip for Fargate tasks (not instance)Exam appearance probability: HIGH
The settings and concepts most likely to show up as the crux of a scenario question.
my-app:7)ecr:GetDownloadUrlForLayer, logs:CreateLogStream) — not the task role.AccessDenied calling S3/DynamoDB/SQS from inside the container, suspect a missing task role permission.awsvpc Mode Classic exam trapbridge (Docker's default NAT'd networking, shared with the host) or host (container uses the host's own network namespace directly)awsvpc because there's no persistent, customer-managed EC2 host to share a network namespace with — each task must be independently addressable.awsvpc means security groups are attached per task, not per host — this is what enables genuinely fine-grained, task-level network segmentation.awsvpc networking mode" → because Fargate has no shared EC2 host for tasks to attach to via bridge/host networking; each task needs its own ENI.ip — targets are registered by task private IP, not by EC2 instance IDinstance, using dynamic host port mapping, is also validinstance-type targets are not usable — the ALB must route directly to each task's ENI IP.ip target type for Fargate.ECSServiceAverageCPUUtilization, ECSServiceAverageMemoryUtilization, or ALB ALBRequestCountPerTarget.minimumHealthyPercent and maximumPercent deployment configurationappspec.yml and deployment-group mechanics that drive ECS blue/green.aws ecr get-login-password piped to docker login — issues a short-lived (12-hour) auth tokenlatest or a release tag from being overwritten after push — useful for audit/compliance requirements.awsvpc modekubectl, YAML manifests, Helm) — that portability is EKS's whole value proposition versus ECS's AWS-proprietary API/CLI.Requirement → Keywords → Expected Answer → why every distractor fails.
Amazon ECS (or EKS) on AWS Fargate
| Distractor | Why it's wrong |
|---|---|
| ECS/EKS on EC2 launch type | Requires you to provision, patch, and scale the underlying instances |
| EC2 Auto Scaling Group running Docker directly | No orchestration, and still full instance management overhead |
| AWS Lambda | Viable for short-lived/event-driven workloads, but not for long-running services or workloads needing full container runtime control described in most container scenarios |
Fix the Task Execution Role's IAM permissions (ecr:GetDownloadUrlForLayer, logs:CreateLogStream, etc.)
| Distractor | Why it's wrong |
|---|---|
| Task Role | Governs the app's own AWS API calls at runtime, not image pulls/log delivery, which happen before the app starts |
| ECR repository policy | Could also matter for cross-account access, but the default single-account symptom is almost always an execution-role gap |
| Security group | Controls network reachability, not the identity/authorization used to call ECR or CloudWatch Logs APIs |
Add the missing permission to the Task Role
| Distractor | Why it's wrong |
|---|---|
| Task Execution Role | Only used by the ECS agent/infrastructure before your code runs — never assumed by your application |
| Instance profile role (EC2 launch type) | Legacy pattern predating per-task IAM roles; would over-grant permissions to every task on that instance |
| Resource-based policy on the S3 bucket only | Necessary in cross-account cases but not sufficient on its own if the task role itself lacks the action |
Create the target group with target type "ip"
| Distractor | Why it's wrong |
|---|---|
| Target type "instance" | Requires an EC2 instance ID — Fargate tasks have no backing EC2 instance to register |
| Target type "lambda" | For Lambda function targets, unrelated to ECS tasks |
| Classic Load Balancer | Legacy load balancer type, not the modern recommended choice and doesn't resolve the target-type issue |
ECS blue/green deployment via AWS CodeDeploy
| Distractor | Why it's wrong |
|---|---|
| ECS rolling deployment | No traffic-shifting control and no automatic rollback tied to CloudWatch alarms |
| Manually swap task definition revisions | Fully manual, slow, and error-prone rollback |
| Route 53 weighted routing between two clusters | Possible DIY approach, but far higher operational overhead than the native CodeDeploy integration |
AWS Cloud Map (ECS Service Discovery / Service Connect)
| Distractor | Why it's wrong |
|---|---|
| Internal ALB | Works but is higher overhead/cost than DNS-based discovery for simple internal calls, and isn't the most direct native answer |
| Hardcoded IP addresses | Breaks immediately on task replacement — IPs are ephemeral per task |
| Route 53 public hosted zone | Publicly exposes the service unnecessarily |
Configure an ECR lifecycle policy
| Distractor | Why it's wrong |
|---|---|
| Custom Lambda function on a schedule | Reinvents a feature ECR already provides natively — more operational overhead than necessary |
| Manually delete images periodically | Doesn't scale, error-prone, not automated |
| Switch to a public registry | Unrelated to lifecycle management and changes the security posture unnecessarily |
Amazon EKS
| Distractor | Why it's wrong |
|---|---|
| Amazon ECS | AWS-proprietary API — existing Kubernetes manifests, Helm charts, and operational tooling wouldn't carry over directly |
| AWS Fargate (standalone) | Fargate is a compute engine, not an orchestrator — it still needs ECS or EKS on top |
| Elastic Beanstalk with Docker platform | Simplifies deployment but doesn't provide Kubernetes-native tooling/portability |
ip for Fargate) ↔ ECS service, health checks drive task replacementawslogs driver, CPU/memory metrics, Container Insights for cluster/service/task-level dashboardssecrets fieldbuildspec.yml and pushes it to an ECR repository (tagged with the commit SHA).awsvpc mode with its own ENI in a private subnet; the ALB in public subnets is the only internet-facing component; a tightly scoped security group allows only ALB→task traffic on the application port.awslogs driver; an X-Ray daemon sidecar container captures traces; Container Insights dashboards show per-service CPU/memory; alarms on ALB 5xx rate and task count feed an SNS topic for on-call alerting.See 08 — Cross-Service Architectures Guide, Architecture 1, for the fully diagrammed version of this pattern.
| Dimension | Amazon ECS | Amazon EKS |
|---|---|---|
| Orchestration model | AWS-proprietary, simpler API/CLI | Standard Kubernetes API — kubectl, manifests, Helm |
| Operational overhead | Lower — no control plane to think about, tightly integrated with AWS services | Higher — even though AWS manages the control plane, you still operate within Kubernetes concepts/add-ons |
| Portability | AWS-only | Kubernetes is portable across clouds/on-prem — a major reason teams choose it |
| Ecosystem | Smaller, AWS-curated | Massive open-source Kubernetes ecosystem (Helm charts, operators, service meshes) |
| Compute options | EC2 launch type or Fargate | Self-managed EC2, EKS managed node groups, or Fargate profiles |
| Best fit | Teams that want simplicity and are all-in on AWS | Teams with existing Kubernetes investment, multi-cloud strategy, or needing the K8s ecosystem |
This is one of the most common phrasing traps: ECS is the orchestrator (what runs where, service management, deployments). Fargate is a compute engine (where the container actually executes). You always use ECS with either the EC2 launch type or Fargate — "ECS vs Fargate" as a real either/or choice is a category error, though exam questions sometimes phrase it loosely as shorthand for "ECS-on-EC2 vs ECS-on-Fargate."
| Dimension | ECS (orchestrator, always present) | Fargate (one of two compute options) |
|---|---|---|
| Role | Decides task placement, scaling, deployments, service health | Provides the actual compute a task runs on, serverlessly |
| You manage | Task definitions, services, scaling policies | Nothing at the instance/OS level |
| Alternative compute | N/A — ECS always needs a launch type | The alternative is the EC2 launch type, not "no ECS" |
| Dimension | AWS Fargate | EC2 Launch Type |
|---|---|---|
| Server management | None — AWS manages the compute entirely | You provision, patch, and scale the EC2 instances (or use Capacity Providers to automate some of this) |
| Billing granularity | Per-task vCPU/memory-second actually consumed | Per EC2 instance-hour, regardless of how tightly tasks are packed |
| Bin packing / density | No control — one task gets its own isolated compute | You can pack multiple tasks onto one instance for higher density/lower cost at scale |
| Networking mode | awsvpc only | awsvpc, bridge, or host |
| Specialized hardware | Limited (no GPU support in standard Fargate, no custom AMIs) | Full flexibility — GPU instances, custom AMIs, specific instance families |
| Best fit | Variable/unpredictable load, teams wanting minimal ops burden, per-task isolation requirements | Steady, high, predictable load where instance-level bin packing meaningfully reduces cost, or workloads needing GPUs/custom AMIs |
| Misconception | Reality |
|---|---|
| "The task role and task execution role are the same thing, or interchangeable" | They serve completely different purposes: task role = app runtime permissions; execution role = agent-level permissions (image pull, logging, secrets fetch) used before the app starts |
| "Fargate tasks can use bridge networking mode like EC2 tasks" | Fargate mandates awsvpc — there's no shared host to bridge onto |
| "An ALB target group for ECS is always type 'instance'" | Only true for EC2 launch type with bridge/host networking; Fargate requires target type ip |
| "ECS and Fargate are two alternative orchestrators" | Fargate isn't an orchestrator at all — it's a compute engine that ECS (or EKS) schedules tasks/pods onto |
| "EKS should be the default choice because Kubernetes is more powerful" | "More powerful" often means "more operational overhead" — if there's no existing Kubernetes requirement, ECS is usually the lower-overhead, exam-preferred answer |
| "Updating a task definition updates the running tasks in place" | Task definitions are immutable; an update creates a new revision, and the service must be redeployed against it for running tasks to change |
| "ECR image scanning happens automatically on a schedule for every image" | Basic scanning runs once on push unless you explicitly configure enhanced/continuous scanning (powered by Amazon Inspector) |
awsvpc.Three self-contained exercises using AWS CLI v2 in the eu-west-1 region. Exercise 1 builds the full path from a Docker image to a Fargate service behind an ALB. Exercise 2 deliberately breaks the task execution role to reproduce and diagnose a CannotPullContainerError — the hands-on version of the task-role-vs-execution-role trap covered in Components and Best Practices. Exercise 3 is a short EKS/Fargate comparison, kept brief since EKS is tested at the conceptual level on DVA-C02. Replace placeholder values (account ID, VPC/subnet/security-group IDs) with your own before running.
This is the exact request-to-container flow described in the Overview tab's decision flow diagram. Running it once by hand makes the task-definition fields, awsvpc networking, and ip target-type requirement concrete instead of memorized facts.
aws ecr create-repository \ --repository-name demo-web-app \ --region eu-west-1 \ --image-scanning-configuration scanOnPush=truethe command returns a
repository object whose repositoryUri looks like 123456789012.dkr.ecr.eu-west-1.amazonaws.com/demo-web-app
nginx-based image serving a static page on port 80).
docker build -t demo-web-app:v1 .
docker images lists demo-web-app:v1
123456789012 with your account ID).
aws ecr get-login-password --region eu-west-1 \ | docker login --username AWS --password-stdin 123456789012.dkr.ecr.eu-west-1.amazonaws.com docker tag demo-web-app:v1 123456789012.dkr.ecr.eu-west-1.amazonaws.com/demo-web-app:v1 docker push 123456789012.dkr.ecr.eu-west-1.amazonaws.com/demo-web-app:v1the pushed digest appears when you run
aws ecr describe-images --repository-name demo-web-app --region eu-west-1
The get-login-password token is valid for 12 hours — this is the same mechanism CI/CD pipelines use, just invoked manually here.
cat > ecs-trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role \
--role-name demoWebAppExecutionRole \
--assume-role-policy-document file://ecs-trust-policy.json
aws iam attach-role-policy \
--role-name demoWebAppExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
aws iam list-attached-role-policies --role-name demoWebAppExecutionRole shows AmazonECSTaskExecutionRolePolicy attached
cat > task-def.json <<'EOF'
{
"family": "demo-web-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::123456789012:role/demoWebAppExecutionRole",
"containerDefinitions": [{
"name": "demo-web-app",
"image": "123456789012.dkr.ecr.eu-west-1.amazonaws.com/demo-web-app:v1",
"portMappings": [{ "containerPort": 80, "protocol": "tcp" }],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/demo-web-app",
"awslogs-region": "eu-west-1",
"awslogs-stream-prefix": "demo"
}
}
}]
}
EOF
aws ecs register-task-definition \
--cli-input-json file://task-def.json \
--region eu-west-1
response includes "taskDefinitionArn": ".../demo-web-app:1"
ip in your VPC (replace the vpc-id).
aws ecs create-cluster --cluster-name demo-cluster --region eu-west-1 aws elbv2 create-target-group \ --name demo-web-app-tg \ --protocol HTTP --port 80 \ --vpc-id vpc-0123456789abcdef0 \ --target-type ip \ --health-check-path / \ --region eu-west-1target group ARN is returned; note it for the next steps — target type
ip is the only valid option because Fargate tasks have no backing EC2 instance
aws elbv2 create-load-balancer \
--name demo-web-app-alb \
--subnets subnet-aaa111 subnet-bbb222 \
--security-groups sg-alb0001 \
--scheme internet-facing --type application \
--region eu-west-1
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:eu-west-1:123456789012:loadbalancer/app/demo-web-app-alb/abc123 \
--protocol HTTP --port 80 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:eu-west-1:123456789012:targetgroup/demo-web-app-tg/def456 \
--region eu-west-1
aws ecs create-service \
--cluster demo-cluster \
--service-name demo-web-app-svc \
--task-definition demo-web-app:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-aaa111,subnet-bbb222],securityGroups=[sg-tasks0001],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:eu-west-1:123456789012:targetgroup/demo-web-app-tg/def456,containerName=demo-web-app,containerPort=80" \
--region eu-west-1
aws ecs describe-services --cluster demo-cluster --services demo-web-app-svc --region eu-west-1 shows runningCount reach desiredCount, and aws elbv2 describe-target-health shows both targets healthy
curl -I http://$(aws elbv2 describe-load-balancers \ --names demo-web-app-alb --region eu-west-1 \ --query "LoadBalancers[0].DNSName" --output text)
HTTP/1.1 200 OK from your container, served through the ALB → target group (ip) → Fargate task path
The Components tab flags task role vs task execution role as a classic exam trap. Deliberately breaking it here turns "the execution role pulls the image" from a memorized sentence into something you've watched fail and repaired — the symptom (CannotPullContainerError, tasks stuck cycling PROVISIONING→STOPPED) is a very common Domain 4 troubleshooting scenario.
aws iam detach-role-policy \ --role-name demoWebAppExecutionRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
aws iam list-attached-role-policies --role-name demoWebAppExecutionRole now returns an empty AttachedPolicies list
aws ecs update-service \ --cluster demo-cluster \ --service demo-web-app-svc \ --force-new-deployment \ --region eu-west-1the service's deployment stays stuck below
desiredCount; new tasks appear briefly then disappear from RUNNING status
aws ecs list-tasks \
--cluster demo-cluster \
--desired-status STOPPED \
--region eu-west-1
aws ecs describe-tasks \
--cluster demo-cluster \
--tasks <task-arn-from-previous-command> \
--region eu-west-1 \
--query "tasks[0].{stoppedReason:stoppedReason,container:containers[0].reason}"
stoppedReason contains something like CannotPullContainerError: ... 403: Forbidden or a ResourceInitializationError — the image pull failed before your application code ever ran
executionRoleArn in task-def.json.
you can point at the exact task-definition field (executionRoleArn) responsible, not taskRoleArn
aws iam attach-role-policy \ --role-name demoWebAppExecutionRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy aws ecs update-service \ --cluster demo-cluster \ --service demo-web-app-svc \ --force-new-deployment \ --region eu-west-1within a few minutes,
runningCount returns to desiredCount and aws elbv2 describe-target-health shows targets healthy again
DVA-C02 tests EKS mostly at the "know when to choose it and that Fargate can back it" level, not administrator depth — so this exercise stays short. The point is to see that the same image from Exercise 1, on the same account and region, ends up behind kubectl/Kubernetes manifests instead of ECS's own API, with no EC2 nodes to manage either way.
eksctl.
eksctl create cluster \ --name demo-eks \ --region eu-west-1 \ --fargatecluster creation completes (10-15 minutes) and
kubectl get nodes shows no visible EC2 nodes — pods run on AWS-managed Fargate compute instead
eksctl create fargateprofile \ --cluster demo-eks \ --region eu-west-1 \ --name demo-web-app-profile \ --namespace demo-web-app
eksctl get fargateprofile --cluster demo-eks --region eu-west-1 lists the new profile
kubectl create namespace demo-web-app
cat > deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-web-app
namespace: demo-web-app
spec:
replicas: 2
selector:
matchLabels: { app: demo-web-app }
template:
metadata:
labels: { app: demo-web-app }
spec:
containers:
- name: demo-web-app
image: 123456789012.dkr.ecr.eu-west-1.amazonaws.com/demo-web-app:v1
ports:
- containerPort: 80
EOF
kubectl apply -f deployment.yaml
kubectl get pods -n demo-web-app shows 2 pods in Running state, each scheduled onto Fargate (no node name resembling an EC2 instance)
Notice what stayed identical to Exercise 1: the ECR image URI and region. What changed: the API surface (Kubernetes manifests + kubectl instead of task definitions + aws ecs), and per-pod IAM would use IRSA rather than a task execution role.
Click card to flip. Mark right or wrong to track score.
DVA-C02 scenario style, Easy → Specialty. Select an answer to reveal the explanation.