Service и Ingress: кто за что отвечает
Service даёт стабильную точку доступа к подам и балансирует TCP/UDP-соединения на уровне L4. Ingress маршрутизирует HTTP и HTTPS по доменам и путям на уровне L7. Первый ресурс работает с IP и портами, второй читает заголовок Host и URL запроса. Заменить одно другим нельзя: для публикации веб-приложения нужны оба.
Service создаёт виртуальный IP из сервисной подсети и DNS-имя вида my-app-service.default.svc.cluster.local. Трафик на этот адрес kube-proxy или eBPF-реализация вроде Cilium раскидывает по готовым подам. Поды пересоздаются при деплое, их IP меняются, а ClusterIP и DNS остаются на месте. В этом главная ценность ресурса: стабильный адрес поверх нестабильного набора подов.
Ingress описывает правила: какой хост и путь ведут к какому сервису, где завершать TLS. Сам ресурс трафик не проксирует. Правила читает Ingress Controller (NGINX Ingress, Traefik, HAProxy, Contour) и переносит их в конфигурацию своего прокси. Ingress стоит на L7: видит HTTP-заголовки, переписывает пути, отдаёт редиректы, ограничивает частоту запросов.
| Критерий | Service | Ingress |
|---|---|---|
| Уровень OSI | L4, TCP и UDP | L7, HTTP и HTTPS |
| Что адресует | IP и порт подов | Хост и путь запроса |
| Типы трафика | любой TCP/UDP, включая БД и очереди | только HTTP/HTTPS |
| Точка входа снаружи | NodePort или LoadBalancer | Ingress Controller за NodePort или LoadBalancer |
| TLS-терминация | нет, шифрование заканчивается на балансировщике или выше | да, сертификаты из Secret, обновление через cert-manager |
| Маршрутизация по путям | нет | да, path и pathType |
| Зависимости | ни от чего не зависит | нужен контроллер и ClusterIP Service в backend |
Подробный разбор уровней с манифестами всех типов сервисов и сравнением контроллеров собран в статье Маршрутизация в Kubernetes: Service, Ingress и Gateway API на уровнях L4 и L7.
Для не-HTTP трафика Ingress не подходит: он разбирает только HTTP и HTTPS. PostgreSQL, Redis, MQTT, произвольный TCP и gRPC-вызовы без HTTP публикуют через Service типа LoadBalancer или NodePort.
Почему Ingress не может работать без Service
Ingress это декларация правил, а не прокси. Фактический трафик принимает под Ingress Controller, который работает в кластере отдельным Deployment и виден снаружи через сервис типа LoadBalancer или NodePort.
Путь запроса выглядит так: внешний клиент, DNS, адрес Ingress Controller, правило Ingress по host и path, ClusterIP Service, под. На последнем шаге Service сам выбирает, какой из готовых подов получит соединение.
Без Service у контроллера нет стабильной цели. IP подов меняются при каждом рестарте и деплое, поэтому зашивать их в правила бессмысленно: в backend Ingress указывают имя сервиса и номер порта. Некоторые контроллеры умеют балансировать напрямую по EndpointSlice в обход kube-proxy, но это нестандартный режим: метрики сервиса теряются, поведение зависит от вендора. Для продакшена держите связку Ingress плюс ClusterIP Service.
Ключевые сущности: ClusterIP, NodePort, LoadBalancer и Ingress
ClusterIP это тип по умолчанию: виртуальный адрес, доступный только внутри кластера. При инициализации через kubeadm сервисная подсеть задаётся параметром --service-cidr, например 10.96.0.0/12, и все ClusterIP выделяются из неё (документация Kubernetes по интеграции kubelet и kubeadm).
NodePort открывает один и тот же порт (по умолчанию из диапазона 30000-32767) на каждом узле, и запрос на любой IP узла доходит до подов. LoadBalancer просит у облачного провайдера внешний балансировщик с публичным адресом: каждый такой сервис оплачивается отдельно.
Ingress не тип Service, а отдельный ресурс API. Сравнение типов и критерии выбора с манифестами разобраны в материале Как выбрать тип Service в Kubernetes: ClusterIP, NodePort или LoadBalancer. Короткое правило: для веб-приложения берите Ingress и ClusterIP Service, для всего остального LoadBalancer или NodePort.
Схема работы: как внешний трафик доходит до подов
- Клиент открывает my-app.example.com, DNS возвращает публичный адрес Ingress Controller.
- Ingress Controller принимает HTTP-запрос и сопоставляет заголовок Host и путь с правилами ресурсов Ingress.
- Совпавшее правило указывает на ClusterIP Service с портом 80.
- kube-proxy переводит трафик на один из готовых подов по алгоритму балансировки.
- Под отвечает, ответ возвращается по тому же маршруту.
Схема без графических редакторов выглядит так:
внешний клиент
-> DNS: my-app.example.com, A-запись 203.0.113.10 (IP Ingress Controller)
-> Ingress Controller, поды в namespace ingress-nginx
-> правило: host my-app.example.com, path /
-> ClusterIP Service my-app-service, 10.96.0.15:80
-> kube-proxy выбирает под 10.244.1.7:8080
-> ответ клиенту
Ingress Controller нужно ставить в кластер отдельно: без него ресурс Ingress создастся, но останется без адреса и без обработки правил.
Роль Ingress Controller и IngressClass
IngressClass указывает, какой контроллер обслуживает конкретный Ingress, если в кластере их несколько. В манифесте это поле spec.ingressClassName: nginx. Если поле не задано и в кластере нет класса по умолчанию, контроллер просто не возьмёт ресурс в работу, а в статусе Ingress не появится ADDRESS.
Проверьте установленные классы командой kubectl get ingressclass. Метка kubernetes.io/ingress.class в аннотациях устарела, используйте поле ingressClassName.
Как Service выбирает поды: селекторы и эндпоинты
Service находит поды по label selector. Совпавшие поды с пройденным readiness probe попадают в объект EndpointSlice, и только по ним идёт балансировка. Если селектор не совпал ни с одним подом, список эндпоинтов пуст, и трафик никуда не уходит, хотя сам Service существует и выглядит рабочим.
Проверка занимает секунду: kubectl get endpoints my-app-service. Пустой столбец ENDPOINTS означает проблему с метками или с пробой готовности. Ingress в свою очередь обращается к сервису по имени и порту, а не к подам напрямую.
Готовые манифесты: Service и Ingress для одного приложения
Ниже рабочая пара манифестов для приложения my-app, которое слушает порт 8080 и публикуется на домене my-app.example.com. Замените имя, домен и порт на свои значения.
Манифест Service: ключевые поля и пояснения
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: ClusterIP
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
Разбор полей: selector должен точно совпадать с метками подов, иначе эндпоинтов не будет. port это порт самого сервиса, на него смотрит Ingress, targetPort это порт контейнера. Если targetPort не указан, он равен port. Для двух портов, например http и metrics, задайте двум записям в ports разные значения name, иначе API отклонит манифест.
Манифест Ingress: ключевые поля и пояснения
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- my-app.example.com
secretName: my-app-tls
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
Поля: host фильтрует трафик по домену, path и pathType описывают маршрут, backend.service.name и backend.service.port.number указывают на ClusterIP Service. Значение pathType Prefix покрывает все вложенные пути, Exact совпадает только целиком, ImplementationSpecific оставляет логику контроллеру. Блок tls с secretName включает HTTPS: сертификат кладётся в Secret, а cert-manager умеет выпускать и продлевать его автоматически.
Несколько приложений разводят по путям: /api на api-service, / на frontend-service. Для подрезки префикса у NGINX Ingress применяют аннотацию nginx.ingress.kubernetes.io/rewrite-target: /. Готовые примеры path-based и host-based правил с разбором ошибок 404 и 502 собраны в статье Маршрутизация по URL в Kubernetes Ingress: настройка правил для микросервисов.
Применение и проверка:
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl get svc my-app-service
kubectl get ingress my-app-ingress
kubectl describe ingress my-app-ingress
Ещё один практический набор манифестов с разбором полей и типичных ошибок портов есть в руководстве Service и Ingress в Kubernetes 2026: руководство по настройке внутреннего и внешнего доступа.
Когда достаточно Service, а когда нужен Ingress
| Задача | Что использовать |
|---|---|
| HTTP-обмен между микросервисами внутри кластера | ClusterIP Service |
| Внутренний трафик с требованием локальности | ClusterIP Service с internalTrafficPolicy: Local |
| Внешний доступ к TCP/UDP: PostgreSQL, Redis, MQTT | Service типа LoadBalancer или NodePort |
| Одно веб-приложение без маршрутизации по путям | Service типа LoadBalancer, Ingress даёт больше гибкости |
| Несколько приложений на одном IP с маршрутизацией по хостам и путям | Ingress плюс ClusterIP Services |
| TLS с автоматическим продлением сертификатов | Ingress плюс cert-manager |
| WAF, rate limiting, аутентификация на L7 | Ingress |
Сценарии, где Service незаменим
- Внутренние вызовы между микросервисами: ClusterIP даёт DNS-имя, которое не меняется при деплое.
- Не-HTTP протоколы: PostgreSQL на 5432, Redis на 6379, MQTT, произвольный TCP или UDP. Ingress их не разберёт.
- Доступ к подам по стабильному адресу внутри кластера без публикации наружу.
- Локальность: ClusterIP с internalTrafficPolicy: Local удерживает соединения в пределах одного узла.
Сценарии, где Ingress даёт преимущества
- Маршрутизация по хостам: a.example.com на service-a, b.example.com на service-b, один публичный IP на все приложения.
- Маршрутизация по путям: /api на api-service, /static на static-service.
- TLS-терминация на одном прокси с разными сертификатами для разных доменов.
- Единые L7-политики: ограничение частоты запросов, WAF, basic auth, редирект с HTTP на HTTPS.
- Экономия: один Ingress Controller обслуживает десятки сервисов вместо отдельного облачного балансировщика на каждый.
Для AI-нагрузок есть отдельное ограничение: Ingress может обрывать соединение при стриминге ответов LLM, если таймауты прокси меньше времени генерации (How to Diagnose and Fix AI Inference Latency on Kubernetes). В опросе 200 специалистов, запускающих AI в продакшене, 49,5% назвали задержку при пиковой нагрузке самой сложной проблемой масштабирования, при этом маршрутизация и очередь относятся к инфраструктурным этапам и не связаны с работой модели. Проверьте аннотации таймаутов и буферизации у своего контроллера, прежде чем публиковать стриминговый эндпоинт.
Безопасность и производительность: что учесть при выборе
Ingress даёт одну входную точку и централизованные правила: TLS, WAF, ограничение частоты, аутентификация настраиваются в одном месте. Service типа LoadBalancer на каждый сервис открывает отдельный публичный адрес, и поверхность атаки растёт вместе с числом сервисов. Внутренние сервисы держите на ClusterIP и закрывайте NetworkPolicy.
TLS-терминация: где её лучше делать
Терминация на Ingress удобнее: сертификат лежит в Secret, cert-manager продлевает его без ручных действий, один прокси обслуживает десятки доменов. Учтите, что после терминации трафик внутри кластера идёт без шифрования, если не настроен mTLS через service mesh. Service типа LoadBalancer тоже умеет завершать TLS, если это поддерживает облачный балансировщик, но такой вариант привязывает вас к возможностям провайдера.
Service Internal Traffic Policy: когда включать
Поле internalTrafficPolicy: Local заставляет kube-proxy фильтровать эндпоинты так, чтобы внутренний трафик кластера шёл только к подам на том же узле, откуда пришёл запрос. Отказ от лишнего круга по кластерной сети снижает задержку, повышает пропускную способность и сокращает расходы на трафик между зонами (Politique de trafic interne des Services | Kubernetes).
Важная деталь: если на узле нет ни одного эндпоинта сервиса, для подов этого узла сервис ведёт себя так, будто эндпоинтов нет вообще, даже когда они есть на других узлах. Значение по умолчанию Cluster учитывает все эндпоинты кластера. Включайте Local для внутренних сервисов, где важна локальность, и не включайте для трафика, который приходит извне через Ingress.
Планирование размещения влияет на задержку не меньше: 45,5% респондентов того же опроса обслуживают инференс из одного облачного региона, хотя 59,5% считают важным размещать вычисления ближе к пользователям. Один центральный Ingress на несколько регионов усиливает эту проблему.
- Используйте ClusterIP для всех внутренних сервисов.
- Завершайте TLS на Ingress и продлевайте сертификаты автоматически.
- Ограничивайте доступ к сервисам типа LoadBalancer, если они всё же нужны.
- Включайте internalTrafficPolicy: Local там, где важна локальность, и проверяйте, что на каждом узле есть эндпоинты.
- Следите за задержкой и пропускной способностью через метрики контроллера и кластера.
Проверка конфигурации: команды и диагностика
Базовый набор команд для проверки связки:
kubectl get svc
kubectl get ingress
kubectl describe svc my-app-service
kubectl describe ingress my-app-ingress
kubectl get endpoints my-app-service
kubectl get pods -l app=my-app
Доступность через заголовок Host проверяется так:
curl -H 'Host: my-app.example.com' http://203.0.113.10/
Если в столбце ADDRESS у Ingress пусто, смотрите, запущен ли контроллер и совпадает ли ingressClassName. Если у Service пустой список эндпоинтов, проверьте метки подов и readiness probe. Метрики и алерты по маршрутизации удобно строить на метриках контроллера, примеры мониторинга собраны в статье Маршрутизация запросов в Kubernetes Ingress: от правил до мониторинга для SRE.
Диагностика типичных ошибок
- Ingress Controller не установлен: у Ingress нет адреса, событий о применении правил тоже нет.
- Неправильный номер порта в backend: запрос доходит до контроллера и получает 502.
- Селектор Service не совпадает с метками подов: эндпоинтов нет, трафик не идёт.
- Один и тот же host в двух Ingress без пересечения путей: правила конфликтуют, часть запросов получает 404.
- Просроченный TLS-сертификат: браузер показывает ошибку, kubectl describe ingress покажет проблему с Secret.
- Стриминг ответов LLM: контроллер обрывает длинную генерацию из-за таймаутов и буферизации, лечится настройкой аннотаций.
Для разбора инцидентов смотрите логи пода контроллера и события кластера:
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=200
kubectl get events --sort-by=.lastTimestamp
Порядок диагностики: контроллер запущен, IngressClass совпадает, Service имеет эндпоинты, DNS указывает на адрес контроллера, сертификат действителен.
Итог: алгоритм выбора для вашего приложения
- Трафик не HTTP и не HTTPS (базы данных, брокеры, чистый TCP/UDP)? Берите Service типа LoadBalancer или NodePort.
- Трафик HTTP/HTTPS и нужен доступ снаружи? Ставьте Ingress Controller и создавайте Ingress плюс ClusterIP Service.
- Нужна маршрутизация по хостам и путям, TLS, WAF или rate limiting? Только Ingress.
- Сервис внутренний? ClusterIP, при необходимости с internalTrafficPolicy: Local.
- Публикуете одно приложение без маршрутизации? Service типа LoadBalancer сработает, но Ingress дешевле в масштабе и гибче.
Краткая шпаргалка: Service или Ingress
| Тип трафика и задача | Ресурс |
|---|---|
| HTTP/HTTPS снаружи с маршрутизацией по хостам или путям | Ingress плюс ClusterIP Service |
| HTTP/HTTPS снаружи без маршрутизации | Service типа LoadBalancer или Ingress |
| HTTP/HTTPS внутри кластера | ClusterIP Service |
| Не-HTTP: TCP, UDP, БД, очереди | Service типа LoadBalancer или NodePort |
| Внутренний трафик с требованием локальности | ClusterIP Service с internalTrafficPolicy: Local |
Ключевой принцип остаётся неизменным: Ingress не заменяет Service, а работает с ним в паре. Описанные поля и API networking.k8s.io/v1 актуальны для Kubernetes 1.29 и новее, поле ingressClassName используйте вместо устаревшей аннотации.
Возьмите манифесты из статьи, примените их в тестовом namespace и сверьте вывод kubectl get endpoints и kubectl get ingress перед выкаткой в прод. Если эндпоинты на месте, а ADDRESS заполнен, маршрут от внешнего клиента до подов собран правильно.