Published on

EKS에서 IPv6만 통신 실패 - CNI·SG·DNS 점검

Authors

EKS에서 IPv4는 정상인데 IPv6만 통신이 실패하는 케이스는 생각보다 자주 나옵니다. 특히 듀얼스택(IPv4/IPv6)로 클러스터를 구성했거나, 외부 서비스가 IPv6를 우선 사용하도록(예: Happy Eyeballs, 라이브러리 기본값) 바뀐 뒤부터 “간헐적 타임아웃”처럼 보이기도 합니다.

이 글은 원인을 크게 CNI(aws-vpc-cni), Security Group/NACL/라우팅, DNS(CoreDNS/AAAA 레코드) 세 축으로 나누어, “IPv6만 안 되는” 상황을 빠르게 좁혀가는 실전 점검 순서를 제공합니다.

> Ingress/ALB에서 5xx로 보이는 경우도 많습니다. 증상이 502/503/504로 나타난다면 아래 글도 함께 보면 원인 분리가 빨라집니다. > - EKS Ingress 503인데 Pod 정상일 때 점검 가이드 > - EKS ALB Ingress 504인데 Pod는 정상일 때

1) 먼저 “IPv6만 실패”를 확실히 재현/분리하기

1-1. Pod 내부에서 IPv4/IPv6를 각각 강제 테스트

Pod에 들어가서 curl -4, curl -6로 강제하면, DNS/라우팅/SG 문제를 더 명확히 분리할 수 있습니다.

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

# DNS가 주는 A/AAAA 확인
dig A example.com +short
dig AAAA example.com +short

# IPv4 강제
curl -4 -v https://example.com --max-time 5

# IPv6 강제
curl -6 -v https://example.com --max-time 5

관찰 포인트:

  • dig AAAA는 나오는데 curl -6만 타임아웃 → IPv6 경로/보안/라우팅 쪽 가능성이 큼
  • dig AAAA 자체가 실패하거나 응답이 없고 curl -4만 됨 → DNS(CoreDNS/업스트림/정책) 가능성
  • curl -6에서 Network is unreachable → 노드/Pod의 IPv6 라우트/주소 할당 자체가 안 된 상태

1-2. Pod/Node IP 패밀리 확인

듀얼스택이면 Pod에 IPv6가 실제로 붙었는지부터 확인합니다.

kubectl get pod -A -o wide
kubectl describe pod <pod> | sed -n '/IPs:/,/Controlled By:/p'

# Pod 안에서
ip -br a
ip -6 route
  • IPs:에 IPv6가 없으면: CNI 설정/클러스터 IP 패밀리/서브넷 IPv6 설정부터 의심
  • IPv6는 있는데 라우트가 이상하면: 노드/서브넷/라우팅 테이블/egress 경로 점검

2) CNI(aws-vpc-cni)에서 가장 흔한 함정들

EKS 듀얼스택은 “클러스터/노드/서브넷/애드온”이 모두 맞물려야 합니다. IPv4는 기본으로 잘 되니, IPv6만 실패할 때는 CNI가 IPv6 주소를 할당했는지, IPv6 egress가 가능한지를 집중적으로 봅니다.

2-1. aws-node(aws-vpc-cni) 상태와 로그 확인

kubectl -n kube-system get ds aws-node -o wide
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
kubectl -n kube-system logs -l k8s-app=aws-node --tail=200

로그에서 자주 보는 키워드:

  • IP 할당 실패(ENI, prefix delegation, warm pool 관련)
  • IPv6 관련 오류(주소/라우트 설정 실패)
  • CNI init 단계에서 권한/IMDS 접근 문제

2-2. CNI 설정값(ConfigMap/Env) 점검

EKS 환경마다 값이 다르지만, 핵심은 “IPv6 주소를 실제로 할당/라우팅하도록 설정되어 있는가”입니다.

kubectl -n kube-system get cm aws-node -o yaml
kubectl -n kube-system describe ds aws-node | sed -n '/Environment:/,/Mounts:/p'

체크리스트:

  • 듀얼스택을 의도했는데도 Pod에 IPv6가 안 붙는다 → CNI 버전/설정, 클러스터 생성 시 IP 패밀리, 서브넷 IPv6 CIDR 연결 여부 확인
  • IPv6는 붙는데 외부로 나가기만 실패 → egress 경로(IGW/Egress-only IGW), NACL/SG, 라우팅 테이블 쪽이 더 유력

2-3. “노드 자체는 IPv6 인터넷이 되나?”를 먼저 확인

노드에 SSM이나 임시 디버그 DaemonSet으로 들어가 IPv6 egress를 확인하면, Pod 문제인지 VPC 문제인지 빠르게 갈립니다.

예: 디버그 DaemonSet(간단 버전)

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-debug
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: node-debug
  template:
    metadata:
      labels:
        app: node-debug
    spec:
      hostNetwork: true
      containers:
      - name: debug
        image: nicolaka/netshoot
        command: ["sleep", "360000"]
        securityContext:
          privileged: true
      tolerations:
      - operator: Exists

적용 후 노드 네트워크에서 테스트:

kubectl -n kube-system exec -it ds/node-debug -- bash
curl -6 -v https://example.com --max-time 5
ip -6 route
  • hostNetwork에서도 IPv6가 안 되면: CNI 이전에 VPC/서브넷/라우팅/보안 문제
  • hostNetwork는 되는데 Pod에서만 안 되면: CNI/iptables/eBPF/NetworkPolicy 등 Pod 경로 문제

3) Security Group / NACL / 라우팅: IPv6는 “별도 규칙”이 필요하다

IPv4는 열어놨는데 IPv6는 규칙이 비어 있는 경우가 매우 흔합니다. 이유는 간단합니다. AWS에서 IPv4 CIDR(예: 0.0.0.0/0) 규칙과 IPv6 CIDR(예: ::/0) 규칙은 별개입니다.

3-1. 노드 보안그룹 인바운드/아웃바운드에 IPv6가 있는지

점검 포인트:

  • 아웃바운드에 ::/0 허용이 없어서 IPv6 egress가 막힘
  • 인바운드도 IPv6를 받는 구성인데 ::/0 또는 필요한 IPv6 CIDR이 없음

CLI로 빠르게 확인(예시):

aws ec2 describe-security-groups \
  --group-ids sg-xxxxxxxx \
  --query 'SecurityGroups[0].{Out:IpPermissionsEgress,In:IpPermissions}' \
  --output json

3-2. NACL에서 IPv6 규칙 누락 여부

NACL도 IPv4/IPv6가 분리입니다.

  • IPv4 allow-all 해두고 IPv6는 기본 deny면 “IPv6만 타임아웃”이 됩니다.
aws ec2 describe-network-acls \
  --filters Name=association.subnet-id,Values=subnet-xxxx \
  --query 'NetworkAcls[0].Entries'

3-3. 라우팅 테이블: IPv6는 IGW 또는 Egress-only IGW

IPv6 egress는 보통 두 가지 패턴입니다.

  • 퍼블릭 IPv6: ::/0 -> Internet Gateway(igw)
  • 프라이빗에서 IPv6 egress만: ::/0 -> Egress-only Internet Gateway(eigw)

서브넷 라우트 확인:

aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-xxxx \
  --query 'RouteTables[0].Routes'

흔한 실수:

  • IPv4 NAT Gateway만 구성해두고 “IPv6도 NAT처럼 되겠지”라고 생각함 → IPv6는 NAT GW가 아니라 Egress-only IGW가 일반적
  • 듀얼스택인데 IPv6용 라우트가 누락됨

4) DNS(CoreDNS): AAAA 응답이 “독”이 되는 순간

IPv6만 실패하는데 애플리케이션은 “가끔만 느리다/가끔만 실패한다”로 보일 수 있습니다. 이유는 많은 클라이언트가 AAAA를 먼저 시도하거나, A/AAAA를 병렬로 시도(Happy Eyeballs)하기 때문입니다.

4-1. CoreDNS가 AAAA를 잘 반환하는지, 그리고 그 AAAA가 실제로 reachable인지

kubectl -n kube-system get deploy coredns -o wide
kubectl -n kube-system logs deploy/coredns --tail=200

# Pod에서
nslookup -type=AAAA example.com
nslookup -type=A example.com

케이스별 해석:

  • AAAA는 잘 나오는데 IPv6 경로가 막힘 → 앱은 AAAA로 붙다가 타임아웃 후 IPv4로 fallback → 지연/간헐적 장애
  • CoreDNS가 업스트림으로 IPv6 질의를 못 던짐(노드 IPv6 egress 불가) → AAAA 조회가 느리거나 실패

4-2. VPC DNS/Resolver 경로도 IPv6에 영향

EKS에서 CoreDNS는 보통 노드 네트워크를 통해 업스트림(예: VPC Resolver)로 나갑니다. 이때 노드/서브넷의 IPv6 egress가 막혀 있으면 AAAA 조회가 병목이 될 수 있습니다.

실전 팁:

  • dig AAAA가 느리면 +tries=1 +time=1로 타임아웃을 줄여 증상을 더 빨리 드러내세요.
dig AAAA example.com +tries=1 +time=1

5) Kubernetes 레이어에서 추가로 보는 것들(NetworkPolicy, kube-proxy 등)

IPv6만 막히는 NetworkPolicy는 흔하진 않지만, Cilium/Calico 등에서 정책을 엄격히 쓰는 환경이라면 충분히 가능합니다.

5-1. NetworkPolicy가 IPv6 CIDR을 누락했는지

예: egress를 0.0.0.0/0만 열어둔 정책은 IPv6에 적용되지 않습니다.

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

5-2. kube-proxy/IPVS/iptables 이슈는 “서비스 경로”에서 드러난다

Pod-to-Pod는 되는데 Service ClusterIP로만 IPv6가 실패한다면 kube-proxy 규칙/모드도 의심합니다.

kubectl -n kube-system get ds kube-proxy -o wide
kubectl -n kube-system logs ds/kube-proxy --tail=200

서비스 경로 장애로 503이 보인다면 아래 글의 “EndpointSlice/Readiness/Service 라우팅” 점검도 같이 적용해볼 만합니다.

6) 15분 트러블슈팅 플로우(현장용 체크리스트)

아래 순서로 하면 “IPv6만 실패”의 원인을 보통 15~30분 내에 좁힐 수 있습니다.

6-1. 증상 분리

  1. Pod에서 curl -4, curl -6 강제 테스트
  2. dig A/AAAA로 DNS 응답 확인

6-2. 주소/라우트 존재 확인

  1. Pod에 IPv6가 실제로 붙었는지(describe pod, ip -6 a)
  2. Pod/노드의 IPv6 라우트(ip -6 route)

6-3. 노드 네트워크로 원인 분기

  1. hostNetwork 디버그로 노드에서 curl -6 시도

6-4. AWS 네트워크 계층 점검

  1. 노드 SG egress에 ::/0 또는 필요한 IPv6 CIDR 허용
  2. NACL에 IPv6 allow 규칙 존재 확인
  3. 라우팅 테이블에 ::/0 -> igw/eigw 확인

6-5. 클러스터 애드온 점검

  1. aws-node 로그에서 IPv6 할당/라우트 설정 오류 확인
  2. CoreDNS 로그 및 AAAA 조회 지연/실패 확인

7) 자주 나오는 “정답 패턴” 5가지

  1. SG egress에 IPv6(::/0)가 없음: IPv4는 되는데 IPv6만 전부 타임아웃
  2. NACL IPv6 기본 deny: 특정 서브넷/노드에서만 IPv6 실패(존/서브넷 편차)
  3. 라우팅 테이블에 IPv6 기본 경로(::/0)가 없음: Network is unreachable 또는 즉시 실패
  4. CoreDNS는 AAAA를 주는데 IPv6 경로가 막힘: 앱이 느려지고 간헐적으로 실패(타임아웃 후 IPv4 fallback)
  5. 듀얼스택 기대했지만 Pod에 IPv6가 아예 안 붙음: CNI/클러스터 IP 패밀리/서브넷 IPv6 CIDR 연결 문제

8) 마무리: 결론은 “경로를 나눠서” 본다

IPv6 문제는 한 번에 잡으려 하면 끝이 없습니다. 대신 아래처럼 경로를 명확히 분리하면 빠릅니다.

  • DNS(AAAA) 가 문제인가? → dig AAAA, CoreDNS 로그
  • 노드 egress 가 문제인가? → hostNetwork에서 curl -6
  • Pod 경로/CNI 가 문제인가? → Pod에 IPv6 할당 여부, aws-node 로그
  • AWS 보안/라우팅 이 문제인가? → SG(::/0), NACL IPv6, ::/0 -> igw/eigw

이 순서대로 보면 “IPv4는 되는데 IPv6만 안 되는” 장애는 대부분 SG/NACL/라우팅의 IPv6 규칙 누락 또는 AAAA로 유도된 타임아웃으로 귀결됩니다. 다음 장애부터는 재현 커맨드(curl -4/-6, dig A/AAAA) 두 개만으로도 진단 시간이 크게 줄어들 겁니다.