Перейти к содержанию
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 из 5