Если в лоб давать каждому сервису свой внешний адрес через type: LoadBalancer, ты получишь двадцать публичных IP (а у облака каждый балансировщик - это деньги), двадцать раз настроенный TLS и ноль централизованного контроля над маршрутизацией. Ingress решает ровно эту боль: один вход, умные правила, один TLS-терминатор. Разберём, как это реально устроено под капотом, потому что вокруг Ingress больше всего мифов и граблей у новичков.
Что такое Ingress и чего он сам по себе НЕ делает
Ingress - это объект API, набор правил маршрутизации HTTP и HTTPS трафика внутрь кластера. Ключевое слово - правила. Сам по себе объект Ingress не маршрутизирует ничего. Это как заявка на маршрутизацию, лист бумаги с инструкцией хост такой-то, путь такой-то - отправить на такой-то Service. Кто эту бумагу читает и исполняет - отдельная сущность, Ingress-контроллер.
Это первая и самая частая грабля. Человек создаёт манифест Ingress в голом кластере (например, в свежем kubeadm или minikube без аддонов), делает kubectl get ingress, видит объект - а трафик не идёт, поле ADDRESS пустое. Потому что исполнять правила некому. В управляемых кластерах ситуация разная: в Yandex Managed Service for Kubernetes ALB Ingress Controller ставится как отдельный аддон, в Deckhouse модуль ingress-nginx включается флагом, в k3s из коробки приезжает Traefik. Но базовый Kubernetes контроллер не несёт - его ставишь ты.

Ingress-контроллер: кто реально исполняет правила
Контроллер - это под (обычно Deployment или DaemonSet), внутри которого крутится настоящий обратный прокси: nginx, Traefik, Envoy, HAProxy. Он подписан на API-сервер и следит за объектами Ingress. Как только появляется или меняется правило, контроллер на лету пересобирает конфигурацию своего прокси и применяет её. Грубо говоря, ingress nginx контроллер берёт все твои Ingress-объекты и генерирует из них один большой nginx.conf с server-блоками и location-ами.
Сам контроллер должен иметь точку входа снаружи. Обычно это его собственный Service типа LoadBalancer (в облаке) или NodePort/hostNetwork (на bare metal). То есть схема двухслойная: облачный балансировщик гонит весь трафик на под контроллера, а уже контроллер по правилам Ingress раскидывает запросы по сервисам приложений. Один внешний IP - десятки приложений за ним.
Поставим ingress-nginx через Helm и посмотрим, что приехало:
Код: Выделить всё
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
kubectl get svc -n ingress-nginx
Код: Выделить всё
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller LoadBalancer 10.96.142.10 158.160.12.34 80:31480/TCP,443:30900/TCP
Хосты, пути и IngressClass: как пишется правило
Теперь сам Ingress. Актуальный apiVersion - networking.k8s.io/v1 (стабилен с Kubernetes 1.19, всё, что старше с extensions/v1beta1 - мусор из интернета, не копируй):
Код: Выделить всё
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
namespace: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop-frontend
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: shop-api
port:
number: 8080
pathType - поле, которое все игнорируют, а зря. Значений три. Prefix матчит по сегментам пути: /api совпадёт с /api и /api/users, но НЕ с /apixyz - граница идёт по слешу, а не по символам. Exact требует точного совпадения строки. ImplementationSpecific отдаёт трактовку на откуп контроллеру (у nginx это включает регулярки). Перепутал Prefix и Exact - и половина роутов внезапно отдаёт 404. Маршруты внутри одного хоста матчатся от самого длинного и специфичного к короткому, поэтому /api перехватит запросы раньше, чем /.
Проверяем, что правило подхвачено:
Код: Выделить всё
kubectl describe ingress shop-ingress -n shop
Код: Выделить всё
Rules:
Host Path Backends
---- ---- --------
shop.example.com / shop-frontend:80 (10.112.1.5:80,10.112.1.6:80)
/api shop-api:8080 (10.112.2.9:8080)
Events:
Type Reason Age From Message
Normal Sync 12s nginx-ingress-controller Scheduled for sync
TLS, cert-manager и Let's Encrypt: HTTPS без боли
Голый HTTP в проде - моветон. Ingress терминирует TLS на стороне контроллера: браузер устанавливает HTTPS с nginx, а внутрь кластера к приложению идёт уже расшифрованный HTTP. Сертификат и ключ контроллер берёт из Secret типа kubernetes.io/tls.
Руками генерить и продлевать сертификаты - каторга. Стандарт индустрии - cert-manager: оператор, который сам получает сертификаты от Let's Encrypt по протоколу ACME и автоматически продлевает их за две недели до истечения. Ставишь cert-manager, описываешь ClusterIssuer (кто выдаёт), вешаешь аннотацию на Ingress - дальше магия.
Код: Выделить всё
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
ingressClassName: nginx
Код: Выделить всё
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- shop.example.com
secretName: shop-tls
Смотрим статус:
Код: Выделить всё
kubectl get certificate -n shop
Код: Выделить всё
NAME READY SECRET AGE
shop-tls True shop-tls 90s
Аннотации: где спрятана половина функциональности
Базовый спек Ingress умеет немного: хост, путь, бэкенд, TLS. Всё остальное - таймауты, размер тела запроса, rewrite пути, rate limiting, sticky sessions, проксирование gRPC - живёт в аннотациях, и они специфичны для конкретного контроллера. Пример для ingress-nginx:
Код: Выделить всё
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
Gateway API: преемник Ingress в 2026
Сообщество признало, что Ingress исчерпан, и сделало замену - gateway api. Это не косметика, а новая модель управления входящим трафиком, и в 2026 она уже не эксперимент: ресурсы Gateway, GatewayClass и HTTPRoute стабильны (apiVersion gateway.networking.k8s.io/v1), а в феврале 2026 вышла версия 1.5, где в стабильный канал переехали ещё несколько фич. Дополнительный толчок к миграции - проект ingress-nginx объявлен end-of-life с марта 2026, так что планировать переход на gateway api придётся всем, кто на нём сидит.
Главная идея - разделение ответственности по ролям, чего в Ingress не было вообще. Раньше один объект Ingress мешал в кучу инфраструктуру (TLS, внешний адрес) и прикладную маршрутизацию (пути приложения). В Gateway API три уровня:
- GatewayClass - описание реализации (какой контроллер), ставит провайдер кластера. Аналог IngressClass, но богаче.
- Gateway - сама точка входа: какие порты, протоколы, домены и TLS-сертификаты слушаем. Этим владеет платформенная команда или SRE.
- HTTPRoute (а также TLSRoute, GRPCRoute) - правила маршрутизации конкретного приложения. Этим владеет команда разработки, в своём namespace.
Код: Выделить всё
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: infra
spec:
gatewayClassName: nginx
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
Код: Выделить всё
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop-route
namespace: shop
spec:
parentRefs:
- name: main-gateway
namespace: infra
hostnames:
- shop.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: shop-api
port: 8080
Про миграцию практически. Не надо переписывать всё за ночь. Gateway API и Ingress спокойно работают параллельно на одном кластере: ставишь Gateway-контроллер (NGINX Gateway Fabric, Envoy Gateway, Cilium, Istio - список реализаций большой), поднимаешь Gateway, и по одному переводишь приложения с Ingress на HTTPRoute, переключая DNS. cert-manager уже умеет работать с Gateway API, выпуская сертификаты на Gateway. Нюанс: в модели Gateway сертификаты привязаны к Gateway (владение инфраструктурой), а не к каждому приложению, как было удобно с Ingress-аннотацией - это меняет процесс выдачи, учитывай при планировании.
LoadBalancer против Ingress: когда что
Частый вопрос новичка: зачем Ingress, если есть Service type: LoadBalancer? Разница принципиальная по слою. LoadBalancer работает на L4 (TCP/UDP) - он просто прокидывает порт наружу, не зная, что внутри. Один балансировщик - один сервис, никакого разбора по доменам и путям, никакой TLS-терминации на уровне HTTP. Ingress и Gateway работают на L7 - понимают HTTP, читают Host-заголовок и URL, терминируют TLS, могут роутить десятки приложений за одним IP.
Практический вывод. Нужен голый TCP (база данных, кастомный бинарный протокол, игровой сервер) - бери LoadBalancer. Публикуешь веб и API по доменам с HTTPS - бери Ingress или, на новых проектах в 2026, сразу Gateway API. И помни: сам Ingress-контроллер наружу почти всегда торчит как раз через один Service type: LoadBalancer - это не конкуренты, а слои одной системы, где kubernetes трафик снаружи сначала бьёт в L4-балансировщик, а тонкую L7-маршрутизацию делает контроллер.
Типичные грабли и антипаттерны
- Нет контроллера. Ingress создан, ADDRESS пустой, трафика нет. Сначала ставь контроллер, потом правила.
- Забыл ingressClassName. Правило висит бесхозным, ни один контроллер его не берёт.
- Перепутал pathType. Exact там, где нужен Prefix - и подпути отдают 404.
- Сертификат до DNS. Запросил Let's Encrypt раньше, чем A-запись указала на EXTERNAL-IP - challenge не проходит, Certificate висит False.
- Слепое копирование аннотаций. Скопировал nginx-аннотации в кластер с Traefik или ALB - они молча игнорируются.
- Один гигантский Ingress на всё. Сложно дебажить, конфликты путей, размытое владение. Дроби по приложениям и namespace.
- TLS-терминация без понимания. Помни, что внутрь кластера от контроллера к поду идёт расшифрованный HTTP - если нужен сквозной TLS, это отдельная настройка (re-encrypt или passthrough).
- Подними локальный кластер (kind или minikube) и поставь ingress-nginx через Helm, как показано выше.
- Задеплой два простых приложения (например, два разных Deployment с echo-сервером) и заведи на каждый Service.
- Опиши один Ingress с ingressClassName: nginx, хостом demo.local и двумя путями Prefix - / на первое приложение, /api на второе.
- Пропиши demo.local в /etc/hosts на EXTERNAL-IP (или 127.0.0.1 для kind) и проверь curl-ом, что разные пути попадают на разные приложения.
- Сделай kubectl describe ingress и найди в выводе блок Rules с эндпойнтами и событие Sync.
- Со звёздочкой: разверни тот же роутинг через Gateway API - поставь NGINX Gateway Fabric, опиши Gateway и HTTPRoute, сравни структуру с Ingress.
- Почему созданный объект Ingress без установленного контроллера не пропускает трафик и что физически исполняет правила?
- Чем отличаются pathType Prefix и Exact, и к каким багам приводит их путаница?
- Как cert-manager получает сертификат от Let's Encrypt через http01 и почему до этого обязательно настроить DNS?
- Какую архитектурную проблему Ingress решает Gateway API и как в нём разделена ответственность между ролями?
Ingress - это декларативные правила L7-маршрутизации, но исполняет их отдельно поставленный контроллер (nginx, Traefik), который терминирует TLS и раскидывает kubernetes трафик по сервисам за одним внешним IP. HTTPS закрывается связкой cert-manager плюс Let's Encrypt в три строки. Главный архитектурный недостаток Ingress - вендорные аннотации и смешение ролей - снят в Gateway API, который в 2026 уже стабилен и становится стандартом, тем более что ingress-nginx уходит на покой. На существующих проектах оставайся на Ingress, но новое лучше строить сразу на gateway api, а миграцию вести постепенно и параллельно.