Задача
Под обращается к сервису по адресу 10.96.0.42:80 (ClusterIP). Запрос успешно доходит до одного из подов-бэкендов.
Но если зайти на любую ноду и поискать этот адрес, обнаружится странное:
ip addr | grep 10.96.0.42 # пусто
ping 10.96.0.42 # не отвечаетАдреса нет ни на одном интерфейсе. Ни один процесс на нём не слушает.
Вопрос: как тогда запрос доходит до бэкенда?
Что кажется очевидным
«ClusterIP — это адрес виртуального балансировщика, где-то есть прокси-процесс, который принимает соединения и раскидывает их».
Действительно, раньше kube-proxy работал именно так (userspace-режим). Сейчас — нет.
Как это работает на самом деле
ClusterIP не существует как сетевой адрес. Это запись в таблице правил, по которой ядро подменяет адрес назначения у пакета. Никто на этом адресе не слушает, и пинговать его бессмысленно.
Механизм:
- Вы создаёте Service — API server выделяет ему ClusterIP из служебного диапазона.
- Контроллер endpoints собирает список готовых подов (см. задачу 1).
- kube-proxy на каждой ноде видит эти объекты и программирует правила в ядре.
- Пакет, отправленный на ClusterIP, попадает под правило DNAT: адрес назначения переписывается на IP конкретного пода.
- Дальше пакет идёт обычной маршрутизацией — через 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 и другие).
Посмотреть текущий режим:
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.
Что с этим делать
Практические следствия, которые пригодятся при отладке:
- Не пингуйте ClusterIP. Отсутствие ответа — норма, а не признак поломки. Проверяйте
curlна нужный порт. - Пустой список endpoints — самая частая причина «сервис не работает». Правил DNAT просто не создано:BASH
kubectl get endpointslices -l kubernetes.io/service-name=my-svc - Headless Service (
clusterIP: None) работает иначе: DNAT нет, DNS отдаёт напрямую адреса подов. Именно это нужно для StatefulSet и клиентов, которым важно видеть отдельные экземпляры. - Балансировка не «умная». В режиме iptables это случайный выбор на каждое соединение (не на запрос). Долгоживущие соединения — HTTP/2, gRPC, пулы к БД — прилипают к одному поду, и после масштабирования нагрузка не перераспределится. Лечится клиентской балансировкой или service mesh.
externalTrafficPolicy: Localдля трафика извне сохраняет исходный IP клиента и убирает лишний прыжок между нодами, но требует, чтобы под был на той ноде, куда пришёл трафик.- Много сервисов — смотрите в сторону IPVS или eBPF. На кластере с тысячами сервисов режим iptables заметно замедляет обновление правил, что напрямую усиливает эффект из задачи 2.
Дальше: Задача 7. Три probe и чем они друг от друга отличаются.