Fatskills
Practice. Master. Repeat.
Study Guide: TECH **AWS ECS vs EKS vs Fargate: Zero-Fluff, Hands-On Study Guide**
Source: https://www.fatskills.com/aws-certified-solutions-architect-associate/chapter/tech-aws-ecs-vs-eks-vs-fargate-zero-fluff-hands-on-study-guide

TECH **AWS ECS vs EKS vs Fargate: Zero-Fluff, Hands-On Study Guide**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~8 min read

AWS ECS vs EKS vs Fargate: Zero-Fluff, Hands-On Study Guide

(For AWS Solutions Architect – Associate & Real-World Deployments)


1. What This Is & Why It Matters

You’re a cloud engineer at a startup migrating a monolithic app to containers. Your CTO asks: "Should we use ECS, EKS, or Fargate? And why?"

Wrong answer: "ECS is simpler, EKS is for Kubernetes, Fargate is serverless." Right answer: "ECS is cheaper for AWS-native workloads, EKS gives us Kubernetes portability, and Fargate removes node management—but costs 20% more. Here’s the cost/ops tradeoff for our 50-container microservice."

Why this matters in production:
- Cost: Fargate saves ops time but costs ~$0.0405/vCPU-hour vs ~$0.0116 for ECS on EC2 (us-east-1, 2024). A 100-container app could cost $2,500/month extra on Fargate.
- Ops overhead: EKS requires Kubernetes expertise (etcd, CNI, kube-proxy). ECS is "AWS-managed Kubernetes-lite." - Vendor lock-in: EKS lets you lift-and-shift to GKE/Azure AKS. ECS locks you into AWS.
- Security: Fargate runs containers in isolated VMs (stronger security). ECS/EKS on EC2 shares kernel risks (container escapes).

Scenario: You inherit a legacy Java app running on EC2. It’s a mess of JARs, cron jobs, and hardcoded IPs. Your goal: Containerize it with zero downtime, minimal ops overhead, and cost under $500/month. Which service do you pick?


2. Core Concepts & Components

Term Definition Production Insight
ECS (Elastic Container Service) AWS-native container orchestration (Docker + AWS APIs). ⚠️ No Kubernetes compatibility. Use if you never plan to leave AWS.
EKS (Elastic Kubernetes Service) Managed Kubernetes control plane (you manage worker nodes). ⚠️ Requires Kubernetes expertise. 2x the cost of ECS (control plane + nodes).
Fargate Serverless container runtime (no EC2 instances to manage). ⚠️ No SSH access to containers. Debugging is harder (use CloudWatch Logs).
Task Definition JSON/YAML file defining container(s), CPU, memory, volumes, networking. ⚠️ Immutable—changing it creates a new revision. Use latest tag in dev, pinned versions in prod.
Service Long-running task(s) with scaling, load balancing, and health checks. ⚠️ Deregistration delay (default 300s) can cause 5xx errors during deployments.
Cluster Logical grouping of ECS/EKS resources (tasks, services, nodes). ⚠️ ECS clusters are free; EKS clusters cost $0.10/hour (us-east-1).
ECS Agent Daemon running on EC2 instances to register with ECS. ⚠️ Must be installed on EC2 instances (AMI: amzn2-ami-ecs-hvm-2.0.20240122-x86_64-ebs).
kubelet Kubernetes agent running on EKS worker nodes. ⚠️ Crashes silently if node runs out of disk space (monitor node_disk_pressure).
VPC CNI EKS networking plugin (assigns Pods IPs from VPC subnets). ⚠️ Max Pods per node = (ENI limit × IPs per ENI) - 1 (e.g., t3.medium = 18 Pods).
IAM Roles for Tasks IAM role attached to ECS tasks (not EC2 instances). ⚠️ Least privilege is critical—tasks often get AdministratorAccess in dev.


3. Step-by-Step Hands-On: Deploy a Private ECS Fargate Service with ALB


Prerequisites

  • AWS account with admin IAM permissions (for simplicity; restrict later).
  • AWS CLI installed (aws --version).
  • Docker installed (docker --version).
  • A sample app (we’ll use a Python Flask app).


Step 1: Build and Push a Docker Image

App code (app.py):


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
return "Hello from ECS Fargate!" if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

Dockerfile:


FROM python:3.9-slim
WORKDIR /app
COPY app.py .
RUN pip install flask CMD ["python", "app.py"]

Build and push to ECR:


# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com

# Create ECR repo
aws ecr create-repository --repository-name flask-app --region us-east-1

# Build, tag, and push
docker build -t flask-app .
docker tag flask-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/flask-app:latest docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/flask-app:latest


Step 2: Create a VPC with Private Subnets

# Create VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=ecs-vpc}]'

# Create private subnets (2 AZs for HA)
aws ec2 create-subnet --vpc-id vpc-12345678 --cidr-block 10.0.1.0/24 --availability-zone us-east-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-subnet-1a}]'
aws ec2 create-subnet --vpc-id vpc-12345678 --cidr-block 10.0.2.0/24 --availability-zone us-east-1b --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-subnet-1b}]'

# Create NAT Gateway (for Fargate to pull images)
aws ec2 create-nat-gateway --subnet-id subnet-12345678 --allocation-id eipalloc-12345678


Step 3: Create an ECS Cluster

aws ecs create-cluster --cluster-name fargate-cluster --capacity-providers FARGATE


Step 4: Define Task Definition (JSON)

task-definition.json:


{
  "family": "flask-app",
  "networkMode": "awsvpc",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "containerDefinitions": [
{
"name": "flask-app",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/flask-app:latest",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/flask-app",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
} ], "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512" }

Register the task:


aws ecs register-task-definition --cli-input-json file://task-definition.json


Step 5: Create an ALB

# Create ALB
aws elbv2 create-load-balancer --name fargate-alb --subnets subnet-12345678 subnet-87654321 --security-groups sg-12345678

# Create target group
aws elbv2 create-target-group --name fargate-tg --protocol HTTP --port 80 --vpc-id vpc-12345678 --target-type ip

# Create listener
aws elbv2 create-listener --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/fargate-alb/1234567890abcdef --protocol HTTP --port 80 --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/fargate-tg/1234567890abcdef


Step 6: Deploy the ECS Service

aws ecs create-service \
  --cluster fargate-cluster \
  --service-name flask-service \
  --task-definition flask-app:1 \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-12345678,subnet-87654321],securityGroups=[sg-12345678],assignPublicIp=DISABLED}" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/fargate-tg/1234567890abcdef,containerName=flask-app,containerPort=80"


Step 7: Verify Deployment

  1. Check ALB DNS:
    bash
    aws elbv2 describe-load-balancers --names fargate-alb --query 'LoadBalancers[0].DNSName' --output text

    Open the DNS name in a browser—you should see "Hello from ECS Fargate!".

  2. Check ECS service:
    bash
    aws ecs describe-services --cluster fargate-cluster --services flask-service

    Look for "runningCount": 2.

  3. Check CloudWatch Logs:
    bash
    aws logs get-log-events --log-group-name /ecs/flask-app --log-stream-name ecs/flask-app/<task-id>


4. ? Production-Ready Best Practices


Security

  • IAM Roles for Tasks: Never use EC2 instance roles for tasks. Create a dedicated role with least privilege.
    bash aws iam create-role --role-name ecsTaskRole --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
  • Secrets: Use AWS Secrets Manager or SSM Parameter Store (not environment variables).
    json "secrets": [
    {
    "name": "DB_PASSWORD",
    "valueFrom": "arn:aws:ssm:us-east-1:123456789012:parameter/db/password"
    } ]
  • Networking: Use private subnets for Fargate tasks. Only ALB should be public.

Cost Optimization

  • Fargate vs EC2: Use Fargate for spiky workloads (e.g., CI/CD runners). Use EC2 for steady-state workloads (e.g., APIs).
  • Spot Instances: For ECS on EC2, use Spot Instances (up to 90% cheaper).
    bash aws ecs create-capacity-provider --name spot-cp --auto-scaling-group-provider autoScalingGroupArn=arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:12345678-1234-1234-1234-123456789012:autoScalingGroupName/ecs-asg,managedScaling={status=ENABLED},managedTerminationProtection=DISABLED
  • Task Sizing: Start with 256 CPU / 512 MB and scale up based on CloudWatch metrics (CPUUtilization, MemoryUtilization).

Reliability & Maintainability

  • Immutable Deployments: Never update a running task. Use task definition revisions and rolling updates.
    bash aws ecs update-service --cluster fargate-cluster --service flask-service --task-definition flask-app:2 --force-new-deployment
  • Health Checks: Configure ALB health checks to avoid sending traffic to unhealthy containers.
    bash aws elbv2 modify-target-group --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/fargate-tg/1234567890abcdef --health-check-path /health --health-check-interval-seconds 30
  • Tagging: Tag all resources (ECS, ALB, VPC) for cost allocation.
    bash aws ecs tag-resource --resource-arn arn:aws:ecs:us-east-1:123456789012:cluster/fargate-cluster --tags key=Environment,value=Production

Observability

  • CloudWatch Alarms: Set up alarms for CPUUtilization > 80% and MemoryUtilization > 90%.
    bash aws cloudwatch put-metric-alarm --alarm-name HighCPU-Fargate --metric-name CPUUtilization --namespace AWS/ECS --statistic Average --period 60 --threshold 80 --comparison-operator GreaterThanThreshold --evaluation-periods 2 --alarm-actions arn:aws:sns:us-east-1:123456789012:Alerts
  • X-Ray: Enable AWS X-Ray for distributed tracing.
    json "logConfiguration": {
    "logDriver": "awslogs",
    "options": {
    "awslogs-group": "/ecs/flask-app",
    "awslogs-region": "us-east-1",
    "awslogs-stream-prefix": "ecs"
    } }, "environment": [
    {
    "name": "AWS_XRAY_DAEMON_ADDRESS",
    "value": "xray-daemon:2000"
    } ]


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Fargate tasks in public subnets Containers exposed to the internet (security risk). Always deploy Fargate tasks in private subnets. Use NAT Gateway for outbound.
No health checks ALB sends traffic to unhealthy containers (5xx errors). Configure ALB health checks (/health endpoint).
Over-provisioning CPU/Memory High costs (Fargate charges per vCPU/memory). Start small (256 CPU / 512 MB) and scale based on CloudWatch metrics.
Missing IAM permissions Tasks fail with CannotPullContainerError. Attach AmazonECSTaskExecutionRolePolicy to the task role.
No rollback strategy Bad deployment breaks production (e.g., wrong image tag). Use task definition revisions and ECS deployment rollback.


6. ? Exam/Certification Focus


Typical Question Patterns

  1. "You need to deploy a containerized app with minimal ops overhead. Which service?"
  2. Answer: Fargate (no EC2 management).
  3. Trap: EKS is overkill unless you need Kubernetes portability.

  4. "Your EKS cluster is failing to schedule Pods. What’s the issue?"

  5. Answer: Check VPC CNI limits (max Pods per node = ENI limit × IPs per ENI - 1).
  6. Trap: "Not enough CPU/memory" is a distractor—EKS fails silently on networking.

  7. "How do you reduce costs for a steady-state ECS workload?"

  8. Answer: Use ECS on EC2 with Spot Instances.
  9. Trap: Fargate is more expensive for long-running workloads.

Key Distinctions

Feature ECS EKS Fargate
Kubernetes Compatible ❌ No ✅ Yes ❌ No (but works with ECS/EKS)
Node Management Manual (EC2) or Fargate Manual (EC2) or Fargate ✅ Serverless
Cost Cheapest (EC2) Most expensive (control plane) ~20% more than ECS on EC2
Use Case AWS-native apps Kubernetes portability Spiky workloads, CI/CD


7. ? Hands-On Challenge

Challenge: Deploy a multi-container ECS Fargate task with: - A Flask app (port 80).
- A Redis sidecar (port 6379).
- The Flask app should connect to Redis at localhost:6379.

Solution:
1. Update task-definition.json:
```json
"containerDefinitions": [
{
"name": "flask-app",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/flask-app:latest",
"portMappings": [{"containerPort": 80, "hostPort": 80}],
"environment": [{"name": "REDIS_HOST", "value": "localhost"}],
"essential": true
},
{
"name": "redis",
"image": "redis:alpine",
"portMappings": [{"containerPort": 6379, "hostPort": 6379}



ADVERTISEMENT