Skip to main content

Documentation Index

Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt

Use this file to discover all available pages before exploring further.

ECS vs EKS Comparison

Module Overview

Estimated Time: 5-6 hours | Difficulty: Advanced | Prerequisites: Compute, Networking, DevOps
Containers are the foundation of modern cloud-native applications. The core value proposition: a container packages your application AND its dependencies into a single artifact that runs identically on a developer’s laptop, in CI/CD, and in production. No more “works on my machine.” This module provides deep coverage of container orchestration on AWS — specifically, the decision between ECS (AWS’s simpler, native orchestrator) and EKS (managed Kubernetes, the industry standard). The right choice depends less on technical capabilities and more on your team’s existing expertise and portability requirements. What You’ll Learn:
  • ECS architecture and task definitions
  • EKS cluster management and best practices
  • Fargate vs EC2 launch types
  • Service mesh with App Mesh
  • Container security and image management
  • Microservices design patterns

Container Services Overview

┌────────────────────────────────────────────────────────────────────────┐
│                    AWS Container Services                               │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ORCHESTRATION                                                         │
│   ─────────────                                                         │
│                                                                         │
│   ┌─────────────────────────────────────────────────────────────────┐  │
│   │                                                                  │  │
│   │   ECS (Elastic Container Service)   │  EKS (Kubernetes)         │  │
│   │   ───────────────────────────────   │  ─────────────────        │  │
│   │   • AWS-native orchestration        │  • Managed Kubernetes     │  │
│   │   • Simpler, integrated             │  • Portable, CNCF         │  │
│   │   • No cluster management           │  • Rich ecosystem         │  │
│   │   • Task definitions                │  • Pods, Deployments      │  │
│   │                                     │                            │  │
│   └─────────────────────────────────────────────────────────────────┘  │
│                                                                         │
│   COMPUTE (Launch Types)                                                │
│   ──────────────────────                                                │
│                                                                         │
│   ┌──────────────────────┐          ┌──────────────────────┐          │
│   │      EC2 Launch      │          │   Fargate Launch     │          │
│   │      ───────────     │          │   ─────────────      │          │
│   │  • You manage EC2    │          │  • Serverless        │          │
│   │  • Full control      │          │  • No infra mgmt     │          │
│   │  • Spot instances    │          │  • Per-task pricing  │          │
│   │  • GPU support       │          │  • Fargate Spot      │          │
│   │  • Lower cost at     │          │  • Best for:         │          │
│   │    scale             │          │    variable load     │          │
│   └──────────────────────┘          └──────────────────────┘          │
│                                                                         │
│   SUPPORTING SERVICES                                                   │
│   ───────────────────                                                   │
│   • ECR: Container registry                                            │
│   • App Mesh: Service mesh                                             │
│   • Cloud Map: Service discovery                                       │
│   • App Runner: Simplified containers                                  │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

ECS Deep Dive

ECS Architecture

┌────────────────────────────────────────────────────────────────────────┐
│                    ECS Architecture                                     │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌──────────────────────────────────────────────────────────────────┐ │
│   │                         ECS CLUSTER                               │ │
│   │                                                                   │ │
│   │  ┌─────────────────────────────────────────────────────────────┐ │ │
│   │  │                      ECS SERVICE                             │ │ │
│   │  │                                                              │ │ │
│   │  │  Desired Count: 3    │  Load Balancer: ALB                  │ │ │
│   │  │  Min/Max: 2/10       │  Auto Scaling: Target Tracking       │ │ │
│   │  │                                                              │ │ │
│   │  │  ┌────────────┐  ┌────────────┐  ┌────────────┐            │ │ │
│   │  │  │   TASK 1   │  │   TASK 2   │  │   TASK 3   │            │ │ │
│   │  │  │            │  │            │  │            │            │ │ │
│   │  │  │┌──────────┐│  │┌──────────┐│  │┌──────────┐│            │ │ │
│   │  │  ││Container ││  ││Container ││  ││Container ││            │ │ │
│   │  │  ││   App    ││  ││   App    ││  ││   App    ││            │ │ │
│   │  │  │└──────────┘│  │└──────────┘│  │└──────────┘│            │ │ │
│   │  │  │┌──────────┐│  │┌──────────┐│  │┌──────────┐│            │ │ │
│   │  │  ││Container ││  ││Container ││  ││Container ││            │ │ │
│   │  │  ││  Sidecar ││  ││  Sidecar ││  ││  Sidecar ││            │ │ │
│   │  │  │└──────────┘│  │└──────────┘│  │└──────────┘│            │ │ │
│   │  │  └────────────┘  └────────────┘  └────────────┘            │ │ │
│   │  │                                                              │ │ │
│   │  └─────────────────────────────────────────────────────────────┘ │ │
│   │                                                                   │ │
│   │  CAPACITY PROVIDERS:                                              │ │
│   │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │ │
│   │  │   FARGATE    │  │ FARGATE_SPOT │  │     EC2      │           │ │
│   │  │    (70%)     │  │    (20%)     │  │    (10%)     │           │ │
│   │  └──────────────┘  └──────────────┘  └──────────────┘           │ │
│   │                                                                   │ │
│   └──────────────────────────────────────────────────────────────────┘ │
│                                                                         │
│   KEY CONCEPTS:                                                         │
│   • Cluster: Logical grouping of services                              │
│   • Service: Maintains desired count of tasks                          │
│   • Task: Running instance of task definition                          │
│   • Task Definition: Blueprint (containers, resources, IAM)            │
│   • Container: Docker container within a task                          │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

ECS Task Definition

{
  "family": "my-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789:role/ecsTaskRole",
  
  "containerDefinitions": [
    {
      "name": "app",
      "image": "123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 8080,
          "protocol": "tcp"
        }
      ],
      "environment": [
        { "name": "NODE_ENV", "value": "production" }
      ],
      "secrets": [
        {
          "name": "DB_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:db-password"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/my-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "app"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      },
      "dependsOn": [
        {
          "containerName": "envoy",
          "condition": "HEALTHY"
        }
      ]
    },
    {
      "name": "envoy",
      "image": "envoyproxy/envoy:v1.25.0",
      "essential": true,
      "user": "1337",
      "portMappings": [
        { "containerPort": 9901 },
        { "containerPort": 15000 },
        { "containerPort": 15001 }
      ],
      "environment": [
        { "name": "APPMESH_VIRTUAL_NODE_NAME", "value": "mesh/my-mesh/virtualNode/my-app-node" }
      ],
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -s http://localhost:9901/server_info | grep state | grep -q LIVE"],
        "interval": 5,
        "timeout": 2,
        "retries": 3
      }
    },
    {
      "name": "xray-daemon",
      "image": "amazon/aws-xray-daemon",
      "essential": false,
      "cpu": 32,
      "memory": 256,
      "portMappings": [
        { "containerPort": 2000, "protocol": "udp" }
      ]
    }
  ],
  
  "proxyConfiguration": {
    "type": "APPMESH",
    "containerName": "envoy",
    "properties": [
      { "name": "IgnoredUID", "value": "1337" },
      { "name": "ProxyIngressPort", "value": "15000" },
      { "name": "ProxyEgressPort", "value": "15001" },
      { "name": "AppPorts", "value": "8080" },
      { "name": "EgressIgnoredIPs", "value": "169.254.170.2,169.254.169.254" }
    ]
  }
}

ECS Service with Terraform

resource "aws_ecs_cluster" "main" {
  name = "production"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  configuration {
    execute_command_configuration {
      kms_key_id = aws_kms_key.ecs.arn
      logging    = "OVERRIDE"

      log_configuration {
        cloud_watch_log_group_name = aws_cloudwatch_log_group.ecs_exec.name
      }
    }
  }
}

resource "aws_ecs_cluster_capacity_providers" "main" {
  cluster_name = aws_ecs_cluster.main.name

  capacity_providers = ["FARGATE", "FARGATE_SPOT"]

  default_capacity_provider_strategy {
    base              = 1
    weight            = 70
    capacity_provider = "FARGATE"
  }

  default_capacity_provider_strategy {
    weight            = 30
    capacity_provider = "FARGATE_SPOT"
  }
}

resource "aws_ecs_service" "app" {
  name            = "my-app"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 3

  # Deployment configuration
  deployment_minimum_healthy_percent = 100
  deployment_maximum_percent         = 200

  # Circuit breaker: if a deployment fails (tasks keep crashing), ECS
  # automatically rolls back to the previous stable task definition.
  # Without this, a bad deployment can leave your service in a degraded
  # state with tasks endlessly restarting. Always enable this in production.
  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }

  # Capacity provider strategy
  capacity_provider_strategy {
    capacity_provider = "FARGATE"
    weight            = 70
    base              = 1
  }

  capacity_provider_strategy {
    capacity_provider = "FARGATE_SPOT"
    weight            = 30
  }

  # Networking
  network_configuration {
    subnets          = aws_subnet.private[*].id
    security_groups  = [aws_security_group.app.id]
    assign_public_ip = false
  }

  # Load balancer
  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_name   = "app"
    container_port   = 8080
  }

  # Service discovery
  service_registries {
    registry_arn   = aws_service_discovery_service.app.arn
    container_name = "app"
  }

  # Enable ECS Exec for debugging
  enable_execute_command = true

  # Propagate tags from service
  propagate_tags = "SERVICE"

  lifecycle {
    ignore_changes = [desired_count]
  }
}

# Auto Scaling
resource "aws_appautoscaling_target" "ecs" {
  max_capacity       = 20
  min_capacity       = 3
  resource_id        = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.app.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
}

resource "aws_appautoscaling_policy" "cpu" {
  name               = "cpu-scaling"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
    target_value       = 70
    scale_in_cooldown  = 300
    scale_out_cooldown = 60
  }
}

resource "aws_appautoscaling_policy" "requests" {
  name               = "request-scaling"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ALBRequestCountPerTarget"
      resource_label         = "${aws_lb.main.arn_suffix}/${aws_lb_target_group.app.arn_suffix}"
    }
    target_value       = 1000
    scale_in_cooldown  = 300
    scale_out_cooldown = 60
  }
}

EKS Deep Dive

EKS Architecture

┌────────────────────────────────────────────────────────────────────────┐
│                    EKS Architecture                                     │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌─────────────────────────────────────────────────────────────────┐  │
│   │                     AWS MANAGED                                  │  │
│   │   ┌───────────────────────────────────────────────────────────┐ │  │
│   │   │                  EKS Control Plane                         │ │  │
│   │   │                                                            │ │  │
│   │   │  ┌──────────┐  ┌──────────┐  ┌──────────┐               │ │  │
│   │   │  │API Server│  │  etcd    │  │Controller│               │ │  │
│   │   │  │  (HA)    │  │  (HA)    │  │ Manager  │               │ │  │
│   │   │  └──────────┘  └──────────┘  └──────────┘               │ │  │
│   │   │                                                            │ │  │
│   │   └───────────────────────────────────────────────────────────┘ │  │
│   └─────────────────────────────────────────────────────────────────┘  │
│                              │                                          │
│                              │ kubectl / API                            │
│                              ▼                                          │
│   ┌─────────────────────────────────────────────────────────────────┐  │
│   │                    CUSTOMER MANAGED                              │  │
│   │                                                                  │  │
│   │  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐    │  │
│   │  │ Node Group 1   │  │ Node Group 2   │  │ Fargate Profile │    │  │
│   │  │ (On-Demand)    │  │ (Spot)         │  │ (Serverless)    │    │  │
│   │  │                │  │                │  │                 │    │  │
│   │  │ ┌────────────┐ │  │ ┌────────────┐ │  │                 │    │  │
│   │  │ │ Worker Node│ │  │ │ Worker Node│ │  │   ┌─────────┐   │    │  │
│   │  │ │ ┌────────┐ │ │  │ │ ┌────────┐ │ │  │   │Micro-VM │   │    │  │
│   │  │ │ │  Pod   │ │ │  │ │ │  Pod   │ │ │  │   │ (Pod)   │   │    │  │
│   │  │ │ └────────┘ │ │  │ │ └────────┘ │ │  │   └─────────┘   │    │  │
│   │  │ │ ┌────────┐ │ │  │ │ ┌────────┐ │ │  │                 │    │  │
│   │  │ │ │  Pod   │ │ │  │ │ │  Pod   │ │ │  │                 │    │  │
│   │  │ │ └────────┘ │ │  │ │ └────────┘ │ │  │                 │    │  │
│   │  │ └────────────┘ │  │ └────────────┘ │  │                 │    │  │
│   │  └────────────────┘  └────────────────┘  └────────────────┘    │  │
│   │                                                                  │  │
│   │  ADD-ONS:                                                        │  │
│   │  • VPC CNI (networking)       • CoreDNS                         │  │
│   │  • kube-proxy                 • AWS Load Balancer Controller    │  │
│   │  • EBS CSI Driver             • Cluster Autoscaler / Karpenter  │  │
│   │                                                                  │  │
│   └─────────────────────────────────────────────────────────────────┘  │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

EKS with Terraform

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 19.0"

  cluster_name    = "production"
  cluster_version = "1.28"

  vpc_id                   = module.vpc.vpc_id
  subnet_ids               = module.vpc.private_subnets
  control_plane_subnet_ids = module.vpc.private_subnets

  # Public endpoint with private access
  cluster_endpoint_public_access  = true
  cluster_endpoint_private_access = true

  # Add-ons
  cluster_addons = {
    coredns = {
      most_recent = true
    }
    kube-proxy = {
      most_recent = true
    }
    vpc-cni = {
      most_recent              = true
      before_compute           = true
      service_account_role_arn = module.vpc_cni_irsa.iam_role_arn
      configuration_values = jsonencode({
        env = {
          ENABLE_PREFIX_DELEGATION = "true"
          WARM_PREFIX_TARGET       = "1"
        }
      })
    }
    aws-ebs-csi-driver = {
      most_recent              = true
      service_account_role_arn = module.ebs_csi_irsa.iam_role_arn
    }
  }

  # Managed Node Groups
  eks_managed_node_groups = {
    # General purpose (on-demand)
    general = {
      name = "general-ng"

      instance_types = ["m6i.large", "m5.large"]
      capacity_type  = "ON_DEMAND"

      min_size     = 3
      max_size     = 10
      desired_size = 3

      labels = {
        workload = "general"
      }

      taints = []

      update_config = {
        max_unavailable_percentage = 33
      }
    }

    # Spot instances for non-critical
    spot = {
      name = "spot-ng"

      instance_types = ["m6i.large", "m5.large", "m6a.large", "m5a.large"]
      capacity_type  = "SPOT"

      min_size     = 0
      max_size     = 20
      desired_size = 2

      labels = {
        workload = "spot"
      }

      taints = [
        {
          key    = "spot"
          value  = "true"
          effect = "NO_SCHEDULE"
        }
      ]
    }

    # GPU nodes for ML
    gpu = {
      name = "gpu-ng"

      instance_types = ["g4dn.xlarge"]
      capacity_type  = "ON_DEMAND"

      min_size     = 0
      max_size     = 4
      desired_size = 0

      ami_type = "AL2_x86_64_GPU"

      labels = {
        workload = "gpu"
      }

      taints = [
        {
          key    = "nvidia.com/gpu"
          value  = "true"
          effect = "NO_SCHEDULE"
        }
      ]
    }
  }

  # Fargate Profiles
  fargate_profiles = {
    default = {
      name = "default"
      selectors = [
        {
          namespace = "kube-system"
          labels = {
            k8s-app = "kube-dns"
          }
        },
        {
          namespace = "fargate"
        }
      ]
    }
  }

  # IRSA for aws-load-balancer-controller
  enable_irsa = true

  # Cluster access
  manage_aws_auth_configmap = true

  aws_auth_roles = [
    {
      rolearn  = "arn:aws:iam::123456789:role/AdminRole"
      username = "admin"
      groups   = ["system:masters"]
    }
  ]

  aws_auth_users = [
    {
      userarn  = "arn:aws:iam::123456789:user/developer"
      username = "developer"
      groups   = ["system:masters"]
    }
  ]
}

# Install AWS Load Balancer Controller
resource "helm_release" "aws_load_balancer_controller" {
  name       = "aws-load-balancer-controller"
  repository = "https://aws.github.io/eks-charts"
  chart      = "aws-load-balancer-controller"
  namespace  = "kube-system"

  set {
    name  = "clusterName"
    value = module.eks.cluster_name
  }

  set {
    name  = "serviceAccount.create"
    value = "true"
  }

  set {
    name  = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
    value = module.load_balancer_controller_irsa.iam_role_arn
  }
}

Service Mesh (App Mesh)

┌────────────────────────────────────────────────────────────────────────┐
│                    App Mesh Architecture                                │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌─────────────────────────────────────────────────────────────────┐  │
│   │                        APP MESH                                  │  │
│   │                                                                  │  │
│   │                    ┌──────────────────┐                         │  │
│   │                    │  Virtual Gateway  │                         │  │
│   │                    │  (Ingress)        │                         │  │
│   │                    └────────┬─────────┘                         │  │
│   │                             │                                    │  │
│   │            ┌────────────────┼────────────────┐                  │  │
│   │            ▼                ▼                ▼                  │  │
│   │   ┌────────────────┐ ┌────────────────┐ ┌────────────────┐     │  │
│   │   │Virtual Service │ │Virtual Service │ │Virtual Service │     │  │
│   │   │   (frontend)   │ │   (api)        │ │   (database)   │     │  │
│   │   └───────┬────────┘ └───────┬────────┘ └───────┬────────┘     │  │
│   │           │                  │                  │               │  │
│   │           ▼                  ▼                  ▼               │  │
│   │   ┌────────────────┐ ┌────────────────┐ ┌────────────────┐     │  │
│   │   │ Virtual Node   │ │ Virtual Node   │ │ Virtual Node   │     │  │
│   │   │  ┌──────────┐  │ │  ┌──────────┐  │ │  ┌──────────┐  │     │  │
│   │   │  │  Envoy   │  │ │  │  Envoy   │  │ │  │  Envoy   │  │     │  │
│   │   │  └────┬─────┘  │ │  └────┬─────┘  │ │  └────┬─────┘  │     │  │
│   │   │       │        │ │       │        │ │       │        │     │  │
│   │   │  ┌────▼─────┐  │ │  ┌────▼─────┐  │ │  ┌────▼─────┐  │     │  │
│   │   │  │   App    │  │ │  │   App    │  │ │  │   App    │  │     │  │
│   │   │  └──────────┘  │ │  └──────────┘  │ │  └──────────┘  │     │  │
│   │   └────────────────┘ └────────────────┘ └────────────────┘     │  │
│   │                                                                  │  │
│   │   FEATURES:                                                      │  │
│   │   • Traffic routing (canary, blue/green)                        │  │
│   │   • mTLS encryption                                              │  │
│   │   • Retries, timeouts, circuit breakers                         │  │
│   │   • Observability (metrics, tracing)                             │  │
│   │                                                                  │  │
│   └─────────────────────────────────────────────────────────────────┘  │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

Container Security

ECR Security Best Practices

# ECR Repository with security features
resource "aws_ecr_repository" "app" {
  name                 = "my-app"
  # IMMUTABLE tags prevent overwriting -- once you push my-app:v1.2.3,
  # that tag is permanently locked to that image digest.
  # Why this matters: without immutable tags, someone can push a different
  # image as :v1.2.3, and your next ECS deployment quietly picks up
  # untested code. This is a real attack vector (tag confusion).
  image_tag_mutability = "IMMUTABLE"

  # Scan on push checks for OS and language-level CVEs using Amazon Inspector.
  # Cost: first scan per image is free; continuous monitoring is $0.09/image/month.
  image_scanning_configuration {
    scan_on_push = true
  }

  encryption_configuration {
    encryption_type = "KMS"
    kms_key         = aws_kms_key.ecr.arn
  }
}

# Lifecycle policy - keep last 10 images
resource "aws_ecr_lifecycle_policy" "app" {
  repository = aws_ecr_repository.app.name

  policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Keep last 10 tagged images"
        selection = {
          tagStatus     = "tagged"
          tagPrefixList = ["v"]
          countType     = "imageCountMoreThan"
          countNumber   = 10
        }
        action = {
          type = "expire"
        }
      },
      {
        rulePriority = 2
        description  = "Delete untagged images older than 1 day"
        selection = {
          tagStatus   = "untagged"
          countType   = "sinceImagePushed"
          countUnit   = "days"
          countNumber = 1
        }
        action = {
          type = "expire"
        }
      }
    ]
  })
}

# Repository policy - cross-account access
resource "aws_ecr_repository_policy" "app" {
  repository = aws_ecr_repository.app.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AllowCrossAccountPull"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::222222222222:root"
        }
        Action = [
          "ecr:GetDownloadUrlForLayer",
          "ecr:BatchGetImage",
          "ecr:BatchCheckLayerAvailability"
        ]
      }
    ]
  })
}

Dockerfile Best Practices

# Build stage
FROM node:18-alpine AS builder

# Non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# Install dependencies first (cache layer)
COPY package*.json ./
RUN npm ci --only=production

# Copy source
COPY --chown=nodejs:nodejs . .

# Build
RUN npm run build

# Production stage - minimal image
FROM gcr.io/distroless/nodejs18-debian11

WORKDIR /app

# Copy only necessary files
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

# Run as non-root
USER 1001

EXPOSE 8080

CMD ["dist/index.js"]

# Labels for metadata
LABEL org.opencontainers.image.source="https://github.com/org/repo"
LABEL org.opencontainers.image.version="${VERSION}"

🎯 Interview Questions

Choose ECS when:
  • Team is new to containers
  • Simple orchestration needs
  • Deep AWS integration required
  • Faster time to production
  • Less operational overhead
Choose EKS when:
  • Team has Kubernetes experience
  • Need multi-cloud portability
  • Complex scheduling requirements
  • Rich Kubernetes ecosystem needed
  • Already have K8s manifests
Key differences:
  • ECS: Task definitions, services
  • EKS: Pods, deployments, services
  • ECS: AWS-native, simpler
  • EKS: CNCF standard, portable
Fargate:
  • No infrastructure management
  • Per-task pricing
  • Faster scaling
  • Better for variable workloads
  • Higher per-unit cost
EC2:
  • Full control over instances
  • Lower cost at scale
  • GPU support
  • Spot instances
  • Host networking
Cost comparison (10 vCPU, 20GB RAM, 730 hrs):
  • Fargate: ~$450/month
  • EC2 (m5.xlarge): ~$280/month
  • EC2 Spot: ~$120/month
Options:
  1. Secrets Manager (recommended):
    • Rotation support
    • Fine-grained IAM
    • $0.40/secret/month
  2. Parameter Store:
    • Cost-effective
    • Hierarchical
    • No rotation
  3. Task Definition secrets:
    "secrets": [
      {
        "name": "DB_PASSWORD",
        "valueFrom": "arn:aws:secretsmanager:..."
      }
    ]
    
Never:
  • Hardcode in images
  • Pass as env vars in task def
Sidecar pattern:
  • Helper container alongside main app
  • Share network namespace
  • Same lifecycle as main container
Common sidecars:
  • Envoy: Service mesh proxy
  • X-Ray daemon: Distributed tracing
  • Fluent Bit: Log aggregation
  • CloudWatch agent: Metrics
Example task definition:
{
  "containerDefinitions": [
    { "name": "app", "essential": true },
    { "name": "envoy", "essential": true },
    { "name": "xray", "essential": false }
  ]
}
ECS (CodeDeploy):
  • Blue/green with weighted target groups
  • Canary10Percent5Minutes config
  • CloudWatch alarms for rollback
EKS options:
  1. Native: Multiple deployments with ingress weights
  2. Flagger: Automated canary with Istio/App Mesh
  3. Argo Rollouts: Progressive delivery
Example (Argo Rollouts):
spec:
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: {duration: 5m}
        - setWeight: 50
        - pause: {duration: 5m}
        - setWeight: 100

🧪 Hands-On Lab: Deploy Microservices on ECS

1

Create ECR Repositories

Create repositories for frontend, backend, and worker services
2

Build and Push Images

Build Docker images with multi-stage Dockerfiles, push to ECR
3

Create ECS Cluster

Fargate cluster with Container Insights enabled
4

Define Task Definitions

Task definitions with health checks, secrets, and logging
5

Deploy Services

Services with ALB integration and auto-scaling
6

Configure Service Discovery

Cloud Map namespace for inter-service communication

What’s Next?

Case Study: Serverless

Build a complete serverless application

Well-Architected

Review your architecture against best practices