Service는 실체가 없다 — kube-proxy가 iptables로 트래픽을 꽂는 법

부제: 10.96.x.x에 붙은 프로세스는 없다. 커널의 NAT 룰이 목적지를 Pod로 바꿀 뿐


여기서부터는 시리즈 B(네트워킹과 격리의 안쪽) 다. 시리즈 A가 "이게 뭘
하는가"였다면, B는 "실제로 어떻게 그렇게 되는가" 의 기계를 연다.

시작은 A에서 계속 미뤄 둔 질문이다.
A4에서
나는 "ClusterIP는 주소만 보면 L3(IP)인데 :Port가 고정이고 kube-proxy가 포트
단위로 로드밸런싱해서 L4로 분류한다"고 정리했다. 그런데 그 kube-proxy가
정확히 어떻게 트래픽을 꽂는가? 이번 편은 그 답을 지금 이 클러스터의 실제
iptables 룰
로 확인한다.


한 줄 정의부터

개념 한 줄 정의
kube-proxy Service 정의를 각 노드의 iptables 룰로 번역하는 데몬(DaemonSet)
KUBE-SERVICES 목적지가 ClusterIP:Port면 서비스 체인으로 보내는 입구 룰
KUBE-SVC-* 엔드포인트들로 확률적으로 분기하는 체인 = L4 로드밸런싱
KUBE-SEP-* 최종 DNAT(목적지를 실제 Pod IP로 재작성)하는 체인
KUBE-MARK-MASQ 클러스터 밖에서 온 트래픽에 SNAT 표시(리턴 경로 보장)

ClusterIP에는 아무도 살지 않는다

먼저 직관을 하나 깨야 한다. 10.96.177.199 같은 ClusterIP는 어떤 Pod의
주소도, 어떤 프로세스가 bind한 주소도 아니다.
그 IP로 ping을 쏴도 응답하는
실체가 없다. 그런데도 그 주소로 요청을 보내면 신기하게 Pod에 도착한다.

비밀은 커널의 iptables NAT 테이블에 있다. kube-proxy가 각 노드에서
"목적지가 이 ClusterIP면, 목적지를 실제 Pod IP로 몰래 바꿔치기(DNAT)하라"는
룰을 심어 둔 것이다. 즉 ClusterIP는 실체 있는 프록시가 아니라, 커널이 목적지를
재작성하도록 만든 규칙
이다.

패킷이 KUBE-SERVICES▸KUBE-SVC▸KUBE-SEP를 지나 Pod로 DNAT되는 여정


실제 룰을 그대로 떠 보기

말로만 하면 안 믿긴다. 그래서 이 클러스터의 kube-proxy 파드에서
iptables-save를 그대로 떴다. (kube-proxy는 iptables 바이너리를 갖고
hostNetwork로 도니, 파드에서 바로 노드의 룰을 볼 수 있다.)

실제 iptables 룰 — KUBE-SERVICES, 확률 분기, DNAT

세 단계로 읽힌다.

  • ① 입구(KUBE-SERVICES)목적지 10.96.177.199 : 80인 TCP면
    KUBE-SVC-RPZM… 체인으로 보낸다. 서비스 하나당 이런 입구 룰이 하나씩 생긴다.
  • ② 서비스 체인(KUBE-SVC) — 여기가 로드밸런서다. 엔드포인트(Pod) 3개로
    -m statistic --mode random --probability확률 분기한다. 이 확률 값이
    묘하다 — 0.333 → 0.5 → (나머지).
  • ③ 엔드포인트 체인(KUBE-SEP) — 고른 엔드포인트에서 DNAT --to-destination 10.244.0.21:80. 여기서 목적지가 ClusterIP → 실제 Pod IP로 바뀐다.

왜 확률이 0.333 → 0.5 → 나머지인가

균등하게 1/3씩 보내고 싶은데 왜 값이 제각각일까. iptables는 룰을 위에서부터
순차로
검사하기 때문이다.

  • 첫 룰: 전체의 1/3(0.333)을 잡는다.
  • 통과한 나머지 2/3 중 둘째 룰이 절반(0.5)을 잡는다 → 2/3 × 1/2 = 1/3.
  • 마지막은 확률 조건 없이 남은 전부를 잡는다 → 1/3.

곱해 보면 정확히 균등한 1/3씩이다. 이 포트 단위 확률 분배가 바로 A4에서
"ClusterIP는 IP지만 L4 로드밸런서"라고 부른 것의 실체다. L3(IP)에서 한 일이
아니라, TCP 포트 레벨에서 트래픽을 나눈 L4 동작이다.


정리

  • ClusterIP는 실체가 없다. 붙어 있는 프로세스가 아니라
    KUBE-SERVICES ▸ KUBE-SVC(확률 분기) ▸ KUBE-SEP(DNAT) 이라는 커널 iptables
    룰 뭉치다.
  • kube-proxy는 트래픽을 직접 나르지 않는다. Service 정의를 보고 iptables
    룰을 심어 둘 뿐
    , 실제 패킷 처리는 커널이 한다.
  • A4의 "왜 L4인가"는 여기서 완결된다 — 포트 단위 확률 로드밸런싱 + DNAT.

그런데 이 방식에는 약점이 있다. 룰이 서비스 수에 비례해 선형으로 늘어난다.
서비스가 수천 개가 되면? 다음 편(B2)에서 iptables의 규모의 벽과, 그걸 넘는
IPVS·eBPF(Cilium)
로 이어 간다.


참고

  • Kubernetes Docs — Virtual IPs and Service Proxies, kube-proxy
  • 실제 룰 캡처: docker-desktop 클러스터(kube-proxy mode=iptables)에서 iptables-save -t nat 덤프

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다