Что такое Ingress на самом деле:
Главное, на чём спотыкаются новички: Ingress сам по себе ничего не делает. Это просто объект с правилами вида "запросы на shop.local/api отдавай сервису api-svc". Чтобы правила ожили, в кластере должен работать ingress-контроллер, реверс-прокси (nginx, traefik, haproxy), который следит за Ingress-объектами и на лету перестраивает свой конфиг.
Для учёбы возьмём ingress-nginx: он до сих пор стоит в огромном числе кластеров и в minikube включается одной командой. Но сразу важная оговорка про его статус. В ноябре 2025 проект Kubernetes официально объявил retirement ingress-nginx, и в марте 2026 закончилась даже best-effort поддержка: новых релизов и security-фиксов больше не будет. InGate, который анонсировали как преемника, тоже свёрнут, до рабочего состояния он не дошёл. Контекст у решения тяжёлый: годы нехватки мейнтейнеров плюс история IngressNightmare весной 2025 (CVE-2025-1974 и смежные, CVSS 9.8), когда через admission webhook контроллера можно было без аутентификации выполнить код и дотянуться до всех секретов кластера. Вывод для практики: учиться на ingress-nginx локально нормально, концепции (правила, классы, TLS-секреты) одинаковы у всех контроллеров, но новые продакшен-инсталляции в 2026 на нём не делают. Пути миграции: реализации Gateway API (Envoy Gateway, Cilium, Istio, NGINX Gateway Fabric) либо живые Ingress-контроллеры вроде Traefik, HAProxy Ingress или NGINX Ingress Controller от F5 (это отдельный проект, не путайте с ingress-nginx).
В minikube контроллер включается аддоном:
Код: Выделить всё
minikube addons enable ingress
kubectl get pods -n ingress-nginxПравила маршрутизации:
Возьмём два сервиса из прошлых глав: web-svc (фронтенд) и api-svc (бэкенд), оба типа ClusterIP на порту 80. Разведём трафик двумя способами сразу: на shop.local делим по пути, а всё, что пришло на отдельный хост api.shop.local, целиком уходит на бэкенд. Файл ingress.yaml:
Код: Выделить всё
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "20m"
spec:
ingressClassName: nginx
defaultBackend:
service:
name: web-svc
port:
number: 80
rules:
- host: shop.local
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
- host: api.shop.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80pathType: Prefix сравнивает путь по сегментам: /api совпадёт с /api и /api/users, но не с /apiv2. Есть ещё Exact (точное совпадение) и ImplementationSpecific (трактовку пути определяет контроллер). Последний не экзотика: в ingress-nginx регулярные выражения в path разрешены только с ним. Никакой автоконвертации нет, наоборот: начиная с ingress-nginx 1.12 опция strict-validate-path-type включена по умолчанию, и validating webhook отклонит Ingress, у которого при Prefix или Exact в path есть что-то кроме букв, цифр, '-', '_' и '/'. Об этом ниже в граблях. Более специфичный путь побеждает, поэтому /api уйдёт на бэкенд, а всё остальное на фронтенд. Аннотация proxy-body-size заодно поднимает лимит на тело запроса: у nginx по умолчанию 1m, и загрузка файлов падает с ошибкой 413.
Применяем и проверяем:
Код: Выделить всё
kubectl apply -f ingress.yaml
grep -q shop.local /etc/hosts || echo "$(minikube ip) shop.local api.shop.local" | sudo tee -a /etc/hosts
curl http://shop.local/api/health
curl http://api.shop.local/healthTLS:
Шифрование вешается на тот же Ingress. Для локальной разработки сертификат генерируется через mkcert или openssl, в проде его выдаст Let's Encrypt (обычно через cert-manager, но это отдельная история). Кладём сертификат в секрет специального типа tls, секреты мы разбирали в шестой главе:
Код: Выделить всё
kubectl create secret tls shop-tls --cert=shop.crt --key=shop.key
# в spec ингресса, рядом с rules:
tls:
- hosts:
- shop.local
secretName: shop-tlsGateway API, куда всё движется:
Сам Ingress API из Kubernetes никуда не денется, но он заморожен: новые возможности в него не добавляют. Развитие ушло в Gateway API, который стабилен с версии 1.0 ещё с конца 2023 и давно обкатан в проде. Идея та же, декларативная маршрутизация, но устройство другое: вместо одного объекта три уровня с разделением ролей. GatewayClass описывает реализацию (аналог IngressClass), Gateway это сама точка входа с листенерами, её заводит платформенная команда, а HTTPRoute с правилами маршрутизации пишут разработчики приложений. Всё, что в Ingress делалось вендорскими аннотациями (матчинг по заголовкам, веса для канареек, переписывание путей), в HTTPRoute является штатными полями спеки, поэтому конфиг переносится между контроллерами без переделки. Новые проекты в 2026 разумно стартовать сразу с Gateway API, тем более что после retirement ingress-nginx миграция многим и так предстоит. Но Ingress знать обязательно: в работающих кластерах его тонны, и поддерживать это кому-то придётся ещё годы.
Типичные грабли:
Nginx отвечает 404, хотя сервис живой. Запрос дошёл до контроллера, но не совпал ни с одним правилом, а defaultBackend не задан. Чаще всего виноват Host: запрос по голому IP не попадёт в правило с host: shop.local. Добавьте к curl флаг -H с нужным заголовком Host и проверьте снова.
Ingress создан, но контроллер его игнорирует. Обычно забыли ingressClassName, а дефолтного класса в кластере нет. Кстати, два класса, помеченных default одновременно, ничего не ломают, хотя старая формулировка об этом до сих пор гуляет по докам: начиная с Kubernetes 1.25 admission-плагин DefaultIngressClass в такой ситуации берёт самый свежий класс по creationTimestamp и сам проставляет его в ingressClassName, а до 1.25 API-сервер просто отклонял создание Ingress без поля с ошибкой. Полагаться на этот автоматический выбор не стоит, держите ровно один default. Смотрите kubectl describe ingress shop-ingress: если поле Address пустое дольше минуты, объект никто не взял. Список классов и пометку default покажет kubectl get ingressclass.
Браузер ругается на сертификат "Kubernetes Ingress Controller Fake Certificate". Это заглушка контроллера: секрет не найден. Либо опечатка в secretName, либо секрет лежит в другом namespace. Ingress, секрет и сервисы должны жить в одном namespace.
Бэкенд получает запросы с префиксом /api и сам отвечает 404. Ingress по умолчанию не режет путь. В ingress-nginx это лечится аннотацией rewrite-target: в path пишется регулярка с capture-группами, например /api(/|$)(.*), а в nginx.ingress.kubernetes.io/rewrite-target ставится /$2. Только сначала смените pathType: с регуляркой он обязан быть ImplementationSpecific, сам контроллер ничего не конвертирует. Оставите Prefix, как в нашем примере выше, и apply на актуальной версии упадёт: с ingress-nginx 1.12 опция strict-validate-path-type включена по умолчанию, и validating webhook отклонит такой объект ещё на входе. Цену понимать обязательно: матчинг с регуляркой перестаёт быть честным Prefix и привязан к конкретному контроллеру. При переезде, например на Gateway API, такое правило придётся переписать (там для этого есть штатный фильтр URLRewrite).
Что усвоили:
Ingress это правила, контроллер это исполнитель, и без второго первое бесполезно. Мы подняли ingress-nginx, развели трафик по хостам и путям, поставили catch-all через defaultBackend, повесили TLS и научились читать 404 контроллера. Сам Ingress API остаётся в Kubernetes, но развитие ушло в Gateway API, а ingress-nginx с марта 2026 не получает даже security-фиксов, так что для продакшена выбирайте контроллер из живых. Дальше займёмся данными: в восьмой главе разберём Volumes и PersistentVolumeClaim, чтобы поды переставали терять файлы при рестарте.