- Published on
EKS에서 ALB Ingress 502 Bad Gateway 원인 9가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스/쿠버네티스 환경에서 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 log의
target_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의
port와targetPort확인 - 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분 컷 체크리스트(우선순위)
- Ingress → Service → Endpoints가 정상인가? (Endpoints 비면 거의 끝)
- TargetGroup Healthy인가? Unhealthy면 헬스체크/포트/프로토콜부터
- target-type(ip/instance)와 네트워크 경로가 맞나? instance면 NodePort/SG
- ALB SG → 노드/Pod SG 인바운드가 열려 있나?
- Service port/targetPort가 정확한가?
- backend-protocol(HTTP/HTTPS) 불일치가 없는가?
- 배포 직후면 readiness/preStop/드레인 문제 아닌가?
- 장시간 요청이면 idle timeout/서버 타임아웃 문제 아닌가?
- 앱 로그에서 내부 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개 원인으로 빠르게 좁혀서 수정안을 제시해드릴게요.