Урок 07 · Kubernetes на практике
Ingress и Gateway API
Что-то не запустилось или осталось непонятным? Задайте вопрос в комментариях к разбору этого урока в канале — разберёмся вместе.
Service из урока 4 дал стабильный адрес внутри кластера, а снаружи — что-то вроде 192.168.49.2:31742. Порт случайный, выдан кластером, и на каждый новый сервис будет свой такой же. Никто не пускает пользователей в интернет по такому адресу: снаружи нужны имена (shop.example.com), пути (/api, /admin) и HTTPS — и всё это на одном-единственном входе. В Kubernetes за такой вход отвечали и отвечают два поколения объектов: старый Ingress, который вы точно встретите в чужих кластерах, и пришедший ему на смену Gateway API, на котором мы и сделаем практику.
Ingress — это правила. Исполняет их контроллер
Здесь спотыкаются почти все, кто впервые пробует выпустить сервис наружу: вы пишете манифест, применяете его, kubectl get ingress показывает объект — а по адресу ничего не отвечает. Дело в том, что Ingress сам по себе ничего не делает. Это просто запись в базе кластера вида «запросы к хосту hello.local отправляй в Service hello-fastapi».
Чтобы правила заработали, в кластере должен жить контроллер — обычный Pod с настоящим веб-сервером внутри (чаще всего nginx). Он следит за объектами маршрутизации, на лету превращает их в свой конфиг и принимает трафик снаружи. Разделение труда простое:
- Объект маршрутизации (его пишете вы) — что куда направлять: хост, путь, целевой Service и его порт.
- Контроллер (ставится в кластер один раз) — кто эти правила исполняет и держит на себе внешний вход.
Запомните это разделение: оно одинаково верно и для Ingress, и для Gateway API, и именно его непонимание порождает большинство вопросов «почему не работает».
Как выглядит Ingress-манифест
Применять его мы не будем — но прочитать нужно уметь, потому что в живых кластерах таких файлов тысячи:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-fastapi
spec:
ingressClassName: nginx
rules:
- host: hello.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-fastapi
port:
number: 8000
Читается сверху вниз: правило срабатывает для хоста hello.local, путь / с типом Prefix означает «этот путь и всё, что начинается с него», а запрос уходит в Service hello-fastapi на порт 8000 — порт Service, а не контейнера. Строка ingressClassName говорит, какой именно контроллер должен взять это правило.
Всё, что Ingress умеет выразить, — примерно это и есть. Чуть больше — редиректы, таймауты, переписывание путей, CORS — делалось аннотациями: строчками вида nginx.ingress.kubernetes.io/rewrite-target: / в metadata. У каждого контроллера аннотации были свои, несовместимые с чужими, и перенести конфиг с одного контроллера на другой означало переписать его целиком. Именно из этой боли и вырос Gateway API.
- Ingress API заморожен. В документации Kubernetes прямо сказано: «The Kubernetes project recommends using Gateway instead of Ingress. The Ingress API has been frozen». Заморожен — не значит удалён: объект остаётся стабильным (GA), удалять его не планируют, существующие манифесты продолжат работать. Но новых возможностей у него не будет никогда.
- ingress-nginx ушёл на покой. Тот самый контроллер от сообщества Kubernetes, который стоял примерно в половине кластеров мира и который поднимает команда
minikube addons enable ingress, был объявлен к выводу в ноябре 2025 года, а 24 марта 2026 года его репозиторий заархивирован. Обновлений и патчей безопасности больше не выходит — найденные с тех пор уязвимости не будут исправлены никогда.
kubernetes/ingress-nginx. Практический вывод для вас: читать Ingress уметь надо, а новое строить — на Gateway API. Поэтому дальше в уроке мы идём именно туда.
Gateway API: три объекта вместо одного
Gateway API решает ту же задачу — впустить трафик в кластер, — но разбирает её на три объекта вместо одного. Причина не в любви к сложности, а в том, что за разные части этой задачи в реальной команде отвечают разные люди:
| Объект | Что описывает | Кто обычно владеет |
|---|---|---|
GatewayClass |
Какая реализация обслуживает шлюзы — «мы используем nginx» / «мы используем Envoy». Создаётся один раз при установке контроллера. | Поставщик инфраструктуры |
Gateway |
Сам вход: какие порты слушаем, по каким протоколам, с какими сертификатами и для каких доменов. | Платформенная команда |
HTTPRoute |
Куда девать конкретный запрос: хост, путь, заголовки → в такой-то Service. Живёт рядом с приложением. | Разработчик сервиса |
В Ingress всё это было слито в один файл, и чтобы добавить своему сервису маршрут, разработчику приходилось лезть в общий объект, где рядом лежат сертификаты и чужие правила. В Gateway API маршрут — отдельный объект: команда платформы один раз поднимает Gateway, а дальше каждая команда добавляет свои HTTPRoute, ничего общего не трогая. Плюс то, что раньше жило в аннотациях конкретного контроллера — разделение трафика по весам, работа с заголовками, редиректы, — теперь часть самого API и переносится между реализациями.
Gateway, а маршруты к нему подключаются отдельными объектами HTTPRoute — их можно держать рядом с кодом сервиса.Практика: выпустите сервис наружу через Gateway API
Понадобится Deployment из урока 3 и Service hello-fastapi из урока 4 — проверьте, что они на месте: kubectl get deploy,svc. Ingress-аддон minikube включать не нужно; если вы уже успели его включить, выключите: minikube addons disable ingress.
-
Установите сам Gateway API. В отличие от Ingress, он не встроен в Kubernetes — это набор CRD (пользовательских типов объектов), которые ставятся отдельно:
GWAPI=https://github.com/kubernetes-sigs/gateway-api/releases/download kubectl apply --server-side -f $GWAPI/v1.6.1/standard-install.yamlПроверить, что типы появились в кластере:
kubectl get crd | grep gateway. Сейчас в кластере есть описание объектов — но ещё нет никого, кто бы их исполнял. -
Поставьте контроллер. Возьмём NGINX Gateway Fabric — реализацию Gateway API поверх того же nginx, живую и поддерживаемую (это не тот проект, что закрылся в марте):
NGF=https://raw.githubusercontent.com/nginx/nginx-gateway-fabric kubectl create namespace nginx-gateway kubectl apply --server-side -f $NGF/v2.7.0/deploy/crds.yaml kubectl apply -f $NGF/v2.7.0/deploy/default/deploy.yamlДождитесь готовности контроллера и убедитесь, что он зарегистрировал свой класс:
kubectl get pods -n nginx-gateway kubectl get gatewayclassВ выводе последней команды должен появиться класс
nginxсо значениемACCEPTED: True— это и есть тот самыйGatewayClassиз таблицы выше, вы его не создавали, его принёс с собой контроллер. -
Опишите вход — файл
gateway.yaml:apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: hello-gateway spec: gatewayClassName: nginx listeners: - name: http port: 80 protocol: HTTP hostname: "hello.local" allowedRoutes: namespaces: from: Samelisteners— это порты, которые шлюз слушает снаружи; здесь один, обычный HTTP на 80.allowedRoutesзадаёт, чьи маршруты этот вход готов принимать:Same— только из своего пространства имён. Это та самая ролевая граница — владелец шлюза решает, кого к себе пускать. -
Опишите маршрут — файл
httproute.yaml:apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: hello-fastapi spec: parentRefs: - name: hello-gateway sectionName: http hostnames: - "hello.local" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: hello-fastapi port: 8000Сравните с Ingress-манифестом выше — содержание то же самое, но появился
parentRefs: маршрут сам заявляет, к какому шлюзу он подключается. Именно поэтому его можно писать и применять, не трогая чужой общий объект. -
Примените оба файла и проверьте, что кластер их принял:
kubectl apply -f gateway.yaml -f httproute.yaml kubectl get gateway kubectl describe httproute hello-fastapiУ Gateway в колонке
PROGRAMMEDдолжно появитьсяTrue— контроллер поднял под этот шлюз настоящий nginx. В выводеdescribeдля маршрута ищите условияAccepted: TrueиResolvedRefs: True: первое означает, что шлюз маршрут принял, второе — что Servicehello-fastapiнайден. -
Откройте шлюз наружу. Под
Gatewayконтроллер создал отдельный Deployment с nginx и Service типаLoadBalancer— в minikube такой Service получает внешний адрес только при запущенном туннеле. В отдельном терминале запустите и оставьте работать:minikube tunnelКоманда спросит пароль — ей нужны права, чтобы поднять сетевой маршрут на вашей машине.
-
В первом терминале посмотрите, какой адрес получил шлюз:
kubectl get svcНайдите сервис, имя которого начинается с
hello-gateway, и возьмите егоEXTERNAL-IP. Пропишите этот адрес в/etc/hosts(на Windows —C:\Windows\System32\drivers\etc\hosts, Блокнотом от администратора), подставив вместо<EXTERNAL-IP>увиденное значение:sudo sh -c 'echo "<EXTERNAL-IP> hello.local" >> /etc/hosts' -
Проверьте результат — по имени и без всякого порта:
curl http://hello.local/ curl http://hello.local/healthИ в браузере:
http://hello.local/docs. Тот же сервис, что в уроке 4 отвечал по192.168.49.2:31742, теперь доступен по нормальному имени на стандартном 80-м порту.
curl молчит или отдаёт 404, не переустанавливайте всё заново — у Gateway API очень разговорчивые статусы, и почти всегда он прямо говорит, что не так:
kubectl describe gateway hello-gateway
kubectl describe httproute hello-fastapi
Accepted: False у маршрута — шлюз его не принял: чаще всего не совпал parentRefs.name или маршрут лежит в другом пространстве имён, чем разрешает allowedRoutes. ResolvedRefs: False — не найден Service из backendRefs: опечатка в имени или неверный порт. Пустой EXTERNAL-IP — не запущен minikube tunnel. Это заметное улучшение по сравнению с Ingress, где о причинах приходилось догадываться по логам контроллера.
NodePort из урока 4 больше не нужен — верните Service обычный type: ClusterIP, и снаружи останется ровно один вход, а не два. А чтобы выпустить второй сервис на том же домене, не нужно трогать Gateway вообще: пишете ещё один HTTPRoute с тем же parentRefs и своим путём — например, /api. Ради этого разделения Gateway API и придуман.
Проверьте себя
Что именно произошло с Ingress к 2026 году?
Разработчику нужно выпустить наружу новый сервис на уже работающем шлюзе. Что он создаёт?
У HTTPRoute в статусе видно ResolvedRefs: False. О чём это говорит?
Напишите команду, которая покажет, какие реализации Gateway API зарегистрированы в кластере:
Напишите команду, которая покажет условия Accepted и ResolvedRefs для маршрута hello-fastapi: