Маршрутизация в Kubernetes: полное руководство по настройке трафика между подами и сервисами | AdminWiki

Маршрутизация в Kubernetes: полное руководство по настройке трафика между подами и сервисами

28 июля 2026 10 мин. чтения

Как работает маршрутизация в 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), могут возникнуть конфликты.

Методика отладки:

  1. Проверьте, что политика создана и применяется: kubectl describe networkpolicy <имя> -n <неймспейс>.
  2. Проверьте, что CNI-плагин поддерживает NetworkPolicy. Для Calico: kubectl get pods -n kube-system | grep calico. Поды calico-node должны быть в статусе Running.
  3. Из временного пода выполните nc -zv <сервис> <порт> или curl -v <сервис>:<порт>. Таймаут означает блокировку.
  4. Проверьте логи 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) с приоритетами и логированием.

Сравнительная таблица:

ХарактеристикаFlannelCalico
ПроизводительностьСредняя (оверлей VXLAN)Высокая (BGP без инкапсуляции)
NetworkPolicyНетДа
Сложность настройкиНизкаяСредняя
Режимы работыVXLAN, host-gwIP-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.

Поделиться:
Сохранить гайд? В закладки браузера