홈/인프라/Kubernetes 네트워크 지연 진단: DNS·kube-proxy·MTU부터 eBPF까지 구간별 런북
인프라Kubernetes네트워크지연

Kubernetes 네트워크 지연 진단: DNS·kube-proxy·MTU부터 eBPF까지 구간별 런북

Kubernetes 서비스 간 지연을 DNS(ndots·5초 지연), kube-proxy 모드, 오버레이 MTU, NetworkPolicy, 사이드카 순으로 구간별 측정해 원인을 찾습니다. 증상별 판정표, 복붙 명령어, eBPF 도구(tcpretrans·Hubble)까지 정리했습니다.

Kubernetes 네트워크 지연 진단: DNS·kube-proxy·MTU부터 eBPF까지 구간별 런북
목차
  1. 30초 판정표: 증상으로 구간 좁히기
  2. 1단계: 기준선을 구간별로 측정하기
  3. 2단계: DNS 지연 확인
  4. 3단계: Service 라우팅(kube-proxy)
  5. 4단계: 오버레이와 MTU·MSS
  6. 5단계: NetworkPolicy와 CNI 데이터플레인
  7. 6단계: 서비스 메시 사이드카
  8. eBPF로 커널 안쪽 보기
  9. 측정 가능한 가설로 정리하기
  10. 자주 묻는 질문 (FAQ)

"코드도 그대로고 Pod 리소스도 여유 있는데, 서비스 간 호출만 느리다." Kubernetes에서 이런 지연은 대개 DNS 조회, Service 라우팅(kube-proxy), 오버레이와 MTU, NetworkPolicy, 사이드카 프록시 중 한 구간에서 생깁니다. 이 글은 구간을 하나씩 잘라 측정하고, 원인이 확인된 구간만 튜닝하는 순서를 정리합니다. eBPF는 그 측정과 데이터플레인 개선 양쪽에 쓰는 도구로 다룹니다.

30초 판정표: 증상으로 구간 좁히기

증상먼저 의심할 구간첫 확인 명령
첫 요청이나 새 연결만 느리고, 간헐적으로 정확히 약 5초씩 늦음DNS (CoreDNS, ndots, conntrack 경합)kubectl exec <pod> -- cat /etc/resolv.conf
Pod IP로 직접 호출하면 빠르고 Service 이름/ClusterIP로 호출하면 느림kube-proxy 모드·규칙 수kubectl -n kube-system get cm kube-proxy -o yaml | grep -E '^\s*mode:'
작은 요청은 되는데 큰 응답·TLS 핸드셰이크에서 멈추거나 재전송이 많음오버레이 MTUping -M do -s 1472 -c 3 <대상_IP>
같은 노드 Pod 간은 빠르고 다른 노드 간만 느림오버레이/캡슐화, 노드 간 경로노드 간 vs Pod 간 iperf3·RTT 비교
사이드카 주입 후 모든 호출의 P99가 올라감서비스 메시 프록시사이드카 있음/없음 같은 부하로 비교
트래픽 피크에만 연결 실패·지연, 노드 로그에 nf_conntrack: table fullconntrack 테이블dmesg | grep conntrack, sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

평균값이 아니라 P95·P99를 기준으로 보세요. 평균이 정상인데 P99만 튄다면 일부 요청이 재전송, DNS 타임아웃, 특정 노드 경로처럼 특정 구간에 걸리고 있다는 신호입니다.

1단계: 기준선을 구간별로 측정하기

추측 전에 같은 조건으로 네 가지 경로를 잽니다. 숫자가 갑자기 커지는 구간이 원인 후보입니다.

  1. 노드 ↔ 노드 (노드 IP)
  2. 같은 노드의 Pod ↔ Pod (Pod IP)
  3. 다른 노드의 Pod ↔ Pod (Pod IP)
  4. Pod → Service 이름 (DNS + ClusterIP 경유)

도구는 측정 대상에 맞게 고릅니다.

  • 처리량·재전송: iperf3. 지연 측정 도구가 아닙니다. 출력의 Retr 열이 TCP 재전송 수입니다.
    Bash
    iperf3 -s                          # 서버 Pod
    iperf3 -c <서버_IP> -t 10 -P 4      # 클라이언트 Pod: 10초, 병렬 스트림 4개
  • 요청-응답 지연: netperf -t TCP_RR 또는 실제 프로토콜의 부하 도구(HTTP라면 wrk, hey). 페이로드 크기와 반복 횟수를 고정하고 평균과 P99를 함께 기록합니다. ping은 ICMP RTT만 보여 줍니다.
  • 경로 홉별 손실: mtr -n -r -c 100 <대상_IP>. 중간 라우터는 ICMP 응답을 낮은 우선순위로 처리하는 경우가 많으므로, 마지막 홉까지 이어지는 손실·지연만 실제 문제로 봅니다.

2단계: DNS 지연 확인

Pod의 /etc/resolv.conf에는 보통 options ndots:5와 여러 search 도메인이 들어 있습니다. 점이 5개 미만인 이름(예: api.example.com)은 search 도메인을 하나씩 붙여 먼저 조회하므로, 외부 도메인 하나에 여러 번의 조회가 생길 수 있습니다.

  • 자주 호출하는 외부 도메인은 끝에 점을 붙인 FQDN(api.example.com.)으로 쓰거나, Pod의 dnsConfig.options에서 ndots를 낮춥니다. 클러스터 내부 짧은 이름 해석에 영향이 있으니 서비스별로 시험한 뒤 적용합니다.
  • 간헐적으로 정확히 약 5초씩 늦다면, 리눅스 conntrack의 UDP 경합으로 DNS 패킷 하나가 버려지고 resolver 기본 타임아웃(5초) 뒤 재시도하는 경우가 잘 알려져 있습니다. 노드별 DNS 캐시(NodeLocal DNSCache)를 쓰면 이 경로를 줄일 수 있습니다.
  • CoreDNS Pod의 CPU 스로틀링과 Pod 수가 트래픽에 비해 부족하지 않은지도 함께 봅니다.

3단계: Service 라우팅(kube-proxy)

ClusterIP 트래픽은 kube-proxy가 만든 규칙을 탑니다. 모드에 따라 비용이 다릅니다.

모드특징비고
iptablesService·엔드포인트가 많아질수록 규칙이 늘고 규칙 갱신도 느려짐많은 클러스터의 기본값
nftables더 나은 성능을 내도록 설계, Kubernetes 1.33에서 GA공식 문서가 iptables의 대안으로 권장
ipvs해시 기반 조회Kubernetes 1.35부터 deprecated
kube-proxy 대체(eBPF)Cilium 등이 eBPF로 Service를 직접 처리CNI 기능으로 제공
Bash
kubectl -n kube-system get cm kube-proxy -o yaml | grep -E '^\s*mode:'   # kubeadm 기준, 비어 있으면 iptables

Pod IP 직접 호출과 Service 경유 호출의 지연 차이가 작다면 이 구간은 원인이 아닙니다. 모드 변경은 클러스터 전체에 영향을 주므로, 1단계 측정에서 이 구간이 원인으로 확인됐을 때만 검토합니다. 자료: Kubernetes: Virtual IPs and Service Proxies.

4단계: 오버레이와 MTU·MSS

VXLAN, IP-in-IP, WireGuard 같은 오버레이는 패킷마다 터널 헤더를 덧붙이므로, Pod MTU는 노드 경로 MTU보다 그 헤더만큼 작아야 합니다. Pod MTU가 너무 크면 큰 패킷이 단편화되거나 Path MTU Discovery에 의존하게 되고, 중간 장비가 ICMP "Fragmentation Needed"를 막으면 큰 패킷만 조용히 사라지는 MTU 블랙홀이 생깁니다.

먼저 오버레이를 쓰는지 패킷으로 확인합니다. 노드 NIC에서 VXLAN 포트를 캡처해 트래픽이 보이면 캡슐화 경로입니다. VXLAN 표준 포트는 UDP 4789이고, Flannel과 Cilium의 Linux 기본값은 8472입니다.

Bash
sudo tcpdump -ni <노드_NIC> 'udp port 4789 or udp port 8472' -c 20

점검 순서:

  1. 노드와 Pod의 인터페이스 MTU 확인

    Bash
    ip -d link show                                  # 노드: 물리 NIC, flannel.1, vxlan.calico, tunl0, cilium_vxlan 등
    kubectl exec -it <pod> -- ip link show eth0      # Pod (이미지에 ip 명령이 있을 때)
  2. 경로 MTU 실측: -s는 ICMP 페이로드 크기이므로 IPv4에서는 IP 헤더 20바이트 + ICMP 헤더 8바이트를 더한 값이 실제 패킷 크기입니다.

    Bash
    ping -M do -s 1472 -c 3 <대상_IP>    # 1472 + 28 = 1500바이트
    ping -M do -s 1422 -c 3 <대상_IP>    # VXLAN 기준 1450 - 28
    tracepath -n <대상_IP>               # 홉별 PMTU

    노드 간에는 노드 IP로, Pod 간에는 Pod 안에서 상대 Pod IP로 각각 확인하면 MTU가 줄어드는 구간을 구분할 수 있습니다.

  3. CNI MTU 설정: IPv4 기준 터널 헤더는 IP-in-IP 20바이트, VXLAN 50바이트, WireGuard 60바이트입니다. Pod MTU는 "노드 경로 MTU − 헤더 크기" 이하여야 합니다(경로 1500 + VXLAN이면 1450). Calico MTU 문서

    • Calico: 기본은 자동 감지. 직접 지정은 operator 설치면 Installation의 spec.calicoNetwork.mtu, manifest 설치면 calico-config ConfigMap의 veth_mtu를 바꾸고 calico-node를 재시작합니다.
    • Flannel: 호스트 인터페이스 MTU에서 백엔드 헤더를 뺀 값을 자동 계산해 각 노드의 /run/flannel/subnet.env에 FLANNEL_MTU로 기록합니다. 노드가 같은 L2에 있다면 host-gw 백엔드로 터널 자체를 없앨 수 있습니다. Flannel Backends
    • Cilium: Helm 값 MTU(기본 0 = 자동 감지). Cilium Helm 레퍼런스

    MTU를 바꿔도 이미 떠 있는 Pod는 예전 값을 유지하므로 Pod를 다시 만들어야 반영됩니다.

  4. MSS 클램핑(필요할 때만): VPN·터널 경계처럼 MTU가 줄어드는 지점에서 PMTUD가 막혀 있다면 그 경계의 라우터·게이트웨이에서 SYN의 MSS를 줄입니다.

    Bash
    sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

    MSS는 sysctl 하나로 바꾸는 값이 아닙니다. net.ipv4.tcp_mem은 시스템 전체 TCP 메모리 한도라 MTU·MSS와 관계가 없습니다.

5단계: NetworkPolicy와 CNI 데이터플레인

CNI데이터플레인지연 관점 특징
CalicoBGP 라우팅 또는 VXLAN/IP-in-IP. 데이터플레인은 iptables(기본)·nftables·eBPF 중 선택iptables 데이터플레인에서는 정책이 늘수록 규칙 처리 비용 증가. eBPF 데이터플레인으로 바꿀 수 있음
FlannelVXLAN(기본) 또는 host-gw캡슐화 비용과 MTU 감소. NetworkPolicy는 자체 지원하지 않음
CiliumeBPF정책과 Service 처리를 eBPF 맵 조회로 수행, kube-proxy 대체 가능

iptables는 체인의 규칙을 순서대로 평가하므로 규칙 수가 많을수록 패킷당 처리 비용이 커집니다. eBPF 데이터플레인은 해시 맵 같은 자료구조로 조회해 규칙 수 증가의 영향을 덜 받습니다. 다만 수십 개 정책 수준에서는 차이가 측정되지 않는 경우가 많습니다. 데이터플레인 교체는 4단계까지 측정한 뒤에도 정책 처리 구간이 원인으로 남을 때 검토하세요. 정책 설계 자체는 NetworkPolicy 통신 오류 해결: Calico vs Cilium에서 다룹니다.

6단계: 서비스 메시 사이드카

사이드카(Envoy 등)는 모든 요청을 가로채 mTLS, 재시도, 메트릭을 처리하므로 홉마다 지연이 더해집니다. 같은 부하로 사이드카 있음/없음을 비교해 증가분을 숫자로 확인하세요.

  • 증가분이 크면 프록시 CPU 제한(스로틀링), 액세스 로그 수준, 재시도·타임아웃 정책부터 확인합니다.
  • 메시가 꼭 필요하지 않은 워크로드는 주입에서 제외하는 방안을 검토합니다.
  • Istio는 사이드카 없이 노드 단위 프록시를 쓰는 ambient 모드도 제공합니다(1.24에서 GA). 도입 전 같은 방식으로 측정해 비교하세요.

eBPF로 커널 안쪽 보기

tcpdump는 캡처한 인터페이스에서 패킷을 본 시각만 기록합니다. 커널 내부에서 어디서 지연·재전송이 생기는지는 eBPF 기반 도구로 봅니다.

도구보여 주는 것
bcc tcpretrans재전송이 발생한 연결과 시점
bcc tcprtt커널이 측정한 TCP RTT 분포
bcc tcpconnlat연결 수립(SYN→SYN-ACK) 지연
Cilium Hubble (hubble observe)플로우, 정책 판정(허용/차단), L7 메타데이터
Bash
sudo tcpretrans          # bcc-tools 설치 경로에 따라 tcpretrans-bpfcc 등
sudo tcpconnlat 10       # 10ms 넘는 연결 수립만 출력

측정 가능한 가설로 정리하기

"느리다" 대신 **"A → B 호출(응답 100KB)의 P99가 노드 간 경로에서만 40ms 높고, 같은 노드에서는 차이가 없다"**처럼 구간과 숫자로 적으세요. 그러면 다음에 볼 곳(오버레이·MTU)이 정해지고, 튜닝 후 같은 측정으로 효과를 확인할 수 있습니다.

자주 묻는 질문 (FAQ)

Q1. CNI를 Cilium으로 바꾸면 지연이 자동으로 줄어드나요? A1. 원인이 iptables 규칙 처리나 kube-proxy라면 줄 수 있지만, DNS·MTU·사이드카가 원인이라면 효과가 없습니다. 먼저 구간별 측정으로 원인을 확인하세요.

Q2. iperf3 결과가 좋은데 서비스가 느린 이유는 무엇인가요? A2. iperf3는 처리량을 재는 도구입니다. 요청-응답 지연은 DNS 조회, 연결 수립, 사이드카, 애플리케이션 처리 시간을 포함하므로 netperf TCP_RR이나 실제 프로토콜 부하 도구로 P99를 따로 측정해야 합니다.

Q3. 서비스 메시를 쓰면 무조건 느려지나요? A3. 프록시 홉이 추가되므로 지연이 늘어나는 것은 맞지만 크기는 설정과 부하에 따라 다릅니다. 사이드카 유무를 같은 조건으로 비교해 기준선을 잡은 뒤 판단하세요. mTLS 설정은 Istio PeerAuthentication으로 mTLS 강제하기를 참고하세요.

확인 정보
✦ ✦ ✦
출처·검증 안내

글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보

이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.

발행 · ·업데이트 ·
주제 관련 공식 문서(자동 연결 · 문장별 근거 아님)Kubernetes 공식 문서 ↗

댓글

첫 번째 댓글을 남겨보세요.