Задача 6. Путь пакета: от пода к поду через kube-proxy

Под обращается к ClusterIP — но такого адреса нет ни на одном интерфейсе в кластере. Разбираем, что ClusterIP это правило DNAT, а не адрес, и чем iptables отличается от IPVS.
Опубликовано:

Задача

Под обращается к сервису по адресу 10.96.0.42:80 (ClusterIP). Запрос успешно доходит до одного из подов-бэкендов.

Но если зайти на любую ноду и поискать этот адрес, обнаружится странное:

BASH
ip addr | grep 10.96.0.42     # пусто
ping 10.96.0.42               # не отвечает
Нажмите, чтобы развернуть и увидеть больше

Адреса нет ни на одном интерфейсе. Ни один процесс на нём не слушает.

Вопрос: как тогда запрос доходит до бэкенда?

Что кажется очевидным

«ClusterIP — это адрес виртуального балансировщика, где-то есть прокси-процесс, который принимает соединения и раскидывает их».

Действительно, раньше kube-proxy работал именно так (userspace-режим). Сейчас — нет.

Как это работает на самом деле

ClusterIP не существует как сетевой адрес. Это запись в таблице правил, по которой ядро подменяет адрес назначения у пакета. Никто на этом адресе не слушает, и пинговать его бессмысленно.

Механизм:

  1. Вы создаёте Service — API server выделяет ему ClusterIP из служебного диапазона.
  2. Контроллер endpoints собирает список готовых подов (см. задачу 1).
  3. kube-proxy на каждой ноде видит эти объекты и программирует правила в ядре.
  4. Пакет, отправленный на ClusterIP, попадает под правило DNAT: адрес назначения переписывается на IP конкретного пода.
  5. Дальше пакет идёт обычной маршрутизацией — через CNI до нужного пода, возможно, на другой ноде.
  flowchart LR
    P["Под-клиент<br/>curl 10.96.0.42:80"] --> N["netfilter на ноде-источнике"]
    N -->|"DNAT: 10.96.0.42:80<br/>→ 10.244.2.7:8080"| CNI["Сеть CNI"]
    CNI --> B["Под-бэкенд<br/>10.244.2.7:8080"]
    KP["kube-proxy<br/>на каждой ноде"] -.программирует правила.-> N
    API["API server<br/>Service + EndpointSlice"] -.наблюдение.-> KP

Ключевой вывод: балансировка происходит на ноде-отправителе, ещё до того, как пакет ушёл в сеть. Выделенного балансировщика в кластере нет вовсе.

iptables против IPVS

Два основных режима kube-proxy:

iptables (долгое время был по умолчанию). Для каждого Service создаётся цепочка правил, выбор бэкенда — через вероятностное сопоставление (statistic с вероятностью 1/N). Работает надёжно, но правила обрабатываются последовательно: при тысячах сервисов таблица разрастается, а время обновления правил растёт нелинейно. Балансировка — фактически случайная.

IPVS. Использует подсистему ядра для балансировки: правила лежат в хеш-таблицах, поиск идёт за константное время. Масштабируется гораздо лучше на больших кластерах и поддерживает разные алгоритмы (rr, lc, sh и другие).

Посмотреть текущий режим:

BASH
kubectl -n kube-system logs -l k8s-app=kube-proxy | grep -i "proxy mode"

# Правила для конкретного сервиса (iptables)
sudo iptables -t nat -L KUBE-SERVICES -n | grep 10.96.0.42

# Или для IPVS
sudo ipvsadm -Ln | grep -A3 10.96.0.42
Нажмите, чтобы развернуть и увидеть больше

Отдельно стоит знать: часть современных CNI (например, Cilium в режиме замены kube-proxy) реализует то же самое на eBPF — без iptables и IPVS вовсе.

Ответ

Запрос доходит потому, что ядро на ноде-отправителе переписывает адрес назначения (DNAT) с ClusterIP на IP реального пода. ClusterIP — это идентификатор для правил, а не адрес интерфейса; поэтому его не видно в ip addr и он не отвечает на ping.

Что с этим делать

Практические следствия, которые пригодятся при отладке:


Дальше: Задача 7. Три probe и чем они друг от друга отличаются.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша