AWS ECS Fargate containers

ECS Fargate task zatrzymany: diagnostyka awarii kontenerów

Naprawa tasków ECS Fargate, które się zatrzymują z Essential container exited, OutOfMemoryError lub CannotPullContainerError. Diagnostyka na podstawie metadanych i logów CloudWatch.

·
Taski ECS ciągle się zatrzymują i restartują. Serwis wygląda na zdrowy w konsoli, ale użytkownicy doświadczają sporadycznych błędów. Ten runbook pokazuje jak zidentyfikować przyczynę i naprawić najczęstsze problemy.

Ten runbook opisuje najczęstsze awarie tasków ECS Fargate. Aby zapewnić bieżące utrzymanie kontenerów, zobacz CloudOps Maintenance. Przykład migracji do ECS znajdziesz w case study: EC2 do ECS z Fargate.

Objaw

Taski są ciągle zatrzymywane i zastępowane. Eventy serwisu ECS pokazują:

# Check stopped task reason
aws ecs describe-tasks \
  --cluster my-cluster \
  --tasks $(aws ecs list-tasks --cluster my-cluster \
    --service-name my-service --desired-status STOPPED \
    --query 'taskArns[0]' --output text) \
  --query 'tasks[0].{reason:stoppedReason,status:lastStatus,stopCode:stopCode}'

# Common outputs:
# "Essential container in task exited"
# "Task stopped by: OutOfMemoryError: Container killed due to memory usage"
# "CannotPullContainerError: pull image manifest has been retried 5 time(s)"

# Check service events for patterns
aws ecs describe-services \
  --cluster my-cluster \
  --services my-service \
  --query 'services[0].events[:10].[createdAt,message]' \
  --output table

Możesz też zaobserwować, że serwis oscyluje między żądaną liczbą tasków a zerem, lub wysoką liczbę zatrzymanych tasków w konsoli ECS.

Przyczyna

Taski ECS Fargate zatrzymują się z trzech głównych powodów:

1. Brak pamięci (OOMKill): Kontener przekracza skonfigurowany twardy limit pamięci. Fargate natychmiast ubija kontener bez graceful shutdown. Jest to częste, gdy limity pamięci są ustawione zbyt blisko rzeczywistego użycia lub gdy aplikacja ma wycieki pamięci.

2. Essential container exited: Główny proces kontenera uległ awarii (niezerowy exit code) lub zakończył się poprawnie (exit code 0, ale task definition oczekuje ciągłego działania). Częstą przyczyną są nieobsłużone wyjątki, brakujące zmienne środowiskowe lub nieudane health checki, które powodują zastąpienie kontenera.

3. CannotPullContainerError: Fargate nie może pobrać obrazu z rejestru. Dzieje się tak, gdy uprawnienia ECR są źle skonfigurowane, tag obrazu nie istnieje lub VPC nie ma NAT Gateway / VPC endpoint dla ECR.

Rozwiązanie

A) Brak pamięci: zwiększ limity:

# Check actual memory usage before the kill
aws logs filter-log-events \
  --log-group-name /ecs/my-service \
  --filter-pattern "memory" \
  --start-time $(date -d '1 hour ago' +%s000) \
  --query 'events[*].message'

# Check Container Insights for peak memory (if enabled)
aws cloudwatch get-metric-statistics \
  --namespace ECS/ContainerInsights \
  --metric-name MemoryUtilized \
  --dimensions Name=ServiceName,Value=my-service \
               Name=ClusterName,Value=my-cluster \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 60 --statistics Maximum \
  --query 'Datapoints | sort_by(@, &Timestamp) | [-5:]'

# Update task definition with higher memory
# Rule of thumb: set hard limit to 1.5x observed peak
aws ecs register-task-definition \
  --cli-input-json file://task-definition.json
# Then update the service to use the new task definition revision
aws ecs update-service \
  --cluster my-cluster \
  --service my-service \
  --task-definition my-service:NEW_REVISION

B) Essential container exited: sprawdź logi i health checki:

# Get the exit code of the stopped container
aws ecs describe-tasks \
  --cluster my-cluster \
  --tasks <TASK_ARN> \
  --query 'tasks[0].containers[*].{name:name,exitCode:exitCode,reason:reason}'

# Check CloudWatch logs for crash reason
aws logs tail /ecs/my-service --since 30m --format short | tail -50

# If health check is killing the container, review the health check config:
# - Ensure the health check endpoint returns 200 within the timeout
# - Increase startPeriod for slow-starting applications (default: 0s)
# - Common fix: set startPeriod to 60-120s for Java/Spring apps

# Check environment variables are properly set
aws ecs describe-task-definition \
  --task-definition my-service \
  --query 'taskDefinition.containerDefinitions[0].environment'

C) CannotPullContainerError: napraw dostęp do obrazu:

# Verify the image exists in ECR
aws ecr describe-images \
  --repository-name my-service \
  --image-ids imageTag=latest \
  --query 'imageDetails[0].imagePushedAt'

# Check task execution role has ECR permissions
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/ecsTaskExecutionRole \
  --action-names ecr:GetDownloadUrlForLayer ecr:BatchGetImage \
  --query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}'

# If using private subnets, verify VPC endpoints or NAT Gateway exist
aws ec2 describe-vpc-endpoints \
  --filters "Name=service-name,Values=com.amazonaws.eu-west-1.ecr.dkr" \
  --query 'VpcEndpoints[*].{VpcId:VpcId,State:State}'

Prewencja: Ustaw soft limit pamięci (memoryReservation) na rzeczywiste oczekiwane zużycie, a hard limit na 1.5x szczytu. Skonfiguruj startPeriod health checka z zapasem dla wolno startujących aplikacji. W produkcji przypinaj tagi obrazów do konkretnych digestów zamiast używać latest.

Walidacja

# 1. Check tasks are running and stable
aws ecs describe-services \
  --cluster my-cluster \
  --services my-service \
  --query 'services[0].{running:runningCount,desired:desiredCount,pending:pendingCount}'
# Expected: running == desired, pending == 0

# 2. Verify no recent task stops
aws ecs list-tasks --cluster my-cluster \
  --service-name my-service --desired-status STOPPED \
  --query 'taskArns | length(@)'
# Expected: 0 or only old tasks

# 3. Check service events for stability (no recent restarts)
aws ecs describe-services \
  --cluster my-cluster \
  --services my-service \
  --query 'services[0].events[:5].message'
# Expected: "has reached a steady state" message

# 4. Verify health check passing
aws elbv2 describe-target-health \
  --target-group-arn <TARGET_GROUP_ARN> \
  --query 'TargetHealthDescriptions[*].{Target:Target.Id,Health:TargetHealth.State}'
# Expected: all targets "healthy"

Jeśli serwis utrzymuje steady state przez 10+ minut bez zatrzymanych tasków, problem jest rozwiązany. Monitoruj alarmy CloudWatch dotyczące spadku liczby tasków przez następne 24 godziny.

Każdy zatrzymany task oznacza degradację usługi dla użytkowników. ECS w ciszy zastępuje wadliwe taski, co maskuje problem: serwis wygląda na „działający", podczas gdy użytkownicy doświadczają sporadycznych błędów 502 i timeoutów. Bez prawidłowych health checków i alertów na zdarzenia zatrzymania tasków, takie awarie mogą trwać dniami, zanim ktokolwiek zauważy wzorzec.

 

Jerzy Kopaczewski

Taski ECS padają na produkcji?

Umów bezpłatną 30-minutową rozmowę. Przejrzymy task definitions i konfigurację health checków, aby ustabilizować Twoje serwisy.