AWS ALB 502 Bad Gateway: wszystkie cele nie przechodzą weryfikacji stanu. Jak to naprawić
Diagnoza i naprawa błędów ALB 502 Bad Gateway spowodowanych awariami weryfikacji stanu target group - security groups, ścieżki, limity czasu i draining przy wdrożeniach.
unhealthy (trwałe, całkowite 502 - security groups, ścieżka weryfikacji stanu, limity czasu lub wyrejestrowanie celów podczas wdrożenia), albo cele pozostają zdrowe, a mimo to część żądań zwraca 502 sporadycznie pod obciążeniem (wyścig na limicie bezczynności keep-alive). Ten runbook opisuje oba przypadki. Zacznij od sprawdzenia stanu celów; jeśli cele są zdrowe, przejdź do Kroku 5.
Objawy
ALB zwraca HTTP 502 na wszystkie żądania. Weryfikacje stanu target group pokazują wszystkie zarejestrowane cele jako unhealthy:
# Sprawdź status zdrowia celów
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:eu-central-1:123456789012:targetgroup/my-app/abc123
# Wynik pokazuje wszystkie cele jako unhealthy:
# {
# "TargetHealthDescriptions": [
# {
# "Target": { "Id": "10.0.1.42", "Port": 8080 },
# "TargetHealth": {
# "State": "unhealthy",
# "Reason": "Target.ResponseCodeMismatch",
# "Description": "Health checks failed with these codes: [503]"
# }
# }
# ]
# }
# CloudWatch pokazuje skok ELB 5xx
aws cloudwatch get-metric-statistics \
--namespace AWS/ApplicationELB \
--metric-name HTTPCode_ELB_502_Count \
--dimensions Name=LoadBalancer,Value=app/my-alb/abc123 \
--start-time 2026-07-07T08:00:00Z \
--end-time 2026-07-07T09:00:00Z \
--period 60 \
--statistics Sum
Sama aplikacja może działać prawidłowo - problem polega na tym, że ALB nie może potwierdzić zdrowia celów i usuwa je wszystkie z rotacji.
Przyczyna
ALB wymaga co najmniej jednego zdrowego celu, by przekazywać ruch. Gdy wszystkie cele nie przechodzą weryfikacji stanu, zwraca 502. Cztery najczęstsze przyczyny:
- Błędna konfiguracja security groups. Security group ALB może dotrzeć do celów na porcie weryfikacji stanu, ale security group celu nie zezwala na ruch przychodzący z SG ALB-a. Ruch z ALB jest blokowany zanim dotrze do aplikacji.
- Ścieżka weryfikacji stanu zwraca kod inny niż 200. Skonfigurowana ścieżka (
/health,/lub inna) zwraca 4xx lub 5xx. Dzieje się tak gdy aplikacja wymaga uwierzytelnienia na endpoincie weryfikacji stanu, gdy ścieżka nie istnieje lub gdy aplikacja nie zakończyła startu. - Zbyt krótki timeout weryfikacji stanu. Timeout jest krótszy niż czas odpowiedzi aplikacji pod obciążeniem. ALB oznacza cele jako unhealthy bo odpowiadają zbyt wolno, nie dlatego że nie działają.
- Wyrejestrowanie celów podczas wdrożeń. ECS lub ASG wycofuje stare cele zanim nowe przejdą weryfikacje stanu. Jeśli
deregistration_delayjest zbyt krótki lub czas startu nowego zadania zbyt długi, pojawia się okno w którym nie istnieje żaden zdrowy target. - Wyścig na limicie bezczynności keep-alive (cele pozostają zdrowe). Jedyna przyczyna, która przetrwa każdą kontrolę “przecież target jest zdrowy”. ALB ponownie wykorzystuje połączenia do backendu; jeśli limit keep-alive po stronie celu jest krótszy lub równy limitowi bezczynności ALB (domyślnie 60s), target może zamknąć połączenie z puli dokładnie w momencie, gdy ALB wysyła na nim kolejne żądanie, i ALB zwraca 502. Daje to sporadyczne 502 skorelowane z obciążeniem, a nie z wdrożeniami czy stanem celu. Naprawa w Kroku 5.
Rozwiązanie
Krok 1: Zweryfikuj łączność security groups
ALB musi mieć możliwość dotarcia do celów na porcie weryfikacji stanu. Sprawdź obie security groups:
# Znajdź security group ALB
aws elbv2 describe-load-balancers \
--names my-alb \
--query 'LoadBalancers[0].SecurityGroups'
# Znajdź security group celów (zadania ECS lub instancje EC2)
aws ec2 describe-security-groups \
--group-ids sg-target123 \
--query 'SecurityGroups[0].IpPermissions'
SG celu musi zezwalać na ruch przychodzący z SG ALB-a na porcie celu:
# Terraform: prawidłowe reguły security group
resource "aws_security_group" "alb" {
name = "alb-sg"
vpc_id = var.vpc_id
egress {
from_port = var.container_port
to_port = var.container_port
protocol = "tcp"
security_groups = [aws_security_group.ecs_tasks.id]
}
}
resource "aws_security_group" "ecs_tasks" {
name = "ecs-tasks-sg"
vpc_id = var.vpc_id
ingress {
from_port = var.container_port
to_port = var.container_port
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
}
Krok 2: Napraw ścieżkę weryfikacji stanu i oczekiwane kody
Upewnij się, że ścieżka weryfikacji stanu istnieje i zwraca 200 bez uwierzytelnienia:
# Przetestuj endpoint weryfikacji stanu bezpośrednio z bastiona lub z wnętrza VPC
curl -v http://10.0.1.42:8080/health
# Musi zwrócić HTTP 200 z ciałem odpowiedzi
Jeśli aplikacja zwraca 204 lub inne kody sukcesu, zaktualizuj matcher:
resource "aws_lb_target_group" "app" {
name = "my-app-tg"
port = 8080
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
enabled = true
path = "/health"
port = "traffic-port"
protocol = "HTTP"
matcher = "200-299"
interval = 30
timeout = 10
healthy_threshold = 2
unhealthy_threshold = 3
}
}
Krok 3: Dostosuj limity czasu dla wolno startujących aplikacji
Jeśli aplikacja potrzebuje czasu na start (JVM, rozbudowane dependency injection):
resource "aws_lb_target_group" "app" {
# ...
health_check {
path = "/health"
interval = 30
timeout = 15
healthy_threshold = 2
unhealthy_threshold = 5 # Pozwól na więcej niepowodzeń podczas startu
}
}
Dla ECS skonfiguruj również grace period weryfikacji stanu kontenera:
resource "aws_ecs_service" "app" {
name = "my-app"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 2
health_check_grace_period_seconds = 120 # Daj kontenerom czas na start
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "app"
container_port = 8080
}
}
Krok 4: Zapobiegaj 502 podczas wdrożeń
Ustaw deregistration delay dłuższy niż czas odpływu połączeń i zapewnij, że minimum healthy percent utrzymuje stare cele przy życiu:
resource "aws_lb_target_group" "app" {
# ...
deregistration_delay = 120 # Utrzymuj odpływ połączeń przez 2 minuty
}
resource "aws_ecs_service" "app" {
# ...
deployment_minimum_healthy_percent = 100 # Utrzymuj stare taski aż nowe będą zdrowe
deployment_maximum_percent = 200 # Pozwól na podwójną pojemność podczas wdrożenia
deployment_circuit_breaker {
enable = true
rollback = true
}
}
Krok 5: Zamknij wyścig na limicie bezczynności keep-alive (zdrowe cele, sporadyczne 502)
Jeśli cele pozostają zdrowe, a mimo to część żądań zwraca 502 pod obciążeniem, przyczyną jest prawie zawsze wyścig keep-alive. Zasada: limit keep-alive po stronie celu musi być większy niż limit bezczynności ALB (domyślnie 60s). Najpierw odczytaj limit bezczynności ALB:
aws elbv2 describe-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:eu-central-1:123456789012:loadbalancer/app/my-alb/abc123 \
--query "Attributes[?Key=='idle_timeout.timeout_seconds'].Value" --output text
# Domyślnie 60. Limit keep-alive celu musi być WIĘKSZY niż ta wartość.
Następnie podnieś limit keep-alive celu powyżej tej wartości:
// Node.js (Express / http.Server) - najczęstsze źródło tego błędu.
// Domyślny keepAliveTimeout w Node to 5s, znacznie poniżej 60s ALB-a, co
// gwarantuje wyścig pod obciążeniem. Podnieś go powyżej limitu bezczynności ALB.
const server = app.listen(8080);
server.keepAliveTimeout = 75_000; // 75s, musi przekraczać limit bezczynności ALB (60s)
server.headersTimeout = 76_000; // musi być >= keepAliveTimeout
# nginx przed aplikacją - utrzymuj keepalive powyżej limitu bezczynności ALB.
keepalive_timeout 75s;
# Gunicorn - podnieś keep-alive powyżej limitu bezczynności ALB.
# gunicorn --keep-alive 75 --workers 4 app:app
Objaw znika, gdy cel utrzymuje połączenie otwarte dłużej niż ALB, więc to ALB (a nie cel) zawsze decyduje, kiedy zamknąć bezczynne połączenie.
Weryfikacja
Po zastosowaniu poprawek potwierdź, że cele stają się zdrowe:
# 1. Sprawdź zdrowie celów - wszystkie powinny pokazywać "healthy"
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:eu-central-1:123456789012:targetgroup/my-app/abc123 \
--query 'TargetHealthDescriptions[*].TargetHealth.State'
# Oczekiwane: ["healthy", "healthy"]
# 2. Zweryfikuj, że ALB zwraca 200, nie 502
curl -s -o /dev/null -w "%{http_code}" https://my-app.example.com/health
# Oczekiwane: 200
# 3. Potwierdź brak błędów ELB 502 w CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/ApplicationELB \
--metric-name HTTPCode_ELB_502_Count \
--dimensions Name=LoadBalancer,Value=app/my-alb/abc123 \
--start-time "$(date -u -d '5 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 60 \
--statistics Sum
# Oczekiwane: 0
# 4. Przetestuj, że wdrożenie nie powoduje 502
aws ecs update-service --cluster main --service my-app --force-new-deployment
# Monitoruj: brak skoków 502 podczas wdrożenia
Błędy ALB 502 podczas wdrożeń lub skalowania to nie tylko uciążliwość - to utracone przychody. Każde żądanie, które trafia na 502, to klient widzący stronę błędu. Naprawa nie jest skomplikowana, ale wymaga poprawnej konfiguracji wszystkich czterech warstw jednocześnie (reguły SG, konfiguracja weryfikacji stanu, limity czasu, strategia wdrożenia). Jedna źle skonfigurowana warstwa wystarczy, by położyć całą usługę.
Potrzebujesz wdrożeń bez przestojów na AWS ECS?
Umów bezpłatną 30-minutową rozmowę. Projektujemy architektury ALB + ECS z prawidłowymi weryfikacjami stanu, wdrożeniami blue-green i circuit breakerami.