Введение: зачем разделять маршрутизацию на L4 и L7
Service в Kubernetes обрабатывает трафик на уровне L4: балансирует TCP- и UDP-соединения между подами и не разбирает содержимое запроса. Ingress и Gateway API работают на уровне L7: читают HTTP-запрос и выбирают backend по хосту, пути или заголовку. Разделение простое: L4 знает только адрес и порт, L7 понимает протокол приложения.
На L4 балансировщик видит IP источника, порт назначения и факт открытия сессии. Полезная нагрузка для него - поток байт. Backend выбирается один раз при установке соединения, и весь трафик сессии уходит на один под.
На L7 прокси разбирает метод, host, path, заголовки и cookie. Он завершает TLS, меняет маршрут на каждый запрос, повторяет неудачную попытку на другую реплику, применяет rate limit. Плата за это - дополнительный сетевой хоп, больше CPU и ещё одна точка отказа.
Разница видна на канареечном релизе. Service с общим селектором раскидывает запросы по всем подам, включая канареечные. В официальном туториале Kubernetes стабильная версия запущена в трёх репликах, канареечная в одной, и распределение получается примерно 75% на 25%, потому что балансировка идёт по всем четырём подам. Точное управление долей трафика требует уже L7: правила по заголовку, cookie или весу backend.
Ошибка выбора уровня даёт две крайности. Ingress в кластере, где нужен только TCP-доступ к базе данных, добавляет контроллер, сертификаты и лишний слой для отладки. Отказ от L7 там, где нужны виртуальные хосты и терминация TLS, приводит к отдельному внешнему IP и порту на каждый сервис, а маршрутизация уезжает в DNS.
Service: маршрутизация на уровне L4
Service связывает группу подов через селектор меток и выдаёт им один стабильный адрес. Под пересоздаётся, его IP меняется, имя сервиса и виртуальный IP остаются. Пересылку обеспечивает kube-proxy на каждой ноде: он программирует правила iptables или IPVS, и запрос на виртуальный IP попадает на один из готовых подов из Endpoints.
Готовность подов определяет, попадёт ли под в балансировку. Пока readiness probe не прошла, под исключён из Endpoints и запросы на него не идут. Это главный механизм, который спасает от 502 при обновлении приложения.
ClusterIP: внутренний доступ
ClusterIP - тип по умолчанию. Он выдаёт виртуальный IP, доступный только внутри кластера, и DNS-имя вида my-service.my-namespace.svc.cluster.local.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
Так сервис выглядит со стороны кластера: в туториале по канареечному развёртыванию сервис rollout-demo-service получает CLUSTER-IP 10.96.123.45 и публикует порт 80/TCP. Значение port - это порт сервиса, targetPort - порт контейнера. Если targetPort не указан, он совпадает с port, и это частая причина недоступности приложения, слушающего нестандартный порт.
Сценарии: трафик между микросервисами, подключение к базе данных, backend для Ingress-контроллера. Наружу такой сервис не выставляется. Готовые примеры селекторов и разбор ошибок с портами собраны в практическом руководстве по Service и Ingress.
NodePort: доступ через порт на каждой ноде
NodePort открывает один и тот же порт на всех нодах кластера, обычно из диапазона 30000-32767. Запрос на IP любой ноды и этот порт через kube-proxy попадает на поды сервиса.
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080
Что важно помнить: порт слушают все ноды, включая те, что смотрят в интернет. Firewall и security groups настраивают отдельно, а смена IP ноды ломает клиентов, которые его закешировали. Для отладки и временного доступа NodePort удобен, для публикации продакшн-приложения чаще берут LoadBalancer или Ingress.
LoadBalancer: внешний балансировщик от облачного провайдера
Тип LoadBalancer просит облачного провайдера создать внешний балансировщик и выдать сервису внешний IP. В managed Kubernetes это работает сразу после установки cloud controller manager.
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: my-app
ports:
- port: 443
targetPort: 8443
externalTrafficPolicy: Local сохраняет IP клиента и убирает лишний хоп, но трафик идёт только на поды той ноды, куда пришёл запрос. Режим Cluster (по умолчанию) балансирует шире, зато маскирует адрес клиента.
Экономика здесь решает. Каждый сервис типа LoadBalancer обычно получает отдельный внешний IP и отдельную плату, поэтому десяток микросервисов превращается в десяток балансировщиков, а HTTP-трафик сводят в один Ingress. Если кластер разворачивается в облаке, готовый балансировщик экономит время: Timeweb Cloud предоставляет облачную инфраструктуру, включая серверы, VDS/VPS и Kubernetes. Для self-managed окружений без облачного API применяют MetalLB или аналоги: он раздаёт адреса из выделенного пула, и этот пул нужно согласовать с сетевой командой.
| Тип Service | Кто видит сервис | Типичный сценарий | Ограничения |
|---|---|---|---|
| ClusterIP | Только внутри кластера | Связь микросервисов, база данных, backend для Ingress | Нет внешнего доступа |
| NodePort | Любой, кто достучится до ноды и порта | Отладка, временный доступ, bare-metal | Порт из диапазона 30000-32767 на всех нодах, ручной firewall |
| LoadBalancer | Из интернета или внешней сети | Публикация сервиса с постоянным внешним IP | Отдельный IP и плата за каждый сервис |
NodePort и LoadBalancer включают в себя ClusterIP: внутри кластера такой сервис всё равно доступен по виртуальному адресу.
Ingress и Gateway API: маршрутизация на уровне L7
Ingress и Gateway API описывают правила для HTTP и HTTPS. Сами ресурсы трафик не обрабатывают: правила читает контроллер и настраивает свой прокси. Разница в модели конфигурации и наборе возможностей.
Ingress: классический подход к L7-маршрутизации
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: example.com
http:
paths:
- path: /app
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
pathType: Prefix совпадает по префиксу пути, Exact требует полного соответствия. TLS описывают в секции spec.tls с именем Secret, где лежат сертификат и ключ. Расширенные функции (rewrite, rate limit, sticky sessions, basic auth) включаются аннотациями, и набор аннотаций у каждого контроллера свой. Перенос конфигурации с одного контроллера на другой означает переписывание аннотаций, а не копирование файла.
Протоколы: Ingress рассчитан на HTTP и HTTPS. TCP и UDP через него не маршрутизируют, для них настраивают отдельный ConfigMap контроллера или сервис типа LoadBalancer. Отдельный случай - WebSocket: соединение начинается как обычный HTTP-запрос с предложением сменить протокол, и только после подтверждения сервера канал перестаёт быть обменом по схеме запрос-ответ (разбор работы WebSocket). L7-прокси должен пропускать заголовок Upgrade, иначе чат или поток метрик не поднимется.
Gateway API: современная замена Ingress
Gateway API разносит конфигурацию по трём ресурсам с разными зонами ответственности: GatewayClass задаёт контроллер, Gateway описывает точку входа и слушателей, HTTPRoute содержит правила маршрутизации. Инфраструктурная команда держит Gateway, команда приложения - свои HTTPRoute в собственном namespace.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
spec:
parentRefs:
- name: my-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 80
Возможностей больше, чем у Ingress: вес backend для канареечных релизов, зеркалирование трафика, маршрутизация по заголовкам и query-параметрам, разделение прав между командами. Envoy Gateway - одна из реализаций стандарта, и таких реализаций несколько. Практические сценарии с Gateway API разобраны в статье о маршрутизации между сервисами.
Ingress остаётся рабочим вариантом: он стабилен, поддерживается основными контроллерами и закрывает типовые задачи виртуальных хостов и терминации TLS. Для новых кластеров с микросервисами и требованиями к канареечным релизам Gateway API даёт больше без вендорских аннотаций.
Сравнение Ingress-контроллеров: Ingress NGINX, Traefik, Envoy Gateway
Контроллер определяет, какие аннотации и CRD доступны, как быстро применяется конфигурация и что попадёт в метрики.
| Критерий | Ingress NGINX | Traefik | Envoy Gateway |
|---|---|---|---|
| Модель конфигурации | Ingress и аннотации, часть настроек через ConfigMap | Ingress плюс собственные CRD и динамические провайдеры | Ресурсы Gateway API: GatewayClass, Gateway, HTTPRoute |
| Расширенные L7-функции | Аннотации вида nginx.ingress.kubernetes.io/... | Middleware: заголовки, редиректы, ограничения | Веса backend, зеркалирование, детальные правила matches |
| TCP и UDP | Через отдельную конфигурацию контроллера | Поддерживаются штатно | Через ресурсы Gateway API |
| Типичный сценарий | Стандартная HTTP-маршрутизация в кластере | Динамические окружения, автоматический TLS | Сложные L7-сценарии на базе Envoy |
| Порог входа | Низкий, много готовых рецептов | Средний, своя модель CRD | Средний и выше, требуется понимание Gateway API |
Ingress NGINX выбирают за предсказуемость и объём публичных примеров: конфигурация задаётся аннотациями, TLS-терминация и заголовки настраиваются без сторонних компонентов, а метрики легко снимать для SRE-дашбордов. Правила по хостам и путям, TLS через cert-manager и канареечные релизы с контролем веса трафика подробно разобраны в руководстве по Ingress от правил до мониторинга.
Traefik силён там, где окружение меняется часто: динамическая конфигурация, автоматический выпуск сертификатов и поддержка TCP/UDP из коробки. Envoy Gateway стоит смотреть, если вы уже строите сеть вокруг Gateway API или нужны зеркалирование и тонкие правила matches.
Сравнивать контроллеры по «скорости» в отрыве от конфигурации бессмысленно: результат зависит от версии, числа правил, размера TLS-сертификатов и профиля трафика. Корректные цифры даёт только нагрузочный тест на вашем сценарии, а детали API и аннотаций стоит сверять с документацией той версии контроллера, которая стоит в кластере.
Сетевые политики и маршрутизация между подами
По умолчанию любой под может обратиться к любому. NetworkPolicy ограничивает это на уровнях L3 и L4: выбирает поды по меткам и описывает, какой трафик к ним допустим.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-frontend
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 80
Как только политика с policyTypes: Ingress выбирает под, весь остальной входящий трафик к нему блокируется. Это главный источник внезапных сбоев: приложение перестаёт получать ответы от DNS или от сборщика метрик, если правило не разрешает эти направления явно.
Политики работают только там, где их умеет применять CNI. Calico и Cilium такую возможность поддерживают, часть простых плагинов объекты NetworkPolicy игнорирует: манифест создастся без ошибок и ничего не ограничит. Проверяют поведением: запускают под-нарушитель и убеждаются, что соединение не проходит.
Осторожность оправдана и на уровне платформы: правки сетевого доступа, ролей и admission-политик затрагивают весь кластер, а не одно приложение (разбор аудита и сетевых прав в Kubernetes). Держите включённым аудит API-сервера: политика аудита определяет, какие обращения записывать и насколько подробно, от фиксации факта вызова до полного тела запроса и ответа. Так проще найти, кто и когда изменил правило, после которого развалилась маршрутизация. Контроль входящего и исходящего трафика, включая egress, разобран в отдельном руководстве по управлению трафиком.
Типичные ошибки и рекомендации
Ниже то, что чаще всего ломает маршрутизацию, и как это ловить.
- Пустой Endpoints из-за селектора. Сервис создан, но kubectl get endpoints показывает none. Причина почти всегда в несовпадении меток селектора и меток подов либо в чужом namespace. Проверяйте kubectl get pods --show-labels и kubectl describe svc.
- Ingress есть, ответ 404. Объект Ingress без установленного контроллера не делает ничего. Проверьте, какой IngressClass используется и совпадает ли он с spec.ingressClassName.
- Порт NodePort вне диапазона или занят. Значение должно попадать в 30000-32767, иначе API отклонит манифест. Конфликты портов между сервисами исключайте заранее.
- Слишком широкая NetworkPolicy. Правило, разрешающее трафик только от frontend, попутно рубит DNS и сбор метрик. Разрешайте нужные направления явно, включая UDP/53 к kube-dns.
- Отсутствие readiness probe. Без проверки готовности трафик идёт на ещё стартующий под, и пользователь получает 502 во время rolling update.
Отдельная тема - control plane. Для отказоустойчивого etcd нужны минимум три хоста, которые видят друг друга по TCP-портам 2379 и 2380 (руководство по настройке HA etcd с kubeadm). kubeadm по умолчанию поднимает локальный etcd на каждой ноде control plane и допускает внешний etcd-кластер на отдельных хостах. Если нода control plane одна, отказоустойчивости нет, сколько бы реплик приложения вы ни запустили.
Порядок диагностики: сначала селекторы и Endpoints, затем слой L7 (IngressClass, правила, аннотации), затем сетевые политики. Новые NetworkPolicy прогоняйте в staging на копии манифестов. Держите метрики задержки и кодов ответов на входе в кластер, чтобы отличать проблему маршрутизации от проблемы самого приложения.
Заключение: как выбрать подход
Алгоритм короткий. Внутренний трафик между сервисами - ClusterIP. Нужен внешний доступ без HTTP-маршрутизации - NodePort для отладки и LoadBalancer для постоянного внешнего IP. Нужны виртуальные хосты и терминация TLS - Ingress, а если требуются канареечные релизы по весу, зеркалирование и разделение зон ответственности между командами - Gateway API. Изоляция трафика между подами - NetworkPolicy поверх CNI, который её поддерживает.
Лишние уровни не добавляйте. Ingress-контроллер над чистым TCP-сервисом, второй прокси поверх уже работающего и NetworkPolicy «на всякий случай» в кластере с непроверенным CNI дают работу без результата. Каждый слой должен отвечать на конкретную задачу: доступ снаружи, маршрутизация по HTTP или изоляция трафика.
Проверить конфигурацию перед выкаткой в продакшн удобно на отдельном кластере: Timeweb Cloud предоставляет облачную инфраструктуру с Kubernetes, где тестовое окружение поднимается быстрее, чем на локальном железе. Напишите в комментариях, какой стек маршрутизации используете вы и какие ошибки встречали: это поможет дополнить статью разобранными сценариями.