Published on

EKS에서 Pod DNS는 되는데 외부 HTTPS만 실패할 때

Authors

서버리스가 아닌 EKS에서 DNS 조회는 되는데 외부 HTTPS만 실패하는 상황은 생각보다 흔합니다. nslookup google.com은 잘 되는데 curl https://google.com이 타임아웃/핸드셰이크 실패/리셋으로 끝나면, 문제는 DNS가 아니라 TCP/443 egress 경로 혹은 TLS 계층에 있습니다.

이 글은 “무엇부터 확인하면 가장 빨리 좁혀지나”에 초점을 둔 실전 트러블슈팅 가이드입니다.

증상 패턴 정리: DNS 성공 ≠ HTTPS 성공

먼저 증상을 로그/에러 문자열로 분류하면 원인 후보가 크게 줄어듭니다.

  • Could not resolve host → DNS 문제(이번 주제와 다름)
  • connect timeout, Operation timed out → 라우팅/NAT/SG/NACL/프록시/네트워크 정책
  • TLS handshake timeout → MTU/패킷 드롭, 프록시, SNI, 중간 장비, 방화벽 DPI, 오버로드
  • SSL: certificate verify failed → CA 번들/신뢰체인/프록시 MITM
  • connection reset by peer, EOF, server disconnected → 프록시/중간장비/서버측 정책/ALPN/HTTP2 등

TLS 타임아웃 계열은 원인이 다양하므로, 별도 체크리스트도 함께 참고하면 좋습니다: EKS TLS handshake timeout 원인·해결 9가지

1) 재현을 표준화: 디버그용 Pod에서 계층별 확인

애플리케이션 컨테이너 안에서만 재현하면 원인(라이브러리/CA/프록시/네트워크)을 분리하기 어렵습니다. 먼저 네트워크 도구가 있는 Pod를 띄워 DNS → TCP → TLS → HTTP 순으로 확인합니다.

디버그 Pod 실행

kubectl run netshoot -it --rm \
  --image=nicolaka/netshoot \
  --restart=Never -- bash

(1) DNS 확인

nslookup example.com
# 또는
DIG=dig; $DIG +short example.com

(2) TCP 443 연결 확인(핵심)

# TCP 레벨에서 443 포트가 열리는지
nc -vz example.com 443

# 더 확실히: IP로 직접 테스트(네임 해석 영향 제거)
IP=$(dig +short example.com | head -n1)
nc -vz $IP 443
  • nc: timed out이면 NAT/라우팅/보안그룹/NACL/네트워크 정책 쪽으로 거의 확정입니다.

(3) TLS 핸드셰이크 확인(SNI 포함)

# SNI를 포함한 핸드셰이크
openssl s_client -connect example.com:443 -servername example.com -brief

# IP로 접속하되 SNI는 도메인으로
openssl s_client -connect ${IP}:443 -servername example.com -brief
  • -servername(SNI)가 없으면 일부 CDN/가상호스팅은 정상 인증서를 주지 않습니다.

(4) HTTP 요청 확인

curl -Iv https://example.com --connect-timeout 5 --max-time 15

여기까지 결과를 가지고 아래 원인별 섹션으로 내려가면 됩니다.

2) 가장 흔한 원인: Private Subnet인데 NAT Gateway/라우팅이 없음

EKS 노드(또는 Pod가 붙는 ENI)가 Private Subnet에 있고, 외부 인터넷으로 나가려면 보통 NAT Gateway가 필요합니다.

  • DNS는 CoreDNS가 VPC 내부 Resolver(169.254.169.253 등)로 질의하므로 되지만
  • HTTPS는 인터넷으로 나가야 하므로 NAT/IGW 경로가 없으면 실패합니다.

확인 포인트

  1. 노드가 속한 서브넷의 라우트 테이블에서
    • 0.0.0.0/0 -> nat-xxxx (Private Subnet)
    • 또는 0.0.0.0/0 -> igw-xxxx (Public Subnet)
  2. NAT Gateway가 있는 Public Subnet은
    • 0.0.0.0/0 -> igw-xxxx
    • NAT에 Elastic IP가 연결되어 있음

빠른 판별 방법

  • 디버그 Pod에서 nc -vz example.com 443타임아웃
  • 같은 클러스터에서 Public Subnet 노드로 스케줄링했을 때는 성공

해결

  • 워커 노드가 Private Subnet이면 NAT Gateway(가용영역별 권장)를 구성하고
  • 해당 Private Subnet 라우트 테이블의 기본 경로를 NAT로 지정합니다.

3) 보안그룹(Security Group) Egress 또는 NACL이 443을 막음

EKS 노드 보안그룹은 기본적으로 egress가 열려있지만, 조직 정책으로 egress를 제한하는 경우가 많습니다.

확인 포인트

  • Node SG egressTCP 443 또는 All traffic이 허용되어 있는가
  • NACL에서 ephemeral port(대략 1024-65535) 반환 트래픽이 허용되는가
    • NAT/인터넷 통신은 “나가는 포트/들어오는 응답 포트”가 함께 열려야 합니다.

증상 힌트

  • nc -vz가 즉시 refused 또는 특정 네트워크 에러
  • 같은 VPC 내 다른 목적지(예: VPC 엔드포인트)만 되고 인터넷만 안 됨

해결

  • SG egress에 0.0.0.0/0:443 허용(또는 사내 egress CIDR/프록시로 제한)
  • NACL inbound/outbound에 ephemeral port 범위를 포함

4) HTTP(S) 프록시 환경: NO_PROXY 누락으로 내부/외부가 꼬임

기업망/보안망에서는 Pod가 직접 인터넷으로 나가지 못하고 프록시를 통해서만 나가야 하는 구성이 있습니다.

이때 DNS는 되지만 HTTPS가 실패하는 전형적인 원인이:

  • HTTPS_PROXY는 설정됐는데 프록시가 443 CONNECT를 허용하지 않음
  • NO_PROXY가 누락되어 Kubernetes 내부 도메인까지 프록시로 보내며 오작동
  • 프록시가 TLS MITM을 하면서 사내 CA를 신뢰하지 않아 인증서 검증 실패

확인

env | egrep -i 'http_proxy|https_proxy|no_proxy'

curl -Iv https://example.com --proxy $HTTPS_PROXY
curl -Iv https://example.com --noproxy '*'

권장 NO_PROXY 예시

NO_PROXY=localhost,127.0.0.1,.svc,.cluster.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16

해결

  • 프록시 정책에 맞춰 HTTP(S)_PROXY/NO_PROXY를 정확히 설정
  • 사내 MITM 프록시라면 컨테이너에 사내 Root CA를 배포

5) MTU/PMTUD 이슈: DNS는 되는데 TLS에서 멈춘다

DNS 패킷은 작아서 통과하지만, TLS 핸드셰이크/인증서 체인/ALPN 협상은 패킷이 커질 수 있습니다. VPC CNI, 터널링, 방화벽 장비 조합에서 MTU 불일치가 있으면 “TLS handshake timeout”이 발생하기 쉽습니다.

빠른 진단

  • nc -vz host 443는 성공
  • openssl s_client에서 멈추거나 handshake timeout

패킷 크기 테스트(대략적)

# DF(단편화 금지)로 MTU 추정 (ICMP 허용 환경에서만 유효)
ping -M do -s 1472 1.1.1.1
ping -M do -s 1372 1.1.1.1

해결 방향

  • 노드/ENI/네트워크 경로에서 MTU를 일관되게 맞추기
  • CNI 설정, 터널/오버레이 사용 여부 점검

세부 원인과 조치 항목은 위에서 링크한 TLS 타임아웃 글이 도움이 됩니다: EKS TLS handshake timeout 원인·해결 9가지

6) 컨테이너 CA 번들 문제: curl은 되는데 앱 SDK만 실패하는 경우도

운영 중 가장 낚이기 쉬운 케이스가 “Pod에서 curl은 되는데 애플리케이션(파이썬/노드/자바)만 HTTPS 실패”입니다. 이때는 네트워크가 아니라 런타임의 CA store가 오래됐거나 비어있는 경우가 많습니다(특히 distroless/minimal 이미지).

증상

  • curl https://api... 성공
  • 파이썬 httpx, requests, 자바 PKIX path building failed 등 인증서 검증 실패

파이썬 예시(재현 코드)

import httpx

url = "https://example.com"

try:
    r = httpx.get(url, timeout=10)
    print(r.status_code)
except Exception as e:
    print("FAILED:", repr(e))

RemoteProtocolError나 연결 종료가 섞여 보이면, 네트워크/프록시 문제 가능성도 있으니 아래 글의 분류가 유용합니다: Python httpx RemoteProtocolError 서버 끊김 원인과 해결

해결

  • Debian/Ubuntu 계열: ca-certificates 패키지 설치 및 갱신
  • Alpine: apk add ca-certificates && update-ca-certificates
  • 사내 프록시 MITM이면 사내 Root CA를 /usr/local/share/ca-certificates에 추가 후 업데이트

7) NetworkPolicy로 egress 443이 차단됨(Calico/Cilium 등)

EKS 기본 VPC CNI만으로는 NetworkPolicy가 동작하지 않지만, Calico/Cilium 등을 설치한 경우 Pod egress가 정책으로 막힐 수 있습니다.

확인

kubectl get networkpolicy -A
kubectl describe networkpolicy -n <ns> <name>

해결

  • 대상 네임스페이스의 egress 정책에 to: 0.0.0.0/0 + ports: 443 허용
  • 또는 프록시/egress gateway로만 나가도록 설계했다면 해당 경로만 허용

8) VPC 엔드포인트(PrivateLink)와 DNS 옵션 충돌

S3/ECR/STS 같은 AWS API를 VPC 엔드포인트로 붙였는데, 외부 HTTPS(공용 인터넷)도 동시에 써야 하는 환경에서 DNS 옵션이 꼬이면 “어떤 도메인은 내부로 가고 어떤 도메인은 외부로 가는” 혼합 상태가 됩니다.

  • enableDnsHostnames, enableDnsSupport가 꺼져 있거나
  • 엔드포인트의 Private DNS 설정이 의도와 다르게 켜져 있어 특정 도메인이 내부로만 해석

이 케이스는 “외부 전체”가 아니라 “특정 도메인만” 실패하는 형태로 나타나는 경우가 많습니다.

9) 실전 체크리스트: 10분 안에 원인 좁히기

아래 순서로 하면 불필요한 추측을 줄일 수 있습니다.

  1. 디버그 Pod에서 nslookup 성공 확인
  2. nc -vz host 443
    • 타임아웃이면: NAT/라우팅/SG/NACL/NetworkPolicy부터
  3. openssl s_client -connect host:443 -servername host
    • 여기서 멈추면: MTU/중간장비/프록시/DPI 의심
  4. curl -Iv https://host
    • certificate verify failed면: CA 번들/사내 CA
  5. 애플리케이션만 실패하면
    • 런타임 CA store, HTTP/2 설정, 프록시 환경변수, 타임아웃 값을 확인

10) 마무리: “DNS는 된다”는 단서의 의미

DNS가 된다는 건 CoreDNS와 VPC Resolver까지의 경로가 정상이라는 뜻일 뿐, 인터넷으로 나가는 egress 경로가 정상이라는 뜻은 아닙니다. EKS에서 외부 HTTPS만 실패하면 대부분은 다음 셋 중 하나로 수렴합니다.

  • NAT/라우팅 누락(Private Subnet 기본 경로 없음)
  • 보안그룹/NACL/NetworkPolicy egress 차단
  • TLS 계층 이슈(MTU/프록시/CA 번들/SNI)

위의 계층별 테스트(ncopensslcurl)를 표준 루틴으로 만들어두면, 비슷한 장애를 만났을 때 재현과 원인 분리가 훨씬 빨라집니다.