Published on

EKS에서 ALB Ingress 502 Bad Gateway 원인 9가지

Authors

서버리스/쿠버네티스 환경에서 502 Bad Gateway는 대개 ALB가 백엔드(Target)로부터 유효한 응답을 받지 못했다는 뜻입니다. 그런데 EKS에서는 네트워크 경로가 Client → ALB → TargetGroup → Node/Pod로 길고, Ingress/Service/Pod/보안그룹/IAM까지 관여해서 원인 파악이 쉽지 않습니다.

이 글에서는 AWS Load Balancer Controller(ALBC) 기반의 ALB Ingress에서 502가 발생할 때, 현장에서 가장 자주 만나는 원인 9가지를 “증상 → 진단 명령 → 해결” 형태로 정리합니다.

> 참고: ALBC 자체 권한/인증 문제로 Controller가 동작하지 않으면 502 이전에 리소스 생성부터 실패합니다. 관련 이슈는 EKS에서 AWS Load Balancer Controller 403 해결법도 함께 보세요.


먼저: 502를 빠르게 분류하는 관찰 포인트

1) ALB 액세스 로그/CloudWatch 지표로 “어디서 끊겼는지” 확인

  • ALB access logtarget_status_code, error_reason(있다면), elb_status_code를 봅니다.
  • CloudWatch TargetResponseTime, HTTPCode_Target_5XX_Count, UnHealthyHostCount를 확인합니다.

대략적인 해석:

  • elb_status_code=502 + target_status_code=-타깃에 연결 자체 실패(SG/NACL/포트/타깃 등록 문제)
  • elb_status_code=502 + target_status_code=502/503애플리케이션이 5xx를 반환하거나 프록시/업스트림 문제
  • UnHealthyHostCount 증가헬스체크 실패(경로/포트/프로토콜/ready 문제)

2) Ingress/Service/Endpoint부터 역추적

kubectl get ingress -A
kubectl describe ingress -n <ns> <ingress>

kubectl get svc -n <ns>
kubectl describe svc -n <ns> <service>

kubectl get endpoints -n <ns> <service>
# 또는 EndpointSlice
kubectl get endpointslice -n <ns> -l kubernetes.io/service-name=<service>
  • Endpoints가 비어 있으면 ALB가 보낼 곳이 없습니다(거의 항상 502/503 계열).

원인 1) TargetGroup 헬스체크 경로/포트/프로토콜 불일치

전형적인 증상

  • ALB는 뜨지만 TargetGroup이 Unhealthy
  • 브라우저/클라이언트에서는 간헐적 또는 지속적 502

진단

  • AWS 콘솔에서 TargetGroup → Targets 상태 확인
  • Ingress annotation으로 헬스체크가 어떻게 설정됐는지 확인
kubectl get ingress -n <ns> <ingress> -o yaml | yq '.metadata.annotations'

해결

  • 애플리케이션이 실제로 200을 주는 경로로 설정
  • 서비스 포트와 컨테이너 포트, 헬스체크 포트가 일치하도록 조정

예시(헬스체크 경로/포트 지정):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app
  annotations:
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
    alb.ingress.kubernetes.io/healthcheck-port: traffic-port
    alb.ingress.kubernetes.io/success-codes: "200-399"
spec:
  ingressClassName: alb
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-svc
            port:
              number: 80

원인 2) Service/Pod 포트 매핑 오류(서비스는 80, 컨테이너는 8080 등)

전형적인 증상

  • TargetGroup은 등록되지만 헬스체크 실패
  • Pod 자체는 정상인데 외부에서만 502

진단

  • Service의 porttargetPort 확인
  • Deployment/Pod의 containerPort 확인
kubectl get svc -n <ns> app-svc -o yaml
kubectl get deploy -n <ns> app -o yaml | yq '.spec.template.spec.containers[].ports'

해결

  • Service targetPort를 실제 컨테이너 리스닝 포트로 맞춥니다.
apiVersion: v1
kind: Service
metadata:
  name: app-svc
spec:
  selector:
    app: app
  ports:
  - name: http
    port: 80
    targetPort: 8080

원인 3) Target type(instance/ip) 설정과 네트워크 모델 불일치

ALBC의 ALB는 TargetGroup에 instance 또는 ip로 타깃을 등록합니다.

  • instance: 노드(NodePort)로 트래픽 전달
  • ip: Pod IP로 직접 전달

전형적인 증상

  • instance 모드인데 NodePort가 막혀 있음 → 502
  • ip 모드인데 Pod IP로의 라우팅/SG 규칙이 부정확 → 502

진단

Ingress annotation 확인:

kubectl get ingress -n <ns> <ingress> -o yaml | yq '.metadata.annotations."alb.ingress.kubernetes.io/target-type"'

해결

  • 가능하면 EKS에서는 ip 모드를 많이 사용(구성 단순, Pod 단위 스케일)
  • instance 모드라면 NodePort 경로/보안그룹을 반드시 점검

관련해서 “노드포트만 안 열리는” 케이스는 EKS에서 NodePort만 안 열릴 때 CNI·SG 점검을 같이 보면 원인 좁히기가 빠릅니다.


원인 4) 보안그룹(SG) 인바운드/아웃바운드 규칙 누락

ALB → Target(노드 또는 Pod ENI)로의 트래픽이 SG에서 차단되면, ALB는 백엔드에 연결하지 못해 502를 냅니다.

전형적인 증상

  • TargetGroup에 타깃은 등록되지만 Unhealthy
  • ALB access log에서 target_status_code=- 형태가 많음

진단

  • ALB SG가 타깃 SG로 해당 포트 접근 가능한지 확인
  • 노드 SG(또는 Pod ENI SG)를 사용한다면 상호 참조 규칙 확인

해결 체크

  • ALB SG → 노드/Pod SG: 서비스 포트(또는 NodePort) 인바운드 허용
  • 응답 트래픽을 위한 아웃바운드도 제한돼 있다면 허용

실무 팁: SG를 CIDR로 뚫기보다 SG-to-SG 참조로 관리하면 변경에 강합니다.


원인 5) Pod Readiness/Endpoint 반영 지연(준비 안 된 Pod로 라우팅)

ALB 자체는 쿠버네티스의 readiness를 “직접” 이해하지 못합니다. 보통은 Service Endpoint에 준비된 Pod만 들어가지만, 다음 상황에서 엇갈릴 수 있습니다.

  • readinessProbe가 부정확해서 준비 안 됐는데 Ready로 뜸
  • 롤링 업데이트 중 연결이 끊기는데 **종료 처리(preStop/terminationGracePeriod)**가 부족

전형적인 증상

  • 배포 직후/스케일링 직후 502 급증
  • 잠시 후 정상화(또는 계속 불안정)

진단

kubectl get pod -n <ns> -w
kubectl describe pod -n <ns> <pod>
kubectl get endpoints -n <ns> <service> -w

해결

  • readinessProbe를 “앱이 실제로 요청을 처리 가능한 시점”에 맞춤
  • 종료 시 커넥션 드레인 시간을 확보

예시(종료 안정화):

spec:
  terminationGracePeriodSeconds: 60
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 20"]
    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5

원인 6) 백엔드 프로토콜(HTTP/HTTPS) 또는 TLS 설정 불일치

ALB에서 백엔드로 HTTPS로 붙는데 Pod는 HTTP만 리슨하거나, 반대로 HTTP로 붙는데 앱이 HTTPS만 받는 경우 헬스체크부터 실패합니다.

전형적인 증상

  • 헬스체크 실패 + 앱 로그에 TLS handshake 관련 에러
  • 특정 경로만 502(리다이렉트/가상호스트 영향)

진단

Ingress annotation 확인:

kubectl get ingress -n <ns> <ingress> -o yaml | yq '.metadata.annotations."alb.ingress.kubernetes.io/backend-protocol"'

해결

  • 백엔드가 HTTP면:
metadata:
  annotations:
    alb.ingress.kubernetes.io/backend-protocol: HTTP
  • 백엔드가 HTTPS면 인증서/서버네임(SNI)까지 고려해야 합니다. (대부분은 ALB에서 TLS 종료 후 백엔드는 HTTP로 단순화)

추가로 클러스터 내부 TLS/네트워크 이슈가 의심되면 EKS TLS handshake timeout 해결 - IRSA·VPC·CoreDNS도 참고할 만합니다.


원인 7) ALB Idle timeout/애플리케이션 응답 지연(업스트림 타임아웃)

ALB는 기본적으로 **idle timeout(기본 60s)**이 있고, 백엔드가 그 안에 응답을 못 하면 연결이 끊기며 502/504 계열로 이어질 수 있습니다(상황에 따라 502로 관측되기도).

전형적인 증상

  • 대용량 처리/LLM 호출/리포트 생성 같은 장시간 요청에서만 502
  • 동시성 증가 시 급격히 재현

진단

  • CloudWatch에서 TargetResponseTime 증가
  • 애플리케이션 APM/로그에서 처리시간 확인

해결

  • ALB idle timeout 증가(필요한 범위 내에서)
  • 백엔드에서 스트리밍/비동기화, 큐잉, 폴링 패턴 고려

ALB/프록시 뒤에서 스트리밍이 끊기는 류의 타임아웃/버퍼 이슈는 성격이 비슷하므로, 장시간 연결(SSE/WebSocket)이라면 FastAPI Uvicorn에서 SSE 웹소켓 LLM 스트리밍… 체크리스트도 같이 보면 도움이 됩니다.

예시(idle timeout 설정):

metadata:
  annotations:
    alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=180

원인 8) 애플리케이션 레벨 5xx(프레임워크/리버스 프록시 설정 포함)

ALB가 정상적으로 백엔드에 붙었는데도 502가 나오면, 실제로는 백엔드가 502를 반환하거나(예: Nginx/Envoy), 앱이 500을 내고 ALB에서 502로 보이는 케이스가 있습니다.

전형적인 증상

  • TargetGroup은 Healthy인데도 특정 요청에서만 502
  • Pod 로그에 upstream 연결 실패, DNS 실패, connection refused 등

진단

  • Pod 로그에서 5xx 원인 확인
  • 컨테이너 내부에서 로컬 호출로 재현
kubectl logs -n <ns> deploy/app --tail=200
kubectl exec -n <ns> -it deploy/app -- sh -c 'wget -S -O- http://127.0.0.1:8080/healthz'

해결

  • 앱이 의존하는 내부 서비스(DNS/네트워크) 정상 여부 확인
  • 리버스 프록시(Nginx 등)를 쓴다면 upstream 설정/버퍼/timeout을 점검

원인 9) DNS/CoreDNS/서비스 디스커버리 문제로 백엔드 내부 호출이 실패

겉으로는 “ALB 502”지만, 실제 원인은 앱이 내부에서 다른 서비스(예: http://api.default.svc.cluster.local)를 호출하다가 DNS 타임아웃/업스트림 실패로 5xx를 내는 경우가 많습니다.

전형적인 증상

  • 앱 로그에 NameResolutionError, no such host, i/o timeout
  • 특정 노드/특정 시간대에만 재현(리소스 압박)

진단

  • CoreDNS 상태 확인
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200
  • Pod에서 DNS 질의 테스트
kubectl run -n <ns> -it --rm dns-test --image=busybox:1.36 -- sh
# inside
nslookup kubernetes.default.svc.cluster.local

해결

  • CoreDNS 리소스/업스트림 설정 점검, 노드/네트워크 이슈 확인

CoreDNS가 불안정할 때의 전형적인 패턴과 해결은 EKS CoreDNS CrashLoopBackOff - upstream 타임아웃 해결에서 더 깊게 다룹니다.


실전용: 10분 컷 체크리스트(우선순위)

  1. Ingress → Service → Endpoints가 정상인가? (Endpoints 비면 거의 끝)
  2. TargetGroup Healthy인가? Unhealthy면 헬스체크/포트/프로토콜부터
  3. target-type(ip/instance)와 네트워크 경로가 맞나? instance면 NodePort/SG
  4. ALB SG → 노드/Pod SG 인바운드가 열려 있나?
  5. Service port/targetPort가 정확한가?
  6. backend-protocol(HTTP/HTTPS) 불일치가 없는가?
  7. 배포 직후면 readiness/preStop/드레인 문제 아닌가?
  8. 장시간 요청이면 idle timeout/서버 타임아웃 문제 아닌가?
  9. 앱 로그에서 내부 DNS/의존성 호출 실패가 없는가?

마무리: 502는 “ALB 문제”가 아니라 “경로 전체” 문제다

EKS의 ALB Ingress 502는 ALB가 나쁘다기보다, 타깃 등록(Endpoint) → 연결(SG/포트) → 헬스체크 → 애플리케이션 응답(타임아웃/의존성) 중 한 구간이 깨졌다는 신호입니다. 위 9가지를 순서대로 점검하면 “감으로”가 아니라 “증거 기반”으로 원인을 좁힐 수 있습니다.

원하시면, 실제 Ingress/Service/Deployment YAML과 ALB TargetGroup 상태(Healthy/Unhealthy 사유)를 주시면 환경(ip/instance, NodePort 여부, SG 구조)에 맞춰 가장 가능성 높은 1~2개 원인으로 빠르게 좁혀서 수정안을 제시해드릴게요.