Learning Platform
Глоссарий Troubleshooting
Урок 09.03 · 25 мин
Продвинутый
kube-proxyiptablesIPVSnftablesDNATconntrackLoad balancing

kube-proxy: iptables, IPVS, nftables

В прошлом уроке мы выяснили: ClusterIP — это виртуальный IP, реализованный правилами в netfilter. Теперь препарируем сам процесс генерации этих правил. Кто их пишет, какие они, как они работают на больших кластерах.

kube-proxy — это компонент, который превращает декларативные объекты Service/EndpointSlice в реальные данные в kernel: iptables-цепочки, IPVS-таблицы или nftables-правила. Без kube-proxy Services не работают (за исключением кластеров где его роль выполняет CNI типа Cilium через eBPF).


Алгоритмы балансировки нагрузки: round-robin, least-connections, consistent hashing

Что такое kube-proxy

kube-proxy — отдельный компонент, который запускается на каждой ноде (обычно как DaemonSet kube-proxy в namespace kube-system). Его job description прост:

  1. Watch на apiserver-е: все Services и EndpointSlices в кластере.
  2. На каждое изменение — пересобрать правила в netfilter (или IPVS/nftables) на локальной ноде.
  3. Эти правила реализуют: ClusterIP → DNAT на один из endpoint IPs, NodePort на каждой ноде, балансировку.
$ kubectl get ds -n kube-system kube-proxy
NAME         DESIRED   CURRENT   READY   AGE
kube-proxy   3         3         3       45d

$ kubectl get pods -n kube-system -l k8s-app=kube-proxy
NAME               READY   STATUS    RESTARTS   AGE
kube-proxy-abc12   1/1     Running   0          45d
kube-proxy-def34   1/1     Running   0          45d
kube-proxy-xyz56   1/1     Running   0          45d

Pod kube-proxy запускается с hostNetwork: true (использует netns ноды) и имеет permissions писать в iptables/IPVS — ему нужен NET_ADMIN.

NOTE

В кластерах с Cilium с kubeProxyReplacement: true — kube-proxy не нужен, его роль играют eBPF-программы Cilium. Это уже не редкость в современных prod-кластерах. Но на CKAD считаем kube-proxy default.


Режимы работы

kube-proxy умеет три режима:

ModeStatus (v1.35)DefaultПроизводительность
iptablesStableDefault (исторически и в v1.35)O(N) lookup на правилах
IPVSGA с v1.11НетO(1) через hash table
nftablesalpha v1.29 → beta v1.31 → GA v1.33Нет (opt-in через --proxy-mode=nftables)O(1) хешированием

Выбирается через kube-proxy config:

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "iptables"   # или "ipvs", или "nftables"
TIP

На CKAD вас НЕ просят настраивать kube-proxy mode — это CKA-территория. Но знать что такое existует и в чём разница — обязательно. Особенно понимать, что Service “реализуется” правилами kube-proxy, а не магией.


iptables mode: цепочки правил

Самый известный режим (по умолчанию долгие годы). kube-proxy пишет правила в iptables (точнее в подсистему nat table) через цепочки KUBE-SERVICES, KUBE-SVC-XXXX, KUBE-SEP-XXXX.

Структура цепочек

PREROUTING / OUTPUT (kernel hooks)

   KUBE-SERVICES        ← главная цепочка, по одному правилу на Service

   match dst=ClusterIP:port → jump to KUBE-SVC-XXXX

   KUBE-SVC-XXXX        ← цепочка одного Service

   probability-based выбор → jump to одной из KUBE-SEP-YYYY

   KUBE-SEP-YYYY        ← конечная точка, один endpoint

   DNAT to PodIP:targetPort

Пример из реального кластера

Service web (ClusterIP 10.96.0.10:80) с тремя endpoints: 10.244.1.5:8080, 10.244.2.7:8080, 10.244.3.9:8080.

$ iptables -t nat -L KUBE-SERVICES -n
Chain KUBE-SERVICES (2 references)
target              prot  source        destination
KUBE-SVC-7XYZAB     tcp   --  anywhere  10.96.0.10           tcp dpt:80 /* default/web */

$ iptables -t nat -L KUBE-SVC-7XYZAB -n
Chain KUBE-SVC-7XYZAB (1 references)
target            prot  source       destination
KUBE-SEP-AAAA     all   --  anywhere  anywhere   statistic mode random probability 0.33333
KUBE-SEP-BBBB     all   --  anywhere  anywhere   statistic mode random probability 0.50000
KUBE-SEP-CCCC     all   --  anywhere  anywhere

$ iptables -t nat -L KUBE-SEP-AAAA -n
Chain KUBE-SEP-AAAA (1 references)
target  prot  source       destination
DNAT    tcp   --  anywhere  anywhere   tcp to:10.244.1.5:8080

Probability-based load balancing

iptables не умеет round-robin нативно. Используется хак через модуль statistic:

правило 1: probability 0.33333  → 33% пакетов
правило 2: probability 0.50000  → 50% от ОСТАВШИХСЯ (33% от общего)
правило 3: без вероятности      → все остальные (33% от общего)

Формула вероятности для k-го из N endpoints (1-индекс):

probability(k) = 1 / (N - k + 1)

Для 3 endpoints: 1/3 ≈ 0.333, 1/2 = 0.5, 1.0 (последний берёт остаток). Получается равномерное распределение.

iptables: путь пакета через цепочки
пакет входПакет с dst=10.96.0.10:80 приходит на ноду или генерируется локальным Pod-ом.
netfilter hook PREROUTING/OUTPUT
KUBE-SERVICESГлавная цепочка. Содержит по правилу на каждый Service ClusterIP. На больших кластерах — тысячи правил.
jump
KUBE-SVC-7XYZABЦепочка одного Service. Содержит правила перехода на endpoint-цепочки с probability-based selection.
random 0.33 hit
KUBE-SEP-AAAAЦепочка одного endpoint (Service Endpoint Point). Делает DNAT и затем MARK для возможного MASQUERADE на исходящем пути.
dst переписан
conntrack запоминаетConntrack создаёт entry: original (src=client, dst=10.96.0.10) ↔ reply (src=10.244.1.5, dst=client). Обратный пакет восстановит ClusterIP в src.

Что такое KUBE-MARK-MASQ

Кроме DNAT, в цепочках есть KUBE-MARK-MASQ — маркирует пакеты, для которых нужен source NAT (MASQUERADE). Это нужно когда:

  • Pod пишет на собственный Service ClusterIP (loopback hairpin) — без MASQUERADE backend отвергнет пакет.
  • Трафик из external (NodePort) идёт на Pod на другой ноде — иначе client IP мог бы сбить routing назад.

Маркер MASQ обрабатывается в POSTROUTING цепочке: пакеты с этой меткой получат SNAT на NodeIP.

Проблема O(N) на больших кластерах

Каждый Service — это правила в KUBE-SERVICES (одно на Service) и в KUBE-SVC-XXXX (N правил на N endpoints). При N сервисах и M Pods за каждым:

  • KUBE-SERVICES: N правил
  • Все KUBE-SVC-XXXX: суммарно N×M правил

iptables match — это linear scan: каждый пакет проходит правила по порядку. На 5000 services × 10 endpoints = 50000 правил. Это значительный latency на каждый сетевой connect.

WARNING

Хуже того: при каждом изменении (Pod restart, новый Service) kube-proxy перегенерирует и применяет весь набор правил через iptables-restore. На больших кластерах это занимает секунды и блокирует netfilter. Поэтому iptables-mode не масштабируется выше ~5000 Services.


IPVS mode: kernel L4 load balancer

IPVS (IP Virtual Server) — это часть Linux kernel, специально разработанная для L4 load balancing. В отличие от iptables, IPVS хранит правила в hash table — O(1) lookup. Изначально использовался как фронтенд для веб-кластеров (LVS).

При mode: ipvs kube-proxy создаёт:

  1. Виртуальный интерфейс kube-ipvs0 на ноде, к которому привязывает ClusterIPs (ip addr add 10.96.0.10/32 dev kube-ipvs0).
  2. IPVS виртуальные серверы (virtual server) — по одному на Service:port. Каждый имеет список real servers (Pod IPs).
  3. Iptables всё равно используется (только для KUBE-MARK-MASQ и base path), но без огромного дерева правил для каждого Service.
$ ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.96.0.10:80 rr
  -> 10.244.1.5:8080              Masq    1      3          12
  -> 10.244.2.7:8080              Masq    1      2          15
  -> 10.244.3.9:8080              Masq    1      4          11

Алгоритмы балансировки

В отличие от iptables (только probability), IPVS поддерживает много алгоритмов:

  • rr (round-robin) — default, циклически
  • wrr (weighted round-robin) — с весами
  • lc (least-connections) — на endpoint с наименьшим числом активных соединений
  • wlc (weighted least-connections)
  • sh (source hashing) — детерминированно по client IP (sticky без conntrack)
  • dh (destination hashing)

Конфигурируется через kube-proxy config:

mode: ipvs
ipvs:
  scheduler: rr

Преимущества и недостатки

Плюсы:

  • O(1) lookup — масштабируется на 10000+ Services.
  • Меньше CPU на каждый пакет.
  • Больше алгоритмов LB.

Минусы:

  • Сложнее debug — kernel state в отдельной таблице, не видно через iptables-save.
  • Требует загруженных модулей kernel: ip_vs, ip_vs_rr, ip_vs_wrr, nf_conntrack.
  • В bridge-mode конфигурациях нужны дополнительные sysctl (bridge-nf-call-iptables=1).

nftables mode: современная альтернатива

С Linux kernel 3.13 разрабатывалась замена iptables — nftables. У него тот же концепт (правила в kernel netfilter), но:

  • Hash-based matching для set-ов и map-ов — фактически O(log N) или O(1).
  • Один унифицированный backend для NAT/filter/mangle вместо четырёх iptables-таблиц.
  • Атомарные обновления — nft -f rules.nft применяет batch без race conditions.
  • Более выразительный синтаксис.

В Kubernetes nftables mode kube-proxy прошёл путь alpha v1.29 → beta v1.31 → GA v1.33. На v1.35 default остаётся iptables для обратной совместимости, nftables включается явно через --proxy-mode=nftables. Он генерирует nftables-правила вместо iptables, используя map-ы для hash-based lookup ClusterIP → endpoint.

# Пример nft правила kube-proxy:
table ip kube-proxy {
  chain services {
    ip daddr . tcp dport vmap @cluster-ips
  }
  map cluster-ips {
    type ipv4_addr . inet_service : verdict
    elements = {
      10.96.0.10 . 80 : jump svc-web,
      10.96.0.20 . 443 : jump svc-api,
    }
  }
}

Lookup через vmap — это hash, O(1). На больших кластерах nftables mode значительно быстрее iptables и сравним с IPVS, но без необходимости поддерживать отдельный модуль.

NOTE

К моменту v1.35 nftables-mode стабилен и доступен как opt-in (--proxy-mode=nftables); default kube-proxy mode остаётся iptables. Команды начинают мигрировать большие кластеры на nftables/eBPF (Cilium kube-proxy replacement). На CKAD важно знать, что nftables-режим существует и решает O(N) проблему iptables.


Killer момент: посмотреть реальные правила

На любой ноде с iptables-mode kube-proxy можно увидеть всё это в живую:

# Все KUBE-SVC цепочки
sudo iptables -t nat -S | grep KUBE-SVC

# Цепочки для одного Service по имени (комментарий /* namespace/name */)
sudo iptables -t nat -S | grep "default/web"

# Полный путь от ClusterIP до DNAT для Service web
sudo iptables -t nat -L KUBE-SERVICES | grep "default/web"
# KUBE-SVC-7XYZAB  tcp  -- anywhere  10.96.0.10   tcp dpt:80 /* default/web */

sudo iptables -t nat -L KUBE-SVC-7XYZAB
# KUBE-SEP-AAAA  all  -- anywhere  anywhere  /* default/web */ statistic mode random probability 0.33333
# KUBE-SEP-BBBB  all  -- anywhere  anywhere  /* default/web */ statistic mode random probability 0.50000
# KUBE-SEP-CCCC  all  -- anywhere  anywhere  /* default/web */

sudo iptables -t nat -L KUBE-SEP-AAAA
# DNAT  tcp  -- anywhere  anywhere  tcp to:10.244.1.5:8080

Для IPVS:

ipvsadm -L -n -t 10.96.0.10:80
# Видим Service и его real servers (endpoints)

Для nftables:

nft list table ip kube-proxy
# Видим chains и maps

externalTrafficPolicy: Cluster vs Local

Для NodePort / LoadBalancer Service есть externalTrafficPolicy (default Cluster):

spec:
  type: NodePort
  externalTrafficPolicy: Local   # или Cluster

Cluster (default): трафик пришёл на любую ноду → kube-proxy DNAT-ит на любой endpoint в кластере (даже на другой ноде). Плюс: равномерный LB. Минус: дополнительный hop, source IP клиента теряется (заменяется на NodeIP при SNAT).

Local: пакет обрабатывается только если на этой ноде есть endpoint. Если нет — пакет отброшен. Плюс: source IP сохраняется, нет лишнего hop. Минус: неравномерная нагрузка (если на ноде 1 endpoint, она получит больше трафика).

externalTrafficPolicy: Local
external client → NodeA:30080 → [есть Pod на NodeA? yes] → Pod
                                                 [no] → DROP

Local критично если приложению нужен real client IP (логи, rate limit, geo).


kube-proxy и Pods на той же ноде: short-circuit?

В iptables-mode пакет от Pod-а на ClusterIP того же Service-а может попасть на Pod на этой же ноде — но только если probability ему так выпала. Никакого “prefer local” в default-mode нет (это делает internalTrafficPolicy: Local — отдельная фича).

В Cilium с eBPF — там есть прямой short-circuit: пакет к локальному backend идёт минуя сетевой стек, через socket-level redirect.


Проверка знанийKnowledge check
Почему iptables-mode kube-proxy плохо масштабируется на кластерах с 5000+ Services, и как IPVS/nftables это решают?
ОтветAnswer
iptables-mode хранит правила в виде последовательных цепочек, и каждый пакет проходит через них **линейным сканированием** (O(N)) — netfilter проверяет правила по очереди, пока не найдёт match. С 5000 Services × ~5 endpoints на каждый — это десятки тысяч правил, и каждый сетевой пакет добавляет существенный latency. Хуже того, при каждом изменении kube-proxy перегенерирует ВЕСЬ набор правил через iptables-restore — на больших кластерах это занимает секунды и блокирует netfilter. IPVS решает это через kernel L4 LB с **hash table** — O(1) lookup независимо от числа Services, плюс встроенные алгоритмы (rr, lc, sh). Nftables (GA с v1.31, default с v1.32) использует **vmaps** для hash-based dispatching правил и атомарные обновления — те же O(1) преимущества плюс единый netfilter backend без отдельного модуля типа IPVS. На больших кластерах nftables становится default для новых установок.

Проверьте понимание

Результат: 0 из 0
Аналитический
Вопрос 1 из 5. kube-proxy в iptables-mode реализует load balancing между N endpoints через какой механизм?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5