iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

ECS / Fargate

Amazon ECS runs containers without you managing Kubernetes. Pick Fargate for serverless compute, EC2 for control. Tasks and Services map cleanly to “pod” and “deployment”, with auto-scaling, load balancing, and CloudWatch built in.

Fargate vs EC2, task def, service

EXAMPLE
// 1) The pieces
// • Cluster        — logical grouping (often one per env: dev, staging, prod)
// • Task definition — blueprint: containers, CPU, RAM, networking, secrets, IAM role
// • Task           — running instance of a task definition
// • Service        — keeps N tasks running; handles deploys, scaling, ALB integration
// • Capacity       — Fargate (serverless) OR EC2 (you manage nodes)

// 2) Fargate vs EC2
// Fargate:
//   ✓ Zero infra to manage
//   ✓ Per-task CPU + RAM granularity
//   ✓ Auto-patched
//   ✗ ~30% more expensive
//   ✗ Less flexibility (no daemonsets, GPUs limited, no privileged mode)
//
// EC2:
//   ✓ Cheaper at scale
//   ✓ Spot instances
//   ✓ Custom AMIs, GPU types, host volumes
//   ✗ You patch + scale nodes
//
// New projects: start Fargate. Optimise later if cost matters.

// 3) Task definition (JSON)
{
    "family": "my-api",
    "networkMode": "awsvpc",
    "requiresCompatibilities": ["FARGATE"],
    "cpu":    "512",
    "memory": "1024",
    "executionRoleArn": "arn:aws:iam::123:role/ecsTaskExecutionRole",
    "taskRoleArn":      "arn:aws:iam::123:role/myAppTaskRole",
    "containerDefinitions": [
        {
            "name": "api",
            "image": "123.dkr.ecr.ap-southeast-2.amazonaws.com/my-api:1.2.3",
            "essential": true,
            "portMappings": [{ "containerPort": 3000, "protocol": "tcp" }],
            "environment": [
                { "name": "NODE_ENV", "value": "production" }
            ],
            "secrets": [
                { "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:ap-southeast-2:123:secret:db-AbCdEf" }
            ],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group":         "/ecs/my-api",
                    "awslogs-region":        "ap-southeast-2",
                    "awslogs-stream-prefix": "api"
                }
            },
            "healthCheck": {
                "command": ["CMD-SHELL", "curl -fsS http://localhost:3000/healthz || exit 1"],
                "interval": 30, "timeout": 5, "retries": 3, "startPeriod": 10
            }
        }
    ]
}

// 4) executionRole vs taskRole
// executionRoleArn — what ECS uses to PULL the image + read Secrets Manager + write logs
// taskRoleArn      — what your APP CODE uses (S3 access, DynamoDB, etc.)
//
// Keep them SEPARATE. App role gets least privilege.

// 5) Service definition
{
    "serviceName":   "my-api",
    "cluster":       "prod",
    "taskDefinition": "my-api:42",
    "desiredCount":   3,
    "launchType":    "FARGATE",
    "networkConfiguration": {
        "awsvpcConfiguration": {
            "subnets":        ["subnet-a", "subnet-b", "subnet-c"],
            "securityGroups": ["sg-app"],
            "assignPublicIp": "DISABLED"
        }
    },
    "loadBalancers": [{
        "targetGroupArn": "arn:aws:elasticloadbalancing:...:targetgroup/my-api-tg/...",
        "containerName":  "api",
        "containerPort":  3000
    }],
    "deploymentConfiguration": {
        "maximumPercent":        200,
        "minimumHealthyPercent": 100,
        "deploymentCircuitBreaker": { "enable": true, "rollback": true }
    },
    "healthCheckGracePeriodSeconds": 60
}

// 6) Rolling deploy semantics
// • minHealthy 100 + maxPercent 200 → 3 running, spin up 3 new, drain 3 old
// • For tight budgets: min 50% / max 100% → trades cost for slower deploys
// • Circuit breaker rolls back automatically if new tasks fail health checks

// 7) Auto-scaling
// Application Auto Scaling target tracking:
//   • TargetTrackingMetric: ECSServiceAverageCPUUtilization
//   • Target value: 60
//   • Min: 3, Max: 50
//
// Or step scaling on SQS queue depth, ALB request count, custom CloudWatch metric.

// 8) Secrets + config
// • Secrets Manager — rotating creds; reference in task def 'secrets'
// • SSM Parameter Store — cheaper for non-rotating values
// • environment — plain env vars
// • App reads from process.env regardless

// 9) Networking
// • awsvpc mode = each task gets its OWN ENI (Elastic Network Interface)
// • Internal services → private subnets + ALB
// • Public APIs → ALB in public subnets, tasks in private
// • Service Connect (newer) = service-to-service mesh without ALB per service

// 10) Logging + tracing
// • CloudWatch Logs by default (awslogs driver)
// • For high-volume → FireLens with Fluent Bit
//     - Ship to OpenSearch, Datadog, Splunk
//     - Sidecar pattern
// • AWS X-Ray for distributed tracing — sidecar daemon or SDK

// 11) Deploying — common workflows
// • Build image in CI → push to ECR
// • Update task definition (new image tag)
// • Update service to new task def
// • CodeDeploy for blue/green; built-in for canary 10% → 50% → 100%
// • GitHub Actions: aws-actions/amazon-ecs-deploy-task-definition

// 12) Cost levers
// • Fargate Spot — up to 70% off; for fault-tolerant workloads
// • Right-size CPU + RAM (CloudWatch metrics)
// • Compute Savings Plans (1- or 3-year commitment)
// • Schedule non-prod down on weekends (Lambda + EventBridge)

// 13) ECS vs EKS vs App Runner
// • App Runner — simplest, opinionated; tradeoff of control
// • ECS         — sweet spot for most AWS-only shops
// • EKS         — when you need Kubernetes ecosystem (operators, Helm, GitOps)

// 14) Local dev — Copilot CLI or docker compose
copilot init --app my-app --svc api --svc-type 'Load Balanced Web Service' --dockerfile ./Dockerfile
copilot env init --name prod
copilot deploy --name api --env prod

// 15) Common bugs
// • Health check port mismatch → ALB marks unhealthy; deploy stalls
// • Wrong subnets (public vs private) → tasks can't pull images or call APIs
// • Security group missing egress → tasks can't reach AWS APIs
// • Forgot to attach executionRoleArn → 'unable to pull image'
// • Container without health check → bad versions get traffic
// • Task def updated but service NOT updated → still running old version
// • Spot tasks dropped mid-request → enable graceful shutdown signal handling
// • Logging driver missing → stdout lost; always include awslogs config
// • Min healthy % too aggressive on small services → downtime during deploy
// • Capacity provider only has Spot → all capacity gone in spike; mix with On-Demand

Why it matters

ECS Fargate runs containers without the K8s overhead: task definition + service + ALB target group is the whole story. Split execution role (ECS pulls images, reads secrets) from task role (app code), enable deployment circuit breaker for safe rollbacks, plug into Application Auto Scaling for CPU/SQS-based scaling, and ship logs via FireLens when CloudWatch Logs gets expensive.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Run containers without managing VMs (Fargate launch type).
Try it Yourself »

Discussion

Loading…