Published on

EKS Pod→S3 504 타임아웃 - VPC 엔드포인트·NAT·DNS 진단

Authors

서버리스나 외부 API가 아니라 EKS Pod → S3 단방향 egress에서만 504가 뜨면, 애플리케이션 로직보다 네트워크 경로/이름해석/엔드포인트 선택이 먼저 의심 대상입니다. 특히 S3는 같은 “S3 호출”이라도 s3.<region>.amazonaws.com(퍼블릭) vs bucket.s3.<region>.amazonaws.com(버킷 엔드포인트) vs VPC Gateway Endpoint 경유 등으로 실제 경로가 갈라지기 때문에, 관측 없이 감으로 고치면 시간이 오래 걸립니다.

이 글은 “Pod에서 S3 GET/PUT이 간헐 또는 지속적으로 504(혹은 client timeout)로 실패”하는 상황을 가정하고, VPC 엔드포인트·NAT·DNS를 축으로 빠르게 원인을 분기하는 방법을 다룹니다.

> 참고: 504가 아니라 403 AccessDenied라면 권한/버킷 정책 쪽이 우선입니다. 이 경우는 아래 글이 더 적합합니다: EKS Pod에서 S3 403 AccessDenied 원인 10가지

1) 504의 의미부터 정리: “누가” 504를 주는가

S3 자체는 HTTP 504를 거의 “직접” 주지 않습니다. EKS에서 보게 되는 504는 대개 다음 중 하나입니다.

  • 클라이언트(애플리케이션/SDK/프록시)가 자체 타임아웃으로 504처럼 래핑
  • Envoy/사내 프록시/egress gateway가 upstream timeout으로 504 반환
  • NAT Gateway/네트워크 경로 문제로 SYN/ACK 또는 TLS handshake가 지연되어 클라이언트가 타임아웃

따라서 첫 단계는 “S3가 504를 준다”로 가정하지 말고, 어느 홉에서 멈추는지를 확인하는 것입니다.

2) 빠른 재현/관측: Pod 안에서 DNS→TCP→TLS→HTTP 순으로 확인

문제 Pod(또는 동일 노드/서브넷에 스케줄링한 디버그 Pod)에서 아래 순서로 확인합니다.

2.1 디버그 Pod 띄우기

kubectl run -it --rm netdebug \
  --image=public.ecr.aws/amazonlinux/amazonlinux:2023 \
  --restart=Never -- bash

# 도구 설치
dnf -y install bind-utils curl openssl iproute tcpdump

2.2 DNS 해석 확인 (가장 흔한 첫 단추)

# S3 regional endpoint
nslookup s3.ap-northeast-2.amazonaws.com

# 버킷 스타일(가상 호스팅) - 실제 사용하는 호스트로 확인
nslookup my-bucket.s3.ap-northeast-2.amazonaws.com
  • 여기서 SERVFAIL/NXDOMAIN/간헐 실패가 보이면 CoreDNS/노드 DNS 경로부터 정리해야 합니다.
  • 특히 “간헐적 504”는 실제로는 간헐적 DNS 실패 → SDK 재시도 지연 → timeout으로 보이는 경우가 많습니다.

CoreDNS 이슈가 의심되면 다음 글의 체크리스트가 바로 도움이 됩니다: EKS CoreDNS SERVFAIL·NXDOMAIN 간헐 해결 9가지

2.3 라우팅/출구 확인 (NAT인지, VPC Endpoint인지)

ip route

# 현재 Pod가 어떤 DNS 서버를 쓰는지
cat /etc/resolv.conf
  • Pod가 있는 서브넷이 프라이빗 서브넷이면 일반적으로 인터넷 egress는 NAT GW를 탑니다.
  • S3를 VPC Gateway Endpoint로 붙였다면, S3 트래픽은 NAT를 타지 않고 VPC 내부 라우트로 빠져야 합니다.

2.4 실제 연결이 어디서 멈추는지: TCP/TLS/HTTP

# TCP 443 연결 확인
curl -v --connect-timeout 3 https://s3.ap-northeast-2.amazonaws.com/ -o /dev/null

# TLS 핸드셰이크만 확인
openssl s_client -connect s3.ap-northeast-2.amazonaws.com:443 -servername s3.ap-northeast-2.amazonaws.com -brief

관측 포인트:

  • Could not resolve host → DNS
  • Connection timed out(SYN 재전송) → 라우팅/NAT/보안그룹/NACL
  • TLS handshake에서 멈춤 → MTU/프록시/중간장비/네트워크 품질
  • HTTP 요청은 가는데 응답이 느림 → S3 자체보다는 대개 egress 경로 혼잡/커넥션 추적/프록시 타임아웃

3) S3 VPC 엔드포인트부터 점검: Gateway vs Interface 혼동이 잦다

S3는 전형적으로 Gateway VPC Endpoint(com.amazonaws.<region>.s3)를 사용합니다.

3.1 Gateway Endpoint의 핵심 체크 3가지

  1. Endpoint가 붙은 VPC가 맞는가
  2. Endpoint route table에 Pod 서브넷의 라우트 테이블이 연결되어 있는가
  3. S3 Prefix List로 향하는 라우트가 생겼는가

확인 방법(예: AWS CLI):

aws ec2 describe-vpc-endpoints \
  --filters Name=service-name,Values=com.amazonaws.ap-northeast-2.s3 \
  --query 'VpcEndpoints[].{Id:VpcEndpointId,State:State,RouteTables:RouteTableIds,Policy:PolicyDocument}'

aws ec2 describe-route-tables \
  --route-table-ids rtb-xxxxxxxx \
  --query 'RouteTables[].Routes'

정상이라면 RouteTable에 아래와 유사한 엔트리가 있어야 합니다.

  • Destination: pl-xxxx(S3 prefix list)
  • Target: vpce-xxxx(gateway endpoint)

3.2 “엔드포인트는 만들었는데 여전히 NAT를 탐” 패턴

다음 중 하나면 S3 트래픽이 계속 NAT로 나가며, NAT 병목/고장 시 504로 이어질 수 있습니다.

  • Pod가 있는 서브넷의 라우트 테이블이 엔드포인트에 연결되지 않음
  • 같은 VPC라도 다른 라우트 테이블을 쓰는 서브넷에 노드가 있음(노드 그룹 혼재)
  • S3 호출이 리전이 다름(예: ap-northeast-2에서 실행하면서 us-east-1 버킷/엔드포인트로 호출)
  • SDK가 S3 Transfer Acceleration 또는 특정 커스텀 endpoint를 사용(게이트웨이 엔드포인트 경유가 깨짐)

3.3 Interface Endpoint(PrivateLink)를 쓰는 경우의 함정

S3 자체는 보통 Gateway Endpoint로 충분하지만, 조직 정책상 인터페이스 엔드포인트 + Private DNS를 얹는 케이스가 있습니다.

  • Interface Endpoint는 보안 그룹이 붙습니다. 443 인바운드가 막혀 있으면 타임아웃이 납니다.
  • Private DNS가 켜져 있으면 s3.<region>.amazonaws.com프라이빗 IP로 해석됩니다. 이때 라우팅/SG가 맞지 않으면 “인터넷은 되는데 S3만 504” 같은 현상이 납니다.

확인:

aws ec2 describe-vpc-endpoints \
  --filters Name=service-name,Values=com.amazonaws.ap-northeast-2.s3 \
  --query 'VpcEndpoints[].{Id:VpcEndpointId,Type:VpcEndpointType,PrivateDns:PrivateDnsEnabled,Subnets:SubnetIds,SGs:Groups}'

4) NAT 경로 진단: “S3만” 느린 게 아니라 “NAT가” 느린 경우

Gateway Endpoint가 없거나(혹은 미적용) S3가 NAT를 타면, NAT 병목/장애가 곧 S3 타임아웃으로 보입니다.

4.1 NAT가 의심되는 전형적 증상

  • 특정 AZ의 노드에서만 504
  • 특정 시간대(트래픽 피크)만 504
  • S3뿐 아니라 STS, ECR, CloudWatch 등 다른 AWS 퍼블릭 엔드포인트도 간헐 지연

4.2 확인 체크리스트

  • 노드/Pod가 속한 프라이빗 서브넷의 default route(0.0.0.0/0)가 NAT GW를 가리키는지
  • NAT GW가 있는 퍼블릭 서브넷의 route가 IGW를 가리키는지
  • NAT GW의 Elastic IP가 정상인지(고갈/차단 이슈)
  • NACL에서 ephemeral port(1024-65535) 왕복이 허용되는지

라우트 확인:

aws ec2 describe-route-tables --route-table-ids rtb-xxxx \
  --query 'RouteTables[].Routes'

4.3 conntrack 포화로 “새 연결만” 타임아웃 나는 케이스

NAT/노드 커널 conntrack 테이블이 포화되면, 기존 연결은 살아있는데 새로 여는 TCP 연결이 드랍되어 타임아웃처럼 보일 수 있습니다. 대량 병렬 업로드/다운로드, 짧은 keep-alive, 과도한 재시도가 겹치면 잘 터집니다.

이 경우는 아래 글의 진단/튜닝이 그대로 적용됩니다: EKS conntrack 테이블 포화로 연결 끊김 해결법

5) DNS가 “정상처럼 보이는데” S3만 504: Private DNS/서치도메인/캐시를 의심

DNS가 완전히 죽으면 바로 티가 나지만, 더 골치 아픈 건 부분적으로만 잘못된 DNS입니다.

5.1 Private DNS로 인해 S3가 프라이빗 IP로 해석되는지 확인

# dig로 A 레코드 확인
dig +short s3.ap-northeast-2.amazonaws.com

# 결과가 RFC1918(10./172.16/192.168) 또는 VPC 대역이면 Private DNS 경유 가능성
  • 의도적으로 Interface Endpoint + Private DNS를 쓰는 구성이라면 OK
  • 의도치 않게 켜져 있거나, SG/NACL이 받쳐주지 않으면 타임아웃

5.2 CoreDNS 캐시/업스트림 문제로 특정 도메인만 지연

CoreDNS가 업스트림(VPC resolver)과 통신이 불안하면, s3.<region>.amazonaws.com 같은 특정 도메인에서만 지연이 누적될 수 있습니다. 이때 애플리케이션은 “S3가 느리다”고 착각합니다.

  • CoreDNS 로그에서 timeout/SERVFAIL 패턴 확인
  • kubectl -n kube-system logs deploy/coredns 확인

6) 실전 분기표: 10분 안에 원인 좁히기

아래 질문에 “예/아니오”로 답하면 대부분 갈라집니다.

  1. nslookup s3.<region>.amazonaws.com이 실패하거나 1초 이상 흔들리나?
  • 예 → DNS/CoreDNS부터
  • 아니오 → 2로
  1. curl -v https://s3.<region>.amazonaws.com에서 connect timeout인가?
  • 예 → 라우팅/NAT/SG/NACL/엔드포인트
  • 아니오 → 3로
  1. TLS handshake에서 멈추나?
  • 예 → MTU/프록시/중간장비/네트워크 품질, 또는 인터페이스 엔드포인트 SG
  • 아니오 → 4로
  1. 특정 AZ/특정 노드에서만 재현되나?
  • 예 → 해당 AZ NAT/라우트 테이블/엔드포인트 연결 누락 가능성 큼
  • 아니오 → conntrack/프록시 타임아웃/애플리케이션 커넥션 재사용 문제 확인

7) 애플리케이션 레벨에서 “타임아웃을 더 키우는” 실수 방지

네트워크를 고치는 게 우선이지만, 504가 터질 때 장애 전파를 막기 위한 최소한의 방어도 필요합니다.

7.1 AWS SDK 재시도/타임아웃을 합리적으로 설정(예: Python boto3)

import boto3
from botocore.config import Config

config = Config(
    retries={"max_attempts": 5, "mode": "adaptive"},
    connect_timeout=3,
    read_timeout=30,
)

s3 = boto3.client("s3", config=config)

# 예: 단순 HEAD로 연결성 체크
resp = s3.head_bucket(Bucket="my-bucket")
print(resp["ResponseMetadata"]["HTTPStatusCode"])

포인트:

  • connect timeout은 짧게(경로 이상을 빨리 드러냄)
  • read timeout은 워크로드(대용량 다운로드/업로드)에 맞게
  • 재시도는 무한이 아니라 상한을 두고, 실패 시 빠르게 에러를 표면화

7.2 프록시/사이드카(Envoy 등)가 있다면 upstream timeout 확인

서비스 메시/egress gateway를 쓰는 환경에서는 “S3 504”가 사실 Envoy의 upstream request timeout일 수 있습니다. 이 경우 Envoy 로그/메트릭에서 upstream connect failure, timeout 카운터를 확인해야 합니다.

8) 자주 나오는 해결 조합(정답 패턴)

8.1 가장 흔한 정답: S3 Gateway Endpoint + 라우트 테이블 연결 누락 수정

  • S3 Gateway Endpoint 생성
  • 모든 프라이빗 서브넷의 라우트 테이블을 endpoint에 연결
  • 엔드포인트 정책이 버킷/프리픽스를 막고 있지 않은지 확인

8.2 두 번째로 흔한 정답: NAT 단일 장애점 제거

  • AZ별 NAT GW 분리(프라이빗 서브넷도 AZ별로 default route 분리)
  • NAT 포화 시 스케일(아키텍처적으로는 엔드포인트 사용이 더 근본적)

8.3 의외로 자주 나오는 정답: Private DNS/Interface Endpoint SG 정리

  • S3 Interface Endpoint를 쓴다면 SG에서 노드/Pod CIDR → 443 허용
  • Private DNS가 의도치 않게 켜져 있으면 비활성화하거나, 라우팅/SG를 맞춤

9) 마무리: “S3 504”는 S3 문제가 아닐 때가 더 많다

EKS에서 Pod→S3 504는 대부분 다음 3갈래로 귀결됩니다.

  • VPC Endpoint 적용 실패(라우트 테이블/엔드포인트 타입/정책)
  • NAT 경로 병목 또는 AZ 편향
  • DNS(CoreDNS/VPC resolver/Private DNS)로 인한 잘못된 목적지 또는 지연

핵심은 “S3 호출”을 하나로 뭉뚱그리지 말고, **DNS 결과(어디로 가는지) → 라우팅(어떤 출구인지) → TCP/TLS(어디서 멈추는지)**를 순서대로 확인하는 것입니다. 이 3가지만 습관화하면, 재시도/타임아웃만 늘리며 시간을 태우는 일을 크게 줄일 수 있습니다.