- Published on
EKS에서 Pod는 정상인데 egress만 막힐 때 점검
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버는 잘 뜨고(Pod Running, readiness OK), 서비스도 내부 통신은 되는데 curl https://google.com 같은 egress만 타임아웃/리졸브 실패가 나는 상황은 EKS 운영에서 꽤 흔합니다. 특히 프라이빗 서브넷에 있는 워커 노드가 인터넷으로 나가려면 NAT + 라우팅 + SG/NACL이 모두 맞아야 하고, 거기에 VPC CNI의 SNAT 동작까지 겹치면 원인이 여러 갈래로 갈라집니다.
이 글은 “Pod는 되는데 egress만 차단?”이라는 증상을 네트워크 계층별로 분해해, 가장 빠르게 원인을 좁히는 절차를 제공합니다.
> 참고: 인그레스는 정상인데 외부 호출만 실패하는 케이스는 ALB 5xx로도 보일 수 있습니다. 인그레스/ALB 관점의 504 증상은 EKS ALB Ingress 504인데 Pod는 정상일 때도 함께 보세요.
증상 패턴으로 1차 분류하기
먼저 Pod 안에서 아래를 실행해 “DNS 문제인지, 라우팅/NAT 문제인지, 보안 정책 문제인지”를 가릅니다.
# 1) DNS 확인
kubectl exec -it deploy/myapp -- sh -c 'cat /etc/resolv.conf; nslookup google.com || true'
# 2) TCP 연결 확인 (DNS를 우회하기 위해 IP로도 시도)
kubectl exec -it deploy/myapp -- sh -c 'curl -I --connect-timeout 3 https://1.1.1.1 || true'
# 3) HTTP 프록시 환경변수 확인
kubectl exec -it deploy/myapp -- sh -c 'env | egrep -i "http_proxy|https_proxy|no_proxy" || true'
nslookup이 실패하면: CoreDNS / VPC DNS / 라우팅 to resolver / NACL 가능성이 큽니다.- DNS는 되는데
curl https://1.1.1.1이 타임아웃이면: NAT/라우팅/SG/NACL 쪽 가능성이 큽니다. - 특정 도메인만 실패하면: 프록시/방화벽/네트워크 정책/엔드포인트 가능성이 있습니다.
가장 흔한 원인 1: NAT 게이트웨이/인스턴스가 없거나 경로가 틀림
프라이빗 서브넷의 노드에서 인터넷 egress를 하려면 일반적으로 다음이 필요합니다.
- 퍼블릭 서브넷에 NAT Gateway (또는 NAT Instance)
- 프라이빗 서브넷 라우팅 테이블에
0.0.0.0/0 -> NAT라우트 - NAT가 위치한 퍼블릭 서브넷 라우팅 테이블에
0.0.0.0/0 -> IGW - NAT에 연결된 EIP 및 상태 정상
라우팅 테이블 빠른 점검
# 프라이빗 서브넷 라우팅 테이블 확인
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-PRIVATE" \
--query 'RouteTables[0].Routes'
# 퍼블릭 서브넷이 IGW로 나가는지 확인
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-PUBLIC" \
--query 'RouteTables[0].Routes'
정상이라면 대략 이런 형태가 나와야 합니다.
- 프라이빗:
0.0.0.0/0 -> nat-xxxxxxxx - 퍼블릭:
0.0.0.0/0 -> igw-xxxxxxxx
NAT Gateway 상태 확인
aws ec2 describe-nat-gateways \
--filter "Name=vpc-id,Values=vpc-xxxx" \
--query 'NatGateways[].{NatId:NatGatewayId,State:State,Subnet:SubnetId}'
State가 available이 아니면 egress는 거의 확실히 불안정/불가입니다.
가장 흔한 원인 2: 노드 보안그룹(SG) egress가 막혀 있음
EKS에서 Pod의 egress는 기본적으로 노드 ENI를 타고 나갑니다(기본 VPC CNI 동작). 따라서 “Pod 보안그룹”이 따로 없고(기본), 노드 SG의 egress 규칙이 곧 Pod egress 규칙이 됩니다.
- 노드 SG egress가
0.0.0.0/0허용이 아니거나 - 특정 포트(예: 443)만 막혀 있거나
- 목적지가 VPC Prefix List/특정 CIDR로 제한되어 있거나
이면 Pod에서 외부 HTTPS 호출만 실패할 수 있습니다.
SG egress 확인
aws ec2 describe-security-groups \
--group-ids sg-NODE \
--query 'SecurityGroups[0].IpPermissionsEgress'
일반적인 “인터넷 전체 허용”은 아래처럼 보입니다.
IpProtocol: -1IpRanges: [{CidrIp: 0.0.0.0/0}]
> 주의: 보안팀 정책으로 egress를 제한하는 조직에서는 NAT/라우팅이 멀쩡해도 SG에서 막힙니다. 이 경우 VPC Endpoint(PrivateLink)로 우회하는 설계가 더 적합할 수 있습니다.
자주 놓치는 원인 3: 네트워크 ACL(NACL)에서 Ephemeral Port가 차단
SG는 stateful이지만 NACL은 stateless라서, 아웃바운드만 열어도 리턴 트래픽이 인바운드에서 막힐 수 있습니다. 특히 아래가 흔한 함정입니다.
- 아웃바운드 443 허용은 했는데
- 인바운드에서 ephemeral port(대개 1024-65535) 를 막아 리턴 패킷이 드롭
NACL 확인 포인트
- 프라이빗 서브넷에 연결된 NACL의 Inbound/Outbound 모두 확인
0.0.0.0/0에 대해- Outbound: 443(또는 전체)
- Inbound: ephemeral port 범위 허용
CLI로는 연관 NACL을 찾고 엔트리를 확인합니다.
aws ec2 describe-network-acls \
--filters "Name=association.subnet-id,Values=subnet-PRIVATE" \
--query 'NetworkAcls[0].Entries'
원인 4: VPC CNI SNAT 설정/동작 때문에 기대와 다른 경로로 나감
EKS 기본 CNI(amazon-vpc-cni)는 Pod IP를 VPC IP로 직접 할당합니다. 이때 egress는 크게 두 방식이 섞입니다.
- 노드 프라이머리 ENI IP로 SNAT되어 나가거나
- 특정 조건에서 Pod IP가 그대로 나가도록 구성되거나(커스텀/고급 설정)
대표적으로 아래 설정이 문제를 만들 수 있습니다.
AWS_VPC_K8S_CNI_EXTERNALSNAT=true- 노드에서 SNAT를 하지 않으니, 외부로 나가기 위한 별도 장치(NAT 인스턴스/방화벽/egress GW)가 필요
AWS_VPC_K8S_CNI_RANDOMIZESNAT=prng등으로 인해 방화벽 정책과 충돌
CNI 설정 확인
kubectl -n kube-system get ds aws-node -o jsonpath='{.spec.template.spec.containers[0].env}' | jq
EXTERNALSNAT가 켜져 있다면, “NAT Gateway만 있으면 되겠지”가 성립하지 않을 수 있습니다. 이 경우 조직의 egress 아키텍처(방화벽, TGW, egress VPC)와 함께 설계를 재확인해야 합니다.
원인 5: DNS 경로 문제(CoreDNS는 정상인데 VPC Resolver로 못 감)
DNS가 실패하는 경우는 단순히 CoreDNS 장애뿐 아니라, CoreDNS가 VPC Resolver(보통 VPC+2)로 질의 전달을 못하는 라우팅/보안 문제일 수 있습니다.
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에서 resolver로 직접 테스트
VPC DNS는 보통 VPC_CIDR의 .2 주소입니다(예: 10.0.0.2). Pod에서 해당 IP로 UDP/TCP 53이 되는지 확인합니다.
kubectl exec -it deploy/myapp -- sh -c 'apk add --no-cache bind-tools >/dev/null 2>&1 || true; dig @10.0.0.2 google.com +time=2 +tries=1'
- 여기서 타임아웃이면: 노드 SG/NACL에서 53 차단, 또는 라우팅/방화벽 경유 설계 문제일 수 있습니다.
원인 6: Kubernetes NetworkPolicy(또는 CNI 정책 엔진)로 egress 차단
EKS 기본 VPC CNI만 쓰면 NetworkPolicy는 기본적으로 enforcement가 약하지만, Calico/Cilium 등을 붙였거나 AWS Network Policy 기능을 사용하면 Pod 레벨 egress가 정책으로 막힐 수 있습니다.
NetworkPolicy 빠른 탐색
kubectl get networkpolicy -A
kubectl describe networkpolicy -n myns mypolicy
특히 policyTypes: [Egress]가 있고 egress: 규칙이 비어 있거나(deny-all), 특정 CIDR/포트만 허용하면 외부 인터넷은 막힙니다.
원인 7: VPC Endpoint/PrivateLink 환경에서 “인터넷은 막고 AWS만 허용”
보안 요구로 NAT를 없애고, S3/ECR/STS/CloudWatch 등만 VPC Endpoint로 붙이는 구성이 있습니다. 이 경우 증상은 이렇게 보입니다.
aws sts get-caller-identity는 됨 (엔드포인트가 있음)curl https://google.com은 안 됨 (NAT/IGW 경로가 없음)
이건 장애가 아니라 의도된 설계일 수 있으니, 요구사항(외부 API 호출 필요 여부)을 먼저 확인해야 합니다.
IRSA/STS 호출이 타임아웃이라면 엔드포인트/라우팅/보안 문제로도 이어지는데, 관련해서는 EKS IRSA에서 AssumeRoleWithWebIdentity 0s 타임아웃 해결도 참고할 만합니다.
실전 디버깅 루틴(15분 안에 원인 좁히기)
아래 순서로 보면 “어디서 막히는지”가 빨리 드러납니다.
1) 같은 노드에서 재현되는지 확인
Pod가 올라간 노드를 찾습니다.
kubectl get pod -n myns -o wide
가능하면 해당 노드에 임시 디버그 Pod를 붙여 동일 테스트를 합니다.
kubectl run -it netshoot --rm \
--image=nicolaka/netshoot \
--overrides='{"spec":{"nodeSelector":{"kubernetes.io/hostname":"NODE_NAME"}}}' \
-- bash
# inside
curl -I https://1.1.1.1 --connect-timeout 3
nslookup google.com
2) 라우팅 테이블: 프라이빗(0/0 -> NAT) 확인
프라이빗 서브넷의 기본 경로가 NAT로 가는지 먼저 확인합니다.
3) NAT 상태와 퍼블릭 서브넷의 IGW 경로 확인
NAT가 available인지, 퍼블릭 RT가 IGW로 나가는지 확인합니다.
4) 노드 SG egress 확인
특히 443/53이 제한되어 있지 않은지 확인합니다.
5) NACL에서 ephemeral port 포함해 왕복 허용 확인
stateless 특성 때문에 “리턴 트래픽”이 막히는지 확인합니다.
6) NetworkPolicy/서비스메시/프록시 확인
deny-all egress 정책, 사이드카 프록시, 기업 프록시 환경변수 등을 확인합니다.
자주 나오는 케이스별 결론
- 프라이빗 서브넷인데 NAT 없음: 인터넷 egress는 원천적으로 불가. NAT 추가 또는 VPC Endpoint로 요구사항 재정의.
- NAT는 있는데 프라이빗 RT에 0/0가 없음: egress 타임아웃. 라우팅 연결(association)부터 재점검.
- SG egress 제한: 특정 포트만 실패(대개 443). SG 정책 수정 또는 egress 프록시/방화벽 경유.
- NACL ephemeral port 차단: SYN은 나가는데 응답이 안 옴(타임아웃). NACL 양방향 규칙 수정.
- EXTERNALSNAT=true: NAT만으로 해결 안 될 수 있음. egress 아키텍처(방화벽/NAT 인스턴스)와 정합성 확인.
마무리: “Pod는 정상”은 네트워크 정상과 다르다
Kubernetes 관점에서 Pod가 Running이라는 건 컨테이너 프로세스가 살아있다는 뜻이지, VPC의 egress 경로가 열려 있다는 보장은 아닙니다. EKS egress 장애는 대부분
- 라우팅(0/0), 2) NAT/IGW, 3) SG/NACL, 4) CNI SNAT, 5) 정책(NetworkPolicy)
의 교차점에서 터집니다. 위 체크리스트대로 계층적으로 확인하면, “무엇이 막고 있는지”를 재현 가능한 증거(명령 출력)로 남기면서 빠르게 해결할 수 있습니다.