Module Overview
Estimated Time: 5-6 hours | Difficulty: Advanced | Prerequisites: Compute, Networking, DevOps
- 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
Copy
┌────────────────────────────────────────────────────────────────────────┐
│ 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
Copy
┌────────────────────────────────────────────────────────────────────────┐
│ 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
Copy
{
"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
Copy
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
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
Copy
┌────────────────────────────────────────────────────────────────────────┐
│ 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
Copy
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)
Copy
┌────────────────────────────────────────────────────────────────────────┐
│ 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
Copy
# ECR Repository with security features
resource "aws_ecr_repository" "app" {
name = "my-app"
image_tag_mutability = "IMMUTABLE" # Prevent tag overwriting
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
Copy
# 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
Q1: ECS vs EKS - how do you choose?
Q1: ECS vs EKS - how do you choose?
Choose ECS when:
- Team is new to containers
- Simple orchestration needs
- Deep AWS integration required
- Faster time to production
- Less operational overhead
- Team has Kubernetes experience
- Need multi-cloud portability
- Complex scheduling requirements
- Rich Kubernetes ecosystem needed
- Already have K8s manifests
- ECS: Task definitions, services
- EKS: Pods, deployments, services
- ECS: AWS-native, simpler
- EKS: CNCF standard, portable
Q2: Fargate vs EC2 launch type?
Q2: Fargate vs EC2 launch type?
Fargate:
- No infrastructure management
- Per-task pricing
- Faster scaling
- Better for variable workloads
- Higher per-unit cost
- Full control over instances
- Lower cost at scale
- GPU support
- Spot instances
- Host networking
- Fargate: ~$450/month
- EC2 (m5.xlarge): ~$280/month
- EC2 Spot: ~$120/month
Q3: How do you handle container secrets?
Q3: How do you handle container secrets?
Options:
- Secrets Manager (recommended):
- Rotation support
- Fine-grained IAM
- $0.40/secret/month
- Parameter Store:
- Cost-effective
- Hierarchical
- No rotation
- Task Definition secrets:
Copy
"secrets": [ { "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:..." } ]
- Hardcode in images
- Pass as env vars in task def
Q4: Explain the sidecar pattern in containers.
Q4: Explain the sidecar pattern in containers.
Sidecar pattern:
- Helper container alongside main app
- Share network namespace
- Same lifecycle as main container
- Envoy: Service mesh proxy
- X-Ray daemon: Distributed tracing
- Fluent Bit: Log aggregation
- CloudWatch agent: Metrics
Copy
{
"containerDefinitions": [
{ "name": "app", "essential": true },
{ "name": "envoy", "essential": true },
{ "name": "xray", "essential": false }
]
}
Q5: How do you implement canary deployments in ECS/EKS?
Q5: How do you implement canary deployments in ECS/EKS?
ECS (CodeDeploy):
- Blue/green with weighted target groups
- Canary10Percent5Minutes config
- CloudWatch alarms for rollback
- Native: Multiple deployments with ingress weights
- Flagger: Automated canary with Istio/App Mesh
- Argo Rollouts: Progressive delivery
Copy
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