왜 또 kube-proxy 얘기인가
kube-proxy는 Kubernetes에서 가장 오래된 컴포넌트 중 하나다. 그런데 사람들은 의외로 이 친구가 실제로 뭘 하는지 잘 모른다. Service ClusterIP로 패킷이 들어오면 Pod로 잘 가더라, 정도가 평균적인 이해다. 나도 한참 그랬다.
올해 1.33에서 nftables 모드가 정식으로 GA가 됐고, 이제 Linux 노드에서는 공식 권장 모드가 nftables라는 분위기가 만들어졌다. 우리 팀에서도 다음 분기 클러스터 업그레이드 때 nftables로 넘어갈지를 두고 논의 중이다. 결정을 하려면 세 모드가 도대체 어떻게 다른지를 손에 쥐고 있어야 하는데, 매번 검색해서 보다 보니 한번 정리해두자 싶었다.
이 글은 "어느 모드가 빠른가" 같은 표 비교가 아니다. 같은 Service 트래픽이 들어왔을 때 각 모드가 커널 안에서 실제로 어떤 경로를 타는지를 따라가본다.
공통 구조부터: kube-proxy는 데이터플레인이 아니다
가장 먼저 정리할 부분. kube-proxy는 패킷을 직접 처리하지 않는다. 한 번도 처리한 적이 없다.
kube-proxy가 하는 일은 컨트롤러다. apiserver를 watch하면서 Service / EndpointSlice의 변화를 받아오고, 그걸 커널의 패킷 처리 규칙(iptables 룰, IPVS 가상서버, nftables 체인)으로 번역해서 동기화한다. 패킷이 ClusterIP로 들어왔을 때 그걸 백엔드 Pod IP로 DNAT 해주는 건 어디까지나 커널이다.
이 사실 하나로 많은 게 설명된다. kube-proxy를 잠깐 멈춰도 기존 트래픽은 흐른다. 룰이 이미 커널에 박혀 있으니까. 다만 그 사이 Pod가 죽거나 새로 뜨면 그게 반영되지 않을 뿐. 옛날에 kube-proxy를 잠시 죽이고도 트래픽이 흐르는 걸 보고 어리둥절했던 기억이 있다. 이게 그 이유다.
세 모드는 결국 "커널 측 데이터 구조와 동기화 메커니즘을 뭘로 쓸 것인가"의 차이다.
iptables 모드: 가장 익숙하고, 가장 늙은
iptables 모드는 동기화 시점에 PREROUTING / OUTPUT 체인에 KUBE-SERVICES → KUBE-SVC-XXX → KUBE-SEP-YYY 로 이어지는 룰 체인을 만든다. ClusterIP로 들어온 패킷은 KUBE-SERVICES를 거쳐 해당 Service에 해당하는 KUBE-SVC-XXX로 점프하고, 거기서 endpoint 수만큼 나열된 KUBE-SEP-YYY 중 하나로 statistic 모듈을 이용해 확률적으로 라우팅된다. 그리고 KUBE-SEP에서 실제 Pod IP로 DNAT.
여기서 사람들이 자주 놓치는 게 두 가지다.
첫째, 로드밸런싱이 "확률 기반"이다. iptables의 statistic mode random probability를 쓴다. endpoint가 3개면 첫 번째 룰에 1/3, 통과한 패킷에 두 번째 룰 1/2, 마지막 룰 100%. 그래서 엔드포인트가 늘면 룰의 수가 늘어난다. 그것도 선형으로.
둘째, iptables의 룰 업데이트는 "전체 갱신"이다. 룰 하나만 바꿔도 iptables-restore가 내부적으로 테이블 전체를 다시 그린다. Service 수천 개에 endpoint 수만 개 규모가 되면 한 번의 동기화에 수 초가 걸리는 일이 진짜로 일어난다. 우리 클러스터에서도 Service 1500개 넘어가니까 sync 한 번에 1.2초씩 찍히기 시작했다.
이게 iptables 모드의 본질적 한계다. 변경량과 무관하게 O(n) 비용을 매번 낸다.
IPVS 모드: 커널 레벨 LB
IPVS는 원래 Linux Virtual Server 프로젝트에서 나온 4계층 로드밸런서다. kube-proxy의 IPVS 모드는 각 Service를 IPVS의 virtual server로, 각 endpoint를 real server로 매핑한다.
여기서 일단 자료구조가 달라진다. IPVS는 해시 테이블 기반이다. virtual server 룩업도 O(1)에 가깝고, real server 선택도 라운드로빈/최소연결 같은 알고리즘을 커널이 직접 처리한다. iptables처럼 룰을 나열해서 확률 매칭하는 게 아니라, 그냥 LB 테이블이다.
또 하나 핵심. IPVS 모드는 사실 IPVS만으로 안 굴러간다. ClusterIP를 노드의 dummy 인터페이스(kube-ipvs0)에 바인딩해 두고, 그래서 패킷이 노드로 들어왔을 때 INPUT 체인을 타게 만든 다음, IPVS hook이 거기서 잡아서 DNAT한다. 그리고 NodePort나 hairpin SNAT 같은 케이스를 위해 부분적으로 iptables 룰도 함께 깐다. 순수 IPVS는 아닌 거다.
성능 특성은 분명히 좋다. endpoint가 만 개여도 룩업 비용이 거의 일정하다. 알고리즘 선택지도 있다(rr, wrr, lc, sh 등). 그런데 IPVS 모드를 운영하면서 의외로 자주 만나는 함정이 있다.
- conntrack과의 상호작용. IPVS도 conntrack을 거치는데, 옛날에
net.ipv4.vs.conntrack=1같은 설정 누락으로 NodePort 서비스가 이상하게 작동하던 케이스가 있었다. - LoadBalancer/NodePort에서 source IP가 의도와 다르게 SNAT되는 케이스.
externalTrafficPolicy: Local을 안 쓰면 더 그렇다. - 모듈 로드.
ip_vs,ip_vs_rr같은 커널 모듈이 안 올라와 있으면 조용히 iptables 폴백한다. 이거 때문에 새벽 3시에 한참 헤맨 적 있다.
성능은 좋은데, 동작이 완전히 단순해진 건 아니다.
nftables 모드: 본질적으로 같은데, 본질적으로 다른
1.33에서 GA가 된 nftables 모드는 표면적으로는 iptables의 후계자처럼 보인다. 실제로 동작 모델도 비슷하다. PREROUTING 단계에서 ClusterIP를 잡아서 endpoint로 DNAT 한다. 다만 그걸 nftables의 set과 verdict map이라는 자료구조 위에서 한다.
핵심 차이는 두 가지다.
첫째, 증분 업데이트가 진짜로 증분이다. iptables-restore가 전체를 다시 그리는 것과 달리, nftables는 트랜잭션 단위로 추가/삭제만 한다. 그래서 sync 한 번의 비용이 O(전체 Service 수)가 아니라 O(변경된 Service+Endpoint 수)다. 이게 KEP-3866의 가장 큰 동기였다.
둘째, 룩업 자료구조가 다르다. iptables가 룰을 선형으로 매칭하는 반면, nftables는 verdict map(vmap)을 통해 ClusterIP → 점프할 체인을 해시 테이블로 룩업한다. 즉 service 수가 많아져도 룩업이 일정 시간이다. 부분적으로 IPVS와 비슷한 자료구조 이득을 본다고 봐도 된다.
그러면 IPVS와 비교했을 때 nftables는 뭘 더 잘하나? 로드밸런싱 자체는 IPVS가 여전히 알고리즘이 더 풍부하다. nftables는 jhash / random 기반의 선택만 제공한다. 대신 nftables는 iptables / IPVS 모드가 둘 다 끌어쓰는 "iptables 추가 룰" 의존을 안 한다. 한 가지 framework로 깔끔하게 떨어진다. 그리고 kernel 5.13 이상에서만 동작한다는 명시적 요구사항이 생긴 만큼, 운영 환경 가정이 분명하다.
운영 관점에서 매력적인 점은 "iptables-save로 디버깅" 패턴을 그대로 nft list ruleset으로 옮길 수 있다는 거다. IPVS는 ipvsadm이라는 다른 도구를 익혀야 했는데, nftables는 기존 iptables 디버깅 직관이 어느 정도 통한다.
그래서 우리는 뭘 쓸까
솔직히 결론을 내놓기엔 아직 검증 중이다. 우리 클러스터 기준으로 정리해보면:
- Service 수가 적당하고 운영이 안정적이면 iptables 그대로 둬도 별 문제 없다. 단지 클러스터가 커지면 sync latency가 누적된다는 걸 인지하고 있으면 된다.
- 대규모(Service 수천 개 이상)에서 latency 민감하면 IPVS는 여전히 합리적이다. 다만 conntrack/모듈/SNAT 디테일에 대한 부담이 있다.
- 신규 클러스터를 1.33+로 깐다면 nftables를 디폴트 후보로 두는 게 자연스러워졌다. iptables 디버깅 직관과의 호환성, 증분 sync, 단일 framework — 운영 측면에서 가장 깔끔하다.
다음 분기에 우리 dev 클러스터부터 nftables로 전환해보고, 그때 sync latency / Service propagation delay 변화를 같이 측정해보려고 한다. 결과는 후속 글로 정리할 예정이다.
혹시 이미 nftables 모드로 운영 중이신 분 있으면 어떤 함정이 있었는지 댓글 남겨주시면 큰 도움이 될 것 같다. 특히 Calico / Cilium 같은 CNI와 같이 쓸 때 conflict 케이스가 있었는지 궁금하다.
'IT > Kubernets' 카테고리의 다른 글
| startupProbe 모르고 슬로우 스타트 앱 운영하지 마세요 (0) | 2026.05.13 |
|---|---|
| 배포할 때마다 503이 잠깐씩 튀던 이유 — Pod 종료 흐름 삽질 노트 (0) | 2026.05.12 |
| KEDA SQS scaler에서 새벽에 만난 함정 (0) | 2026.05.11 |
| Bottlerocket vs Talos, 워커노드 OS는 뭘 쓸까 (0) | 2026.05.11 |
| distroless 컨테이너에 sh가 없을 때, kubectl debug 한 줄로 끝내기 (0) | 2026.05.10 |