Salta al contenuto principale

Pattern ECS Fargate per la Produzione Che Funzionano Davvero

Indice dei contenuti
Un insieme di pattern ECS Fargate collaudati in produzione che applico a ogni servizio — covering strategie Spot, circuit breaker per i deployment, migrazione ARM64, health check e ottimizzazione dei costi con Aurora Serverless v2.

Ho distribuito e gestito molti servizi containerizzati su ECS Fargate. Nel tempo è emerso un insieme di pattern che applico sistematicamente a ogni nuovo servizio. Questo articolo documenta quei pattern con esempi Terraform, coprendo tutto dalle strategie Fargate Spot ai circuit breaker di deployment e alla migrazione ARM64.

L’Architettura Standard
#

Ogni servizio che distribuisco segue la stessa architettura di alto livello:

flowchart LR
    Internet([Internet / VPC]) --> WAF[WAF\nrate limiting]
    WAF --> ALB[ALB\nHTTPS / TLS 1.3]
    ALB --> Fargate[ECS Fargate\nARM64 Spot+OnDemand]
    Fargate --> Aurora[(Aurora PostgreSQL\nServerless v2)]

    subgraph Security Groups
        WAF
        ALB
        Fargate
        Aurora
    end
Nota

Ogni componente vive nel proprio security group, con il traffico permesso solo dal layer direttamente superiore. L’ALB si trova in subnet private — nessun servizio esposto pubblicamente.

Ogni componente ha il proprio security group, con il traffico che fluisce solo dal layer superiore. L’ALB si trova in subnet private, e Route53 con hosted zone private gestisce il DNS interno.

Strategia Fargate Spot
#

Fargate Spot può ridurre i costi di compute fino al 70%, ma è necessario gestire le interruzioni. L’approccio: usare una strategia di capacity provider con pesi bilanciati tra risparmio e disponibilità.

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

  capacity_providers = ["FARGATE", "FARGATE_SPOT"]

  default_capacity_provider_strategy {
    capacity_provider = "FARGATE_SPOT"
    weight            = var.spot_weight
    base              = 1  # Almeno 1 task su Fargate On-Demand
  }

  default_capacity_provider_strategy {
    capacity_provider = "FARGATE"
    weight            = var.ondemand_weight
  }
}

Il base = 1 su FARGATE garantisce che ci sia sempre almeno un task in esecuzione su capacità On-Demand. Questo è la rete di sicurezza durante le interruzioni Spot.

Per non-produzione uso un rapporto Spot/OnDemand di 4:1. Per produzione lo capovolgo a 1:4, privilegiando la stabilità pur ottenendo qualche risparmio Spot.

Circuit Breaker per i Deployment
#

I circuit breaker di deployment ECS eseguono automaticamente il rollback dei deployment falliti. Combinati con la giusta configurazione degli health check, prevengono che deployment errati abbattano il servizio:

service.tf
resource "aws_ecs_service" "main" {
  name            = var.service_name
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.main.arn
  desired_count   = var.desired_count

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }

  deployment_configuration {
    maximum_percent         = 200
    minimum_healthy_percent = 100
  }
}

maximum_percent = 200 con minimum_healthy_percent = 100 significa che ECS avvierà nuovi task prima di drenare quelli vecchi (rolling deployment). Se i nuovi task falliscono gli health check, il circuit breaker interviene e fa il rollback.

Configurazione degli Health Check
#

Impostare correttamente gli health check è critico. Troppo aggressivi e ottieni falsi positivi; troppo permissivi e i deployment falliti impiegano un’eternità per essere rilevati:

alb.tf
resource "aws_lb_target_group" "main" {
  health_check {
    enabled             = true
    path                = "/health"
    healthy_threshold   = 2
    unhealthy_threshold = 3
    timeout             = 10
    interval            = 30
    matcher             = "200"
  }

  deregistration_delay = 30
}

Alcune note:

  • deregistration_delay = 30 invece dei 300 secondi predefiniti. La maggior parte delle applicazioni può drenare le richieste in-flight in 30 secondi, e il ritardo più breve significa deployment più veloci.
  • healthy_threshold = 2 significa che un task ha bisogno di soli 2 health check riusciti per essere considerato sano (60 secondi con intervallo di 30 secondi).

Migrazione ad ARM64 (Graviton)
#

Le istanze AWS Graviton offrono circa il 20% di migliore price-performance rispetto a x86. Migrare i task ECS Fargate ad ARM64 è semplice se le immagini lo supportano:

Dockerfile
# Dockerfile multi-arch
FROM --platform=$TARGETPLATFORM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Build e push di immagini multi-arch:

terminal
docker buildx create --use
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t $ECR_REPO:latest \
  --push .

Poi aggiorna la task definition:

task-definition.tf
resource "aws_ecs_task_definition" "main" {
  runtime_platform {
    operating_system_family = "LINUX"
    cpu_architecture        = "ARM64"
  }
  # ...
}

La riga chiave è cpu_architecture = "ARM64". Fine. Se la tua immagine Docker è multi-arch, Fargate scarica automaticamente l’architettura giusta.

Auto-Scaling
#

I servizi ECS dovrebbero scalare sia su CPU che su memoria. Uso policy di target tracking:

alarms.tf
resource "aws_appautoscaling_policy" "cpu" {
  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
    target_value       = 70.0
    scale_in_cooldown  = 300
    scale_out_cooldown = 60
  }
}

I cooldown asimmetrici sono importanti: scale_out_cooldown = 60 significa che il servizio reagisce rapidamente ai picchi di carico, mentre scale_in_cooldown = 300 previene lo scale-down prematuro durante traffico a raffiche.

Il Pattern Completo
#

Ecco il pattern completo per un nuovo servizio:

  1. Repository ECR con lifecycle policy (mantieni ultime 10 immagini)
  2. ECS cluster con Container Insights abilitato
  3. Task definition con ARM64, limiti di risorse appropriati, iniezione segreti
  4. Servizio ECS con circuit breaker, strategia Spot, auto-scaling
  5. ALB con HTTPS (TLS 1.3), routing basato su path
  6. WAF con rate limiting e regole AWS managed
  7. Aurora Serverless v2 con scaling appropriato all’ambiente
  8. Route53 record nella hosted zone privata
  9. CloudWatch log group con retention 14 giorni
  10. Security group con modello a tre tier (ALB -> ECS -> Aurora)

Una volta che hai questo come insieme di moduli Terraform, distribuire un nuovo servizio è semplicemente comporre i moduli con variabili specifiche del servizio. L’infrastruttura è coerente, sicura e ottimizzata per i costi in tutti gli ambienti.

Articoli correlati