Как работает маршрутизация в Kubernetes: от пода к сервису
Каждый под в Kubernetes получает уникальный IP-адрес из единой плоской сети кластера. Поды создаются и удаляются постоянно - их IP-адреса эфемерны. Прямое обращение к поду по IP ненадёжно: после перезапуска адрес изменится, и клиент потеряет соединение.
Сервис (Service) решает эту проблему. Он предоставляет стабильный виртуальный IP (ClusterIP) и DNS-имя, которые не меняются при пересоздании подов. Когда под-клиент отправляет запрос на ClusterIP сервиса, трафик перенаправляется на один из целевых подов. Механизм перенаправления настраивает компонент kube-proxy, работающий на каждом узле кластера.
kube-proxy отслеживает изменения в API-сервере: появление новых сервисов, добавление и удаление подов. При изменении состояния он обновляет правила маршрутизации на узле. Вопреки названию, kube-proxy не проксирует трафик в классическом понимании - он конфигурирует сетевой стек ядра Linux так, чтобы пакеты доставлялись до нужного пода.
Путь пакета от пода-клиента до пода-сервера через ClusterIP выглядит так: под-клиент отправляет запрос на виртуальный IP сервиса, ядро узла перехватывает пакет и по правилам kube-proxy подменяет destination IP на реальный IP одного из подов-серверов, затем пакет уходит в сеть и доставляется целевому поду. Ответный трафик проходит обратное преобразование.
Для углублённого понимания эволюции сетевого взаимодействия в Kubernetes - от базовой связности до продвинутых сценариев с Service Mesh - обратитесь к полному руководству по управлению сетевым трафиком.
Роль kube-proxy в маршрутизации: iptables против IPVS
kube-proxy поддерживает два основных режима работы: iptables и IPVS. Выбор режима влияет на производительность и поведение балансировки при росте числа сервисов и подов.
Режим iptables - стандартный. kube-proxy создаёт цепочки правил в netfilter, по одному правилу на каждый эндпоинт сервиса. При получении пакета ядро последовательно обходит цепочку и выбирает целевой под. Балансировка случайная, с равной вероятностью для всех готовых подов. Проблема проявляется на кластерах с сотнями сервисов: линейный обход правил создаёт задержки, а при изменении состояния kube-proxy пересобирает все цепочки заново, что может занимать секунды и вызывать кратковременные сбои соединений.
Режим IPVS использует транспортный уровень ядра Linux - IP Virtual Server. Правила хранятся в хеш-таблицах, поиск целевого пода выполняется за O(1). IPVS предлагает несколько алгоритмов балансировки: round-robin (rr), least connection (lc), source hashing (sh) и другие. Производительность IPVS стабильна при тысячах сервисов - задержки не растут с увеличением числа правил.
Проверить текущие правила iptables для сервиса можно командой:
iptables-save | grep -E 'SERVICE_NAME|CLUSTER_IP'
Для IPVS используйте:
ipvsadm -L -n
Рекомендация: на кластерах с числом сервисов более 500 переключайтесь на IPVS. Настройка выполняется флагом --proxy-mode=ipvs при запуске kube-proxy и требует загрузки модулей ядра ip_vs, ip_vs_rr, ip_vs_wrr, ip_vs_sh.
Как трафик доходит до целевого пода: ClusterIP, NodePort, LoadBalancer
Тип сервиса определяет, как трафик попадает в кластер и доставляется до пода. Разберём три основных типа.
ClusterIP - сервис по умолчанию. Получает виртуальный IP из диапазона, заданного при создании кластера (флаг --service-cluster-ip-range). Доступен только внутри кластера. kube-proxy настраивает DNAT: при получении пакета на ClusterIP меняет destination IP на IP одного из подов. Пример YAML:
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
ports:
- port: 80
targetPort: 8080
NodePort - расширение ClusterIP. Открывает указанный порт (из диапазона 30000-32767) на всех узлах кластера. Внешний запрос приходит на IP любого узла и порт сервиса, затем перенаправляется на ClusterIP, а оттуда - на под. Подходит для отладки и сред без облачного балансировщика.
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
type: NodePort
selector:
app: frontend
ports:
- port: 80
targetPort: 3000
nodePort: 30080
LoadBalancer - надстройка над NodePort. Создаёт внешний балансировщик облачного провайдера (AWS ELB, GCP LB, MetalLB для bare-metal), который распределяет запросы между узлами кластера. Внешний IP балансировщика становится точкой входа для клиентов.
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: LoadBalancer
selector:
app: api
ports:
- port: 443
targetPort: 8443
Схема прохождения запроса для LoadBalancer: клиент → внешний балансировщик → NodePort на узле → ClusterIP → DNAT на IP пода. Для развёртывания кластеров, на которых вы будете применять эти конфигурации, подойдёт облачная инфраструктура Timeweb Cloud с управляемым Kubernetes.
Настройка сетевых политик для изоляции трафика микросервисов
По умолчанию в Kubernetes весь трафик между подами разрешён. Любой под может обратиться к любому другому поду в любом неймспейсе. NetworkPolicy меняет это поведение - она действует как файервол на уровне подов, разрешая только явно указанные соединения.
NetworkPolicy требует CNI-плагин с поддержкой политик. Flannel не поддерживает NetworkPolicy. Calico, Cilium, Weave Net - поддерживают. Без такого плагина политики создаются, но не применяются.
Синтаксис YAML определяет, к каким подам применяется политика (podSelector), направление трафика (policyTypes: Ingress, Egress), и правила фильтрации. В правилах указываются разрешённые источники или назначения (from/to), неймспейсы и порты.
Пример: запретить весь входящий трафик, кроме порта 80 от подов с меткой role=frontend.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- port: 80
Для ограничения egress-трафика - например, запретить подам обращаться наружу, кроме конкретного внешнего API - укажите policyTypes: Egress и перечислите разрешённые CIDR и порты.
Детальную реализацию модели Zero Trust через микросегментацию с готовыми манифестами для Cilium и Calico мы разобрали в руководстве по микросегментации сети Kubernetes.
Практический пример: изоляция фронтенда и бэкенда с помощью NetworkPolicy
Сценарий: в неймспейсе production работают поды frontend, backend и database. Требуется, чтобы frontend обращался к backend, backend - к database, а прямой доступ frontend к database был запрещён.
Шаг 1. Создайте политику для backend - разрешить входящий трафик от frontend.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
Шаг 2. Создайте политику для database - разрешить входящий трафик только от backend.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- port: 5432
Шаг 3. Создайте политику по умолчанию - запретить весь трафик, не разрешённый явно.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
Проверьте связность до и после применения политик. Из пода frontend выполните:
kubectl exec -it deploy/frontend -- curl backend:8080 # должен ответить
kubectl exec -it deploy/frontend -- curl database:5432 # должен зависнуть по таймауту
Диагностика проблем с сетевыми политиками
Неправильно настроенная политика может полностью заблокировать трафик. Типовые ошибки:
- Не тот podSelector. Политика применяется не к тем подам, к которым вы ожидали. Проверьте метки подов через
kubectl get pods --show-labels. - Забыли разрешить DNS-трафик. Если политика ограничивает egress, поды теряют доступ к CoreDNS и не могут резолвить имена сервисов. Добавьте правило на порт 53 UDP для сервиса kube-dns.
- Конфликт политик. Несколько политик применяются к одному поду. Результирующее правило - объединение всех разрешений. Но если одна политика запрещает трафик явно (с помощью Calico или Cilium), могут возникнуть конфликты.
Методика отладки:
- Проверьте, что политика создана и применяется:
kubectl describe networkpolicy <имя> -n <неймспейс>. - Проверьте, что CNI-плагин поддерживает NetworkPolicy. Для Calico:
kubectl get pods -n kube-system | grep calico. Поды calico-node должны быть в статусе Running. - Из временного пода выполните
nc -zv <сервис> <порт>илиcurl -v <сервис>:<порт>. Таймаут означает блокировку. - Проверьте логи CNI-плагина:
kubectl logs -n kube-system <под-cni>. В логах Calico ищите строки с DENY.
Для комплексного подхода к сетевой безопасности контейнеров с примерами iptables и YAML-манифестами используйте руководство по сетевой безопасности контейнеров.
Выбор и настройка CNI-плагина: Calico vs Flannel
CNI (Container Network Interface) - спецификация, по которой плагины настраивают сеть для подов. Плагин назначает подам IP-адреса, строит маршруты между узлами и реализует сетевые политики. Выбор плагина определяет производительность сети и доступные функции безопасности.
Flannel - минималистичный плагин. Создаёт overlay-сеть поверх физической: все поды получают IP из одной подсети, трафик между узлами инкапсулируется в VXLAN или отправляется через host-gw (прямая маршрутизация без инкапсуляции, если узлы в одном L2-сегменте). Настройка сводится к выбору режима в ConfigMap. Flannel не поддерживает NetworkPolicy - все поды могут общаться без ограничений.
Calico - плагин с полной поддержкой сетевых политик. Может работать в режиме overlay (IP-in-IP, VXLAN) и underlay (BGP-пиринг с физической сетью). В underlay-режиме поды получают IP из корпоративной сети и маршрутизируются напрямую, без инкапсуляции - это снижает накладные расходы. Calico реализует NetworkPolicy и собственные расширенные политики (GlobalNetworkPolicy) с приоритетами и логированием.
Сравнительная таблица:
| Характеристика | Flannel | Calico |
|---|---|---|
| Производительность | Средняя (оверлей VXLAN) | Высокая (BGP без инкапсуляции) |
| NetworkPolicy | Нет | Да |
| Сложность настройки | Низкая | Средняя |
| Режимы работы | VXLAN, host-gw | IP-in-IP, VXLAN, BGP |
| Сценарий | Тестовые среды, небольшие кластеры | Production, требования безопасности |
Рекомендация: для тестовых и dev-сред выбирайте Flannel - он разворачивается одной командой. Для production-кластеров, где нужна изоляция микросервисов, выбирайте Calico. Если кластер развёрнут в облаке, изучите нативные CNI-плагины провайдера - они часто оптимизированы под конкретную инфраструктуру.
Диагностика и отладка сетевых проблем в Kubernetes
Системный подход к диагностике экономит часы. При проблемах с доступностью сервиса двигайтесь по цепочке: под → сервис → DNS → сетевые политики → CNI.
Стартовая проверка - эндпоинты сервиса. Сервис направляет трафик только на поды, которые проходят readiness-пробу и имеют соответствующие метки.
kubectl get endpoints <имя-сервиса> -n <неймспейс>
Если список эндпоинтов пуст, проверьте selector сервиса и метки подов - они должны совпадать. Проверьте статус подов: kubectl get pods -l <метки>. Поды в статусе Running не гарантируют готовность - readiness-проба может возвращать ошибку.
Следующий шаг - проверка связности из временного пода:
kubectl run test --rm -it --image=busybox -- sh
# Внутри пода:
nslookup <имя-сервиса>
nc -zv <имя-сервиса> <порт>
Если nslookup возвращает IP, но соединение не устанавливается, проблема на уровне kube-proxy или CNI. Если nslookup не разрешает имя - проблема в DNS.
Проверка DNS-резолвинга сервисов
CoreDNS (или kube-dns в старых кластерах) создаёт DNS-записи для каждого сервиса. Формат A-записи: <имя-сервиса>.<неймспейс>.svc.cluster.local. Для headless-сервисов (clusterIP: None) создаются A-записи на IP каждого пода, а также SRV-записи для портов.
Типовые ошибки DNS:
- CoreDNS не работает - проверьте поды в kube-system:
kubectl get pods -n kube-system -l k8s-app=kube-dns. - Некорректный домен - используйте полное имя с суффиксом .svc.cluster.local.
- Задержки обновления записей - TTL для сервисных записей по умолчанию 30 секунд. После удаления пода его IP может оставаться в DNS до истечения TTL.
Проверка из временного пода:
nslookup backend.production.svc.cluster.local
# Ожидаемый вывод: IP сервиса backend
Скрипты для быстрой отладки сети
Скрипт проверки связности со всеми подами сервиса - опрашивает эндпоинты и тестирует TCP-соединение:
#!/bin/bash
SERVICE=$1
NAMESPACE=${2:-default}
PORT=${3:-80}
ENDPOINTS=$(kubectl get endpoints $SERVICE -n $NAMESPACE -o jsonpath='{.subsets[*].addresses[*].ip}')
for IP in $ENDPOINTS; do
echo -n "$IP:$PORT "
timeout 2 bash -c "echo >/dev/tcp/$IP/$PORT" 2>/dev/null && echo "OK" || echo "FAIL"
done
Скрипт дампа правил iptables для конкретного сервиса - фильтрует цепочки по ClusterIP:
#!/bin/bash
CLUSTER_IP=$1
iptables-save | grep -A 5 -B 2 $CLUSTER_IP
Используйте эти скрипты как основу для собственного инструментария. Для автоматизации рутинных задач можно задействовать AiTunnel - агрегатор API для нейросетей, который через единый интерфейс генерирует скрипты диагностики под конкретную конфигурацию кластера.
Балансировка нагрузки и session affinity в сервисах Kubernetes
По умолчанию kube-proxy балансирует запросы между подами случайным образом. Каждый новый TCP-сеанс направляется на случайный под. Для stateless-приложений это оптимально - нагрузка распределяется равномерно.
Для stateful-приложений, где важна привязка сессии клиента к конкретному поду, настройте session affinity. Kubernetes поддерживает affinity на основе IP клиента:
apiVersion: v1
kind: Service
metadata:
name: stateful-app
spec:
selector:
app: stateful
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
ports:
- port: 80
Параметр timeoutSeconds задаёт время жизни привязки (по умолчанию 10800 секунд, 3 часа). Все запросы с одного IP клиента в течение этого периода направляются на один и тот же под.
Ограничения sessionAffinity: ClientIP:
- Не работает за NAT - все клиенты за одним NAT-шлюзом считаются одним IP.
- Не подходит для горизонтального масштабирования с резкими всплесками - один под может получить всю нагрузку от крупного клиента.
Альтернативы для более тонкого управления: ingress-контроллеры с sticky sessions на основе cookie (Nginx Ingress, Traefik), service mesh (Istio, Linkerd) с маршрутизацией по заголовкам. Эти инструменты выходят за рамки базовой маршрутизации kube-proxy. Практические сценарии организации маршрутизации между сервисами с операторами и интеграцией очередей сообщений рассмотрены в статье о маршрутизации между сервисами в Kubernetes.