본문 바로가기

Troubleshooting

ALB 502 Bad Gateway 원인 분석: Target Group, Health Check, 타임아웃까지

반응형

배포 직후 ALB에서 502가 터지기 시작했습니다. Target Group은 healthy인데 왜 502일까요? ALB 502는 원인이 다양하고, 증상만으로는 구분이 어렵습니다. 이 글에서는 원인별 진단 흐름과 해결 방법을 정리합니다.

핵심 요약

원인 증상 확인 방법
Target이 모두 unhealthy 모든 요청에서 502 Target Group Health Check 상태 확인
백엔드가 연결을 먼저 끊음 간헐적 502 ALB Access Log의 target_status_code 확인
백엔드 응답 타임아웃 특정 요청에서 502 ALB idle timeout vs 백엔드 timeout 비교
Target 등록/해제 중 배포 직후 502 Deregistration delay 설정 확인
백엔드 Keep-Alive < ALB idle timeout 간헐적 502 (부하 시) 백엔드 Keep-Alive 설정 확인
Security Group/NACL 차단 지속적 502 Health Check port 허용 여부 확인

1. ALB 502 Bad Gateway란

ALB가 클라이언트 요청을 백엔드(Target)로 전달했지만, 정상적인 응답을 받지 못했을 때 반환하는 HTTP 상태 코드입니다.

핵심 포인트: 502는 ALB 자체의 문제가 아니라, ALB와 백엔드 사이의 통신 문제입니다.

ALB가 502를 반환하는 조건:

  • Target에 연결을 시도했지만 TCP 연결 자체가 실패한 경우
  • Target이 연결을 수립한 후 응답 없이 연결을 끊은 경우
  • Target이 잘못된 형식의 HTTP 응답을 반환한 경우
  • 모든 Target이 unhealthy 상태인 경우
ALB 502 발생 흐름
ALB 502 발생 흐름

2. 원인별 분석

2-1. 모든 Target이 unhealthy

가장 흔한 원인입니다. Target Group에 등록된 모든 인스턴스가 Health Check에 실패하면, ALB는 요청을 전달할 대상이 없어 502를 반환합니다.

확인 방법:

aws elbv2 describe-target-health \
  --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789012:targetgroup/my-tg/abc123

출력 예시 (unhealthy):

{
  "TargetHealthDescriptions": [
    {
      "Target": { "Id": "i-0abc123", "Port": 8080 },
      "TargetHealth": {
        "State": "unhealthy",
        "Reason": "Target.ResponseCodeMismatch",
        "Description": "Health checks failed with these codes: [503]"
      }
    }
  ]
}

주요 원인:

  • 애플리케이션이 Health Check 경로에서 200을 반환하지 않음
  • Health Check port와 애플리케이션 리스닝 port가 다름
  • Security Group이 Health Check 트래픽을 차단
  • 애플리케이션 시작 시간이 Health Check interval보다 김

해결:

# Health Check 설정 확인
aws elbv2 describe-target-groups \
  --target-group-arns arn:aws:elasticloadbalancing:ap-northeast-2:123456789012:targetgroup/my-tg/abc123 \
  --query 'TargetGroups[0].{Path:HealthCheckPath,Port:HealthCheckPort,Interval:HealthCheckIntervalSeconds,Threshold:UnhealthyThresholdCount}'

2-2. 백엔드가 연결을 먼저 끊음 (Connection Reset)

ALB가 기존 TCP 연결을 재사용하려고 요청을 보냈는데, 백엔드가 이미 그 연결을 닫은 경우입니다. 이 상황은 ALB Access Log에서 target_status_code-로 기록됩니다.

실무 시나리오:

Nginx를 리버스 프록시로 사용하는 환경에서, Nginx의 keepalive_timeout이 60초이고 ALB의 idle timeout이 60초인 경우를 생각해봅니다. 두 값이 같으면 Nginx가 연결을 닫는 시점과 ALB가 요청을 보내는 시점이 겹칠 수 있습니다. 이때 race condition이 발생하여 간헐적 502가 나타납니다.

ALB Access Log 확인:

# target_status_code가 "-"인 경우 = 백엔드가 응답 전에 연결을 끊음
# elb_status_code가 502인 로그 필터링
grep "502" alb-access-log.txt | awk '{print $9, $10}'

해결:

백엔드의 Keep-Alive timeout을 ALB idle timeout보다 크게 설정합니다.

# Nginx 설정 예시
# ALB idle timeout: 60초 → Nginx keepalive_timeout: 65초 이상
keepalive_timeout 65;
# Spring Boot 설정 예시
server:
  tomcat:
    keep-alive-timeout: 65000  # 65초 (ALB idle timeout 60초보다 큼)

2-3. 백엔드 응답 타임아웃

ALB의 idle timeout(기본 60초) 내에 백엔드가 응답을 시작하지 않으면 502를 반환합니다.

실무 시나리오:

대용량 리포트를 생성하는 API가 있습니다. DB 쿼리에 45초, 데이터 가공에 20초가 걸려 총 65초가 소요됩니다. ALB idle timeout이 기본 60초이므로, ALB는 60초 시점에 연결을 끊고 502를 반환합니다.

확인 방법:

# ALB idle timeout 확인
aws elbv2 describe-load-balancer-attributes \
  --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789012:loadbalancer/app/my-alb/abc123 \
  --query 'Attributes[?Key==`idle_timeout.timeout_seconds`].Value'

해결:

# ALB idle timeout 증가 (최대 4000초)
aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789012:loadbalancer/app/my-alb/abc123 \
  --attributes Key=idle_timeout.timeout_seconds,Value=120
주의
idle timeout을 무작정 늘리면 연결이 오래 유지되어 ALB 리소스를 소모합니다. 장시간 처리가 필요한 요청은 비동기 패턴(SQS + Worker)으로 분리하는 것이 운영 관점에서 적합합니다.

2-4. 배포 중 Target 등록/해제 (Deregistration Delay)

Rolling 배포 시 기존 Target이 해제되고 새 Target이 등록되는 과정에서 502가 발생할 수 있습니다.

발생 흐름:

  1. 배포 시작 → 기존 인스턴스 Target Group에서 해제 시작
  2. Deregistration delay 동안 기존 연결은 유지되지만, 새 요청은 전달되지 않음
  3. 새 인스턴스가 등록되었지만 아직 Health Check를 통과하지 못한 상태
  4. 이 시점에 healthy Target이 0개가 되면 502 발생

확인 방법:

# Deregistration delay 확인 (기본 300초)
aws elbv2 describe-target-group-attributes \
  --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:123456789012:targetgroup/my-tg/abc123 \
  --query 'Attributes[?Key==`deregistration_delay.timeout_seconds`].Value'

해결:

  • Health Check의 HealthyThresholdCount를 줄여 새 Target이 빨리 healthy가 되도록 설정
  • 배포 전략에서 최소 healthy Target 수를 보장 (예: ECS minimum healthy percent 100%)
  • Deregistration delay를 애플리케이션의 평균 요청 처리 시간에 맞게 조정
ALB 502 원인 구조
ALB 502 원인 구조

2-5. Security Group / NACL 설정 오류

ALB와 Target 간 네트워크 통신이 차단되면 Health Check가 실패하고 502가 발생합니다.

확인 체크리스트:

확인 항목 설명
Target의 Security Group Inbound ALB의 Security Group에서 오는 트래픽 허용 여부
Health Check Port Target SG에서 Health Check port 허용 여부
NACL Inbound/Outbound Subnet 레벨에서 양방향 트래픽 허용 여부
Ephemeral Port NACL Outbound에서 1024-65535 허용 여부

올바른 Security Group 설정 예시:

# Target EC2의 Security Group: ALB SG에서 오는 트래픽 허용
aws ec2 authorize-security-group-ingress \
  --group-id sg-target-123 \
  --protocol tcp \
  --port 8080 \
  --source-group sg-alb-456

3. 진단 흐름

502가 발생했을 때 아래 순서로 원인을 좁혀갑니다.

ALB 502 진단 흐름
ALB 502 진단 흐름

Step 1: Target Health 확인

aws elbv2 describe-target-health --target-group-arn <TG_ARN>
  • 모든 Target이 unhealthy → 2-1 참고
  • healthy Target이 있는데 502 → Step 2로

Step 2: ALB Access Log 분석

# S3에서 Access Log 다운로드 후 분석
# target_status_code 확인
# "-" = 백엔드가 응답 전에 연결 끊음 → 2-2 참고
# "0" = 타임아웃 → 2-3 참고

Step 3: CloudWatch 메트릭 확인

# HTTPCode_ELB_502_Count 메트릭으로 발생 패턴 확인
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-05-31T00:00:00Z \
  --end-time 2026-05-31T23:59:59Z \
  --period 300 \
  --statistics Sum
  • 배포 시점에 집중 → 2-4 참고
  • 부하 증가 시 발생 → 2-2 (Keep-Alive race condition) 참고
  • 특정 API에서만 발생 → 2-3 (타임아웃) 참고

Step 4: 네트워크 확인

  • Security Group, NACL 규칙 점검 → 2-5 참고

4. 실무 시나리오: 배포 후 간헐적 502

상황: ECS Fargate에서 Rolling 배포 후 약 30초간 502가 간헐적으로 발생합니다.

진단 과정:

  1. Target Health 확인 → 배포 중 일시적으로 healthy Target이 0개가 되는 구간 발견
  2. ECS Task 시작 시간: 약 25초 (컨테이너 pull + 애플리케이션 시작)
  3. Health Check 설정: interval 30초, healthy threshold 3회 → 최소 90초 후 healthy
  4. 기존 Task 해제 + 새 Task healthy 전환 사이에 공백 발생

해결:

{
  "healthCheck": {
    "path": "/health",
    "interval": 10,
    "timeout": 5,
    "healthyThresholdCount": 2,
    "unhealthyThresholdCount": 3
  }
}
  • Health Check interval: 30초 → 10초
  • Healthy threshold: 3회 → 2회
  • 최소 healthy 전환 시간: 90초 → 20초로 단축
  • ECS minimum healthy percent: 100% 유지 (기존 Task를 새 Task가 healthy 될 때까지 유지)

5. ALB Access Log 활성화

502 원인 분석에서 Access Log는 핵심 진단 도구입니다. 운영 환경에서는 반드시 활성화해야 합니다.

# Access Log 활성화
aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn <ALB_ARN> \
  --attributes \
    Key=access_logs.s3.enabled,Value=true \
    Key=access_logs.s3.bucket,Value=my-alb-logs-bucket \
    Key=access_logs.s3.prefix,Value=alb-logs

Access Log 주요 필드:

필드 설명 502 진단 활용
elb_status_code ALB가 클라이언트에 반환한 상태 코드 502 필터링
target_status_code 백엔드가 ALB에 반환한 상태 코드 -: 연결 끊김, 숫자: 백엔드 에러
request_processing_time ALB가 요청을 받고 Target에 전달하기까지 시간 -1이면 Target 전달 실패
target_processing_time Target이 요청을 받고 응답하기까지 시간 -1이면 응답 없음 (타임아웃)
response_processing_time ALB가 응답을 받고 클라이언트에 전달하기까지 시간 -1이면 전달 실패
Tip
Access Log는 5분 단위로 S3에 저장됩니다. 실시간 분석이 필요하면 CloudWatch Logs로 전송하거나, Athena 테이블을 미리 생성해두면 빠르게 쿼리할 수 있습니다.

6. 재발 방지 체크리스트

항목 권장 설정 이유
백엔드 Keep-Alive timeout ALB idle timeout + 5초 이상 Race condition 방지
Health Check interval 10~15초 빠른 상태 감지
Healthy threshold 2회 새 Target 빠른 투입
Deregistration delay 평균 요청 처리 시간 + 여유 진행 중 요청 완료 보장
ECS minimum healthy % 100% 배포 중 가용 Target 보장
ALB Access Log 활성화 사후 분석 가능
CloudWatch 알람 HTTPCode_ELB_502_Count > 0 즉시 감지
Security Note
ALB Access Log에는 클라이언트 IP, 요청 URL, User-Agent 등이 포함됩니다. S3 버킷에 적절한 접근 제어와 수명 주기 정책을 설정하여 로그 데이터를 보호해야 합니다.

7. 정리

  • ALB 502는 ALB 자체 문제가 아니라 ALB↔백엔드 간 통신 실패를 의미합니다.
  • 가장 흔한 원인은 모든 Target이 unhealthy인 경우이며, Health Check 설정을 먼저 확인합니다.
  • 간헐적 502는 Keep-Alive timeout race condition이 원인인 경우가 많습니다. 백엔드 Keep-Alive를 ALB idle timeout보다 크게 설정합니다.
  • 배포 중 502는 Health Check 전환 시간과 배포 전략의 조합으로 해결합니다.
  • Access Log는 502 원인 분석의 핵심 도구입니다. 운영 환경에서는 반드시 활성화합니다.

참고 문서

반응형