- Published on
EKS Pod DNS는 되는데 S3만 503? 엔드포인트 정책
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS에서 흔히 겪는 이상한 증상 중 하나가 이겁니다.
- Pod 내부에서
nslookup,dig같은 DNS 조회는 잘 된다. - 다른 외부 HTTPS 호출도 대체로 잘 된다.
- 그런데 S3만 호출하면 503(Service Unavailable) 가 난다.
애플리케이션 로그에는 503만 남고, 네트워크는 살아있는 것처럼 보이니 원인 파악이 어려워집니다. 특히 S3를 VPC Endpoint(Gateway/Interface)로 붙여둔 환경에서는 “DNS는 되는데 S3만 503”이 엔드포인트 정책(Endpoint policy) 또는 경로/프록시 강제에서 발생하는 경우가 많습니다.
이 글에서는 EKS Pod 기준으로 S3 503을 빠르게 좁혀가는 방법과, 실제로 많이 터지는 S3 VPC Endpoint 정책 실수 패턴을 정리합니다.
> EKS 네트워크 진단을 더 넓게 보고 싶다면: EKS Pod는 뜨는데 트래픽 0 - NetPol·SG·CNI 10분 진단
1) 먼저 정리: S3에서의 503은 “권한 거부”가 아닐 수 있다
S3 권한 문제라면 보통 다음을 기대합니다.
403 AccessDenied(버킷 정책/IRSA/IAM)404 NoSuchBucket등
그런데 503은 보통 아래 범주에서 더 자주 나옵니다.
- 경로(라우팅) 또는 프록시 계층에서 S3로 못 나감
- VPC Endpoint 쪽에서 요청이 비정상적으로 처리됨(정책/엔드포인트 유형/리전/호스트)
- S3 멀티파트/대용량 업로드에서 중간 장비가 끊는 경우(프록시, NAT, 방화벽)
즉, “DNS는 된다”는 사실만으로는 S3 경로가 정상인지 보장하지 않습니다. DNS는 Route53 Resolver로 잘 풀리는데, 실제 트래픽은 S3 엔드포인트로 강제되거나 반대로 엔드포인트가 막혀 실패할 수 있습니다.
2) 5분 컷 1차 진단: Pod에서 curl로 S3 엔드포인트 확인
애플리케이션 SDK 대신, Pod 안에서 S3 API 엔드포인트에 직접 붙여 “어디로 연결되는지/무슨 응답인지” 확인합니다.
2.1 디버그 Pod 띄우기
kubectl run -it --rm net-debug \
--image=curlimages/curl:8.5.0 \
--restart=Never -- sh
2.2 DNS 결과와 실제 연결 IP 확인
아래는 S3 virtual-hosted–style을 가정합니다.
# 버킷명을 넣어 테스트
BUCKET=my-bucket
REGION=ap-northeast-2
# DNS 확인
nslookup ${BUCKET}.s3.${REGION}.amazonaws.com
# TLS 핸드셰이크/연결 대상 확인
curl -v --connect-timeout 5 \
https://${BUCKET}.s3.${REGION}.amazonaws.com/ \
-o /dev/null
여기서 포인트:
- 연결 IP가 사설 IP(10.x/172.16/192.168) 로 나오면 대개 Interface Endpoint(PrivateLink) 또는 내부 프록시를 타고 있을 가능성이 큽니다.
- 연결 IP가 공인 IP로 나오면 NAT/IGW 경로일 수 있습니다(혹은 DNS가 퍼블릭 S3로 해석됨).
* Connected to ...이후에 바로503이 떨어지면, 애플리케이션 권한 문제보다는 네트워크 경로/엔드포인트 정책/중간 장비 가능성이 커집니다.
3) “DNS는 되는데 S3만 503”에서 가장 흔한 원인 3가지
원인 A) S3 Gateway Endpoint 정책이 필요한 액션/리소스를 막고 있음
S3 Gateway Endpoint(라우팅 테이블에 붙는 타입)는 “VPC에서 S3로 가는 경로”를 엔드포인트로 강제합니다. 이때 Endpoint policy가 지나치게 제한적이면 특정 요청이 실패합니다.
보통 정책이 막으면 403을 기대하지만, SDK/프록시/리전 미스매치가 겹치면 애매한 503으로 보이는 케이스도 있습니다(특히 앱 로그가 원문 응답을 제대로 남기지 않을 때).
체크
- VPC 콘솔 → Endpoints →
com.amazonaws.<region>.s3→ Policy 확인 - CloudTrail의 S3 Data Events(활성화된 경우) 또는 VPC Flow Logs로 흐름 확인
“너무 빡센” 정책 예
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}
위 정책은 GetObject만 허용합니다. 하지만 실제 애플리케이션은 다음을 추가로 요구하는 경우가 많습니다.
s3:ListBucket(prefix listing)s3:PutObject,s3:AbortMultipartUpload,s3:ListMultipartUploadParts- KMS 사용 시
kms:Decrypt/Encrypt(이건 Endpoint policy가 아니라 IAM/KMS 정책이지만 함께 점검)
최소 안전 정책(예시)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:AbortMultipartUpload",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}
> 운영에서는 버킷/프리픽스 단위로 더 좁히고, 필요한 액션만 남기되 “멀티파트/리스트” 계열을 빠뜨리지 않는 것이 핵심입니다.
원인 B) S3 Interface Endpoint(PrivateLink)로 붙였는데 DNS/호스트가 꼬임
S3는 전통적으로 Gateway Endpoint가 정석이지만, 요건에 따라 Interface Endpoint를 쓰는 구성도 있습니다(정책/감사/프록시/특정 제약 등).
이때 자주 생기는 문제:
- Private DNS를 켜고/끄는 과정에서 Pod가 보는 S3 도메인이 기대와 다르게 해석됨
- 리전 엔드포인트(
s3.ap-northeast-2.amazonaws.com)와 버킷 엔드포인트(bucket.s3.ap-northeast-2.amazonaws.com)가 섞임 - 경로 스타일(path-style) 강제 사용(
s3.amazonaws.com/bucket)이 남아있어 TLS SNI/Host가 꼬임
체크
- Endpoint 타입 확인: Gateway인지 Interface인지
- Interface라면 Private DNS 설정과, 해당 VPC의 DNS 설정(EnableDnsHostnames/Support)
- 애플리케이션 SDK 설정에서
s3ForcePathStyle같은 옵션이 켜져 있는지
Node.js AWS SDK v3에서 path-style을 강제하는 예(문제 유발 가능):
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
region: "ap-northeast-2",
forcePathStyle: true // virtual-hosted가 필요한 환경에서 이게 켜져 있으면 꼬일 수 있음
});
원인 C) “S3는 반드시 엔드포인트로만” 강제했는데 라우팅/서브넷이 일부 누락
EKS는 서브넷이 여러 개이고, 노드 그룹/파드가 서로 다른 서브넷에 떠 있을 수 있습니다.
S3 Gateway Endpoint는 라우팅 테이블 단위로 붙습니다. 즉,
- 어떤 서브넷의 라우팅 테이블에는 S3 Endpoint route가 있고
- 다른 서브넷 라우팅 테이블에는 없다
이러면 “어떤 파드에서는 되고, 어떤 파드에서는 503/timeout” 같은 형태로 나타납니다.
체크
- EKS 노드가 속한 서브넷들의 Route Table에 S3 Prefix List 경로가 있는지
- VPC Endpoint → Subnets(Interface) 또는 Route Tables(Gateway)가 “전부” 포함되는지
AWS CLI로 Gateway endpoint 연결 라우팅 테이블 확인:
aws ec2 describe-vpc-endpoints \
--filters Name=service-name,Values=com.amazonaws.ap-northeast-2.s3 \
--query 'VpcEndpoints[0].{Id:VpcEndpointId,Type:VpcEndpointType,RouteTables:RouteTableIds,Policy:PolicyDocument}' \
--output json
4) 503을 “정확히” 보기: SDK 재시도/프록시 숨김을 제거하고 원문 로그 남기기
애플리케이션이 S3 SDK를 쓰면 내부적으로 재시도/추상화가 들어가서, 실제로는 403인데 앱 로그에는 503만 남는 식의 왜곡이 생길 수 있습니다.
Python(boto3)에서 디버그 로그 켜기
import boto3
import logging
boto3.set_stream_logger('botocore', level=logging.DEBUG)
s3 = boto3.client('s3', region_name='ap-northeast-2')
print(s3.list_objects_v2(Bucket='my-bucket', MaxKeys=1))
여기서 확인할 것:
- 실제 요청 URL(호스트가
bucket.s3.<region>.amazonaws.com형태인지) - 응답 헤더에
x-amz-request-id,x-amz-id-2가 있는지(진짜 S3 응답인지) - 프록시를 타는지(
HTTPS_PROXY환경변수 등)
5) 엔드포인트 정책을 설계할 때의 실전 가이드
5.1 Endpoint policy vs Bucket policy vs IAM의 역할 분리
- IAM/IRSA 정책: “누가(Principal) 무엇을(Action) 할 수 있나”의 1차 권한
- Bucket policy: 버킷 리소스 관점에서의 추가 제약(특정 VPC/VPCE만 허용 등)
- VPC Endpoint policy: “이 VPC 엔드포인트를 통해 나가는 트래픽”에 대한 필터
운영에서 흔한 실수는 Endpoint policy를 너무 강하게 걸어놓고, 장애 시 원인을 IAM/IRSA로만 의심하는 겁니다. IRSA가 꼬였을 때 전면 장애가 나기도 하니 함께 점검하세요: EKS OIDC Provider 삭제로 IRSA 전부 실패했을 때 복구
5.2 “특정 버킷만 허용” + “필수 액션 포함”을 기본으로
- 리소스는
arn:aws:s3:::bucket과arn:aws:s3:::bucket/*둘 다 필요 ListBucket류는 객체 ARN이 아니라 버킷 ARN에 걸림- 멀티파트/업로드를 쓰면 관련 액션이 추가로 필요
5.3 버킷 정책으로 VPCE 제한 시, 엔드포인트 교체/멀티계정에 주의
버킷 정책에서 아래처럼 aws:sourceVpce로 제한하면 보안은 강해지지만,
- 엔드포인트 재생성(VPCE ID 변경)
- 멀티 VPC/멀티 계정 접근
에서 장애가 날 수 있습니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOnlyFromSpecificVPCE",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": "vpce-0123456789abcdef0"
}
}
}
]
}
이 구성이 들어가 있으면, “DNS는 되는데 S3만” 특정 경로에서 실패하는 현상이 더 자주 보입니다(특히 파드가 다른 VPC/엔드포인트 경로로 나가는 경우).
6) 체크리스트: 재현 → 원인 분리 → 해결 순서
- Pod에서
curl -v로 S3 엔드포인트 직접 호출- 진짜 S3 응답인지(헤더)
- 연결 IP가 사설/공인인지
- VPC Endpoint 타입 확인
- Gateway: 라우팅 테이블 누락 여부
- Interface: Private DNS/호스트 스타일/SDK 설정
- Endpoint policy 확인
- 필요한 액션(
ListBucket, 멀티파트 등) 포함 여부 - 리소스 ARN 범위 정확성
- 필요한 액션(
- 버킷 정책의
aws:sourceVpce/aws:sourceVpc조건 확인 - IRSA/IAM 권한과 CloudTrail 이벤트로 최종 확인
네트워크/정책 이슈는 “파드 상태는 멀쩡한데 트래픽만 이상”으로 나타나는 경우가 많습니다. 그런 경우엔 네트워크 계층을 체계적으로 분해해서 보는 게 가장 빠릅니다: Kubernetes apiserver i/o timeout 원인과 해결
결론
EKS Pod에서 DNS가 정상인데 S3만 503이 나는 상황은, 단순한 권한 문제라기보다 S3 VPC Endpoint(특히 정책/라우팅/Private DNS) 구성 불일치에서 시작되는 경우가 많습니다.
핵심은 “Pod에서 실제로 어디로 붙는지”를 먼저 확인하고(curl -v, SDK 디버그), 그 다음에 Gateway/Interface 엔드포인트 유형에 맞춰 라우팅 테이블과 Endpoint policy를 점검하는 것입니다. 정책은 보안을 위해 좁히되, S3가 실제로 사용하는 액션(리스트/멀티파트)을 빠뜨리지 않도록 설계하면 같은 장애를 크게 줄일 수 있습니다.