- Published on
EKS ALB Ingress 502 Target reset 원인과 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 살아 있는데도 ALB Ingress가 502 Bad Gateway를 반환하면서 ALB 액세스 로그나 콘솔에서 Target reset(혹은 유사한 reset 계열 메시지)이 보이면, **“ALB가 타깃(노드/Pod)과 TCP 연결은 맺었지만 응답을 받기 전에 타깃이 연결을 끊었다”**는 신호인 경우가 많습니다.
문제는 여기서 끝나지 않습니다. EKS에서는 Ingress(=ALB), Service(NodePort/ClusterIP), Pod, CNI, 보안그룹, 타임아웃이 체인처럼 연결되어 있어 어느 한 지점의 불일치가 reset으로 표면화됩니다. 이 글은 “무조건 설정을 바꿔보자”가 아니라, 로그/상태 기반으로 원인을 좁혀가는 체크리스트 + 바로 적용 가능한 설정 예시를 제공합니다.
> 참고로 네트워크/보안그룹 관점에서 NodePort 경로가 꼬이는 케이스도 많습니다. 유사 증상 점검은 EKS에서 NodePort만 안 열릴 때 CNI·SG 점검도 함께 보면 진단 속도가 빨라집니다.
1) 증상 정의: “502 + Target reset”이 의미하는 것
ALB 기준으로 Target reset은 보통 아래 중 하나로 귀결됩니다.
- ALB → 타깃(TG) 연결이 성립했으나 타깃이 RST/FIN로 먼저 끊음
- 헬스체크는 통과/실패를 반복하지만 실제 트래픽 경로는 다른 포트/프로토콜로 들어감
- idle timeout/keep-alive/HTTP2 설정 불일치로 중간에 연결이 정리됨
- Pod/애플리케이션이 요청을 처리하다가 크래시/리스타트/커넥션 드롭
특히 EKS에서 흔한 패턴은 다음입니다.
- Ingress는 80으로 받는데, Service는 8080으로 포워딩, 컨테이너는 3000 리슨… 이런 포트 체인 불일치
target-type: instance인데 Service가ClusterIP만 있고 NodePort 경로가 열려 있지 않음target-type: ip인데 Pod IP로 직접 붙으면서 보안그룹/네트워크 정책/리드니스가 엇갈림
2) 가장 먼저 확인할 5가지(시간 절약용)
2.1 ALB 타깃 그룹(Target Group) 상태
AWS 콘솔 또는 CLI로 TG 타깃 상태를 확인합니다.
healthy가 안정적으로 유지되는가?unhealthy사유가Health checks failed인지,Target.Timeout인지,Target.ResponseCodeMismatch인지?
CLI 예시:
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:...:targetgroup/xxx
2.2 Ingress 이벤트: AWS Load Balancer Controller가 뭘 만들었나
kubectl describe ingress -n <ns> <name>
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -n 50
Ingress 이벤트에 failed build model, security group, target group 관련 힌트가 자주 있습니다. 컨트롤러 권한/403류 이슈가 의심되면 EKS에서 AWS Load Balancer Controller 403 해결법도 함께 확인하세요.
2.3 Service 타입과 target-type 매칭
alb.ingress.kubernetes.io/target-type: instance- ALB → NodePort로 들어갑니다.
- Service는 NodePort(또는 LoadBalancer지만 내부적으로 NodePort 경유)여야 하고, 노드 SG에서 NodePort가 열려야 합니다.
alb.ingress.kubernetes.io/target-type: ip- ALB → Pod IP로 직접 들어갑니다.
- Pod SG(보안그룹 for pods) 또는 노드 SG/서브넷 라우팅/네트워크 정책이 맞아야 합니다.
2.4 Pod Readiness/Endpoint가 실제로 붙어 있나
kubectl get endpoints -n <ns> <svc>
kubectl get pod -n <ns> -o wide
kubectl describe pod -n <ns> <pod>
엔드포인트가 비어 있거나, readiness 실패로 엔드포인트가 빠졌다 들어왔다 하면 ALB는 간헐적으로 reset을 경험할 수 있습니다.
2.5 애플리케이션 로그에서 “connection reset”, “upstream prematurely closed”
ALB가 아니라 Pod가 먼저 연결을 끊는 경우가 많습니다.
- 앱이 SIGTERM 받고 종료 중인데 프리스트롭(PreStop) 없이 바로 커넥션을 끊음
- 워커 수 부족/GC/메모리 압박으로 요청 처리 중 리스타트
kubectl logs -n <ns> deploy/<app> --since=10m
kubectl get pod -n <ns> -w
3) 대표 원인별 해결 패턴
3.1 원인 A: target-type=instance인데 NodePort 경로가 막힘
증상
- TG 타깃이 노드로 잡히지만
unhealthy가 지속 - 노드 보안그룹에서 NodePort 범위(기본 30000-32767) 인바운드가 없음
- 특정 노드만 unhealthy (스케줄링/노드 교체 시 증상 변동)
해결
- Service가 NodePort로 노출되는지 확인
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: NodePort
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 8080
nodePort: 31080
- Ingress에서 instance 타깃을 명시하고, 헬스체크 포트를 일치시킵니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: default
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: instance
alb.ingress.kubernetes.io/healthcheck-path: /healthz
alb.ingress.kubernetes.io/healthcheck-port: "traffic-port"
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
- 노드 SG 인바운드에
nodePort(예: 31080)또는 범위(30000-32767)를 ALB SG 소스로 허용합니다.
> NodePort만 유독 안 열리는 전형적인 점검 흐름은 EKS에서 NodePort만 안 열릴 때 CNI·SG 점검에 더 자세히 정리되어 있습니다.
3.2 원인 B: target-type=ip인데 Pod로의 인바운드가 막힘(보안그룹/네트워크)
증상
- TG 타깃이 Pod IP로 등록되지만 unhealthy
- 같은 클러스터라도 특정 네임스페이스/노드에서만 실패
- 보안그룹 for pods 사용 시, Pod SG 규칙 누락
해결
ip타깃이면 ALB SG → Pod(혹은 노드)로의 인바운드가 허용되어야 합니다.- EKS 보안그룹 for pods를 쓴다면 Pod ENI에 붙은 SG에 인바운드를 열어야 합니다.
Ingress 예시(핵심은 target-type=ip):
metadata:
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/backend-protocol: HTTP
alb.ingress.kubernetes.io/healthcheck-path: /healthz
추가로 Pod IP가 부족/불안정하면 타깃 등록이 들쑥날쑥해지고, 그 결과 reset/502가 간헐적으로 발생하기도 합니다. VPC CNI 문제 가능성이 있으면 EKS VPC CNI IP 누수로 Pod IP 고갈 해결하기도 같이 확인하세요.
3.3 원인 C: 헬스체크 경로/포트/성공코드 불일치
증상
- 실제 서비스는 정상인데 TG만 unhealthy
/는 301/302 리다이렉트,/healthz는 200인데 헬스체크가/로 되어 있음- 앱이 204를 반환하는데 성공코드가 200으로 고정
해결
헬스체크를 명시적으로 고정합니다.
metadata:
annotations:
alb.ingress.kubernetes.io/healthcheck-path: /healthz
alb.ingress.kubernetes.io/healthcheck-port: "traffic-port"
alb.ingress.kubernetes.io/success-codes: "200-399"
또한 Service의 targetPort가 컨테이너 리슨 포트와 같은지 재확인합니다.
kubectl get svc -n default myapp -o yaml | yq '.spec.ports'
kubectl get deploy -n default myapp -o yaml | yq '.spec.template.spec.containers[].ports'
3.4 원인 D: 타임아웃/keep-alive 불일치로 인한 연결 리셋
증상
- 짧은 요청은 OK, 긴 요청에서만 502
- 간헐적으로만 발생(피크 타임/배포 직후)
- 앱/프록시(예: NGINX, Envoy) 로그에 upstream timeout/close
해결
- ALB idle timeout 조정(기본 60초)
metadata:
annotations:
alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=120
애플리케이션/사이드카 keep-alive를 ALB와 맞추고, 장시간 요청은 비동기화(큐/웹훅)도 고려합니다.
HTTP/2(gRPC 포함)라면 reset이 더 자주 관측될 수 있습니다. gRPC/HTTP2 레벨의 RST_STREAM 튜닝 포인트는 Kubernetes gRPC UNAVAILABLE·RST_STREAM 원인과 Envoy·NGINX 대응에서 다룹니다.
3.5 원인 E: Pod 종료/배포 시 연결 드롭(Graceful shutdown 미흡)
증상
- 롤링 업데이트/오토스케일링 때만 502
kubectl rollout restart직후 스파이크- Pod가 SIGTERM 후 즉시 종료하면서 기존 커넥션이 reset
해결
- terminationGracePeriodSeconds + preStop으로 드레이닝 시간을 확보
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
readinessProbe가 실제 트래픽 수용 가능 시점에만 통과하도록 구성(예: DB 연결 완료 후 200)
HPA/Cluster Autoscaler 환경에서는 노드 드레인 이벤트에도 동일한 문제가 발생할 수 있으니 PDB(DisruptionBudget)도 함께 검토합니다.
4) 실전 디버깅: “ALB에서 Pod까지” 패킷 흐름을 끊어보기
문제를 빠르게 좁히려면, 같은 요청을 각 홉에서 직접 호출해 봅니다.
4.1 Pod 내부에서 로컬 리슨 확인
kubectl exec -n default -it deploy/myapp -- sh -c 'netstat -lntp || ss -lntp'
4.2 클러스터 내부에서 Service로 호출
kubectl run -n default -it --rm curl --image=curlimages/curl -- \
curl -sv http://myapp.default.svc.cluster.local/healthz
4.3 노드에서 NodePort로 호출(instance 타깃일 때)
노드에 SSM/SSH로 접속 가능하다면:
curl -sv http://127.0.0.1:31080/healthz
여기서 성공하면 앱/Service 체인은 정상이고, 실패하면 Kubernetes 내부(서비스/엔드포인트/리슨 포트) 문제로 좁혀집니다.
5) 자주 하는 실수 모음(체크리스트)
- Ingress backend servicePort는 80인데 Service가 8080만 열어둠
- readinessProbe 없이 livenessProbe만 둬서 “준비 안 됐는데 트래픽 유입”
target-type: instance인데 Service가ClusterIP- ALB SG는 열었는데 노드 SG/Pod SG 인바운드를 안 열음
- 헬스체크 경로가 인증 필요(
/가 401/302) - 배포 시 SIGTERM 처리 없이 즉시 종료(특히 Node.js/Go에서 흔함)
6) 결론: 502 Target reset은 “불일치”의 신호다
EKS의 ALB Ingress에서 502 + Target reset은 ALB 자체 장애라기보다, 대개 아래 축 중 하나의 불일치에서 시작합니다.
- 타깃 타입(instance/ip) ↔ Service 타입(NodePort/ClusterIP) ↔ 보안그룹
- 헬스체크 경로/포트/성공코드 ↔ 실제 앱 동작
- 타임아웃/keep-alive ↔ 앱/프록시 설정
- 배포/스케일링 시 graceful shutdown ↔ readiness/draining
위의 순서대로 “TG 상태 → Ingress 이벤트 → Service/Endpoints → 앱 로그 → 타임아웃/종료 처리”를 밟으면, 대부분은 30분 내로 원인이 좁혀집니다.
원하시면 현재 사용 중인 Ingress/Service YAML, TG 상태(healthy/unhealthy reason), 그리고 ALB 액세스 로그의 해당 라인(개인정보 제거)을 주시면 케이스에 맞춰 정확히 어떤 불일치가 reset을 만드는지를 짚어서 수정안을 제안해 드릴게요.