24 марта 2026 kubernetes/ingress-nginx — самый популярный ingress-контроллер в кластерах — официально ушёл в End-of-Life. Репозиторий заморожен: ни багфиксов, ни security-патчей, ни ответов на issue. Если он до сих пор держит трафик в вашем кластере, вопрос не «стоит ли мигрировать», а «сколько времени осталось до первой непропатченной уязвимости». Gateway API — не альтернативный синтаксис для Ingress, а его официальная замена в самом Kubernetes, и SIG Network уже выпустил инструмент для миграции. Разберём, как перейти без ночного аврала.
Содержание
Открыть содержание
Почему Ingress оказался в тупике
Ingress был специфицирован в 2015-м как минимальный общий знаменатель: один ресурс, host/path-матчинг, TLS-секция — и всё. Реальные контроллеры почти сразу упёрлись в потолок этой спецификации: canary-релизы, rate limiting, rewrite-правила, mTLS между сервисами — ничего этого в API нет. Каждый контроллер закрывал дыры через nginx.ingress.kubernetes.io/...-аннотации, и в итоге получился не портируемый API, а набор диалектов. Манифест, написанный под ingress-nginx, не запускался на Traefik или HAProxy без переписывания половины аннотаций.
Вторая проблема — роли. В реальной организации инфраструктурная команда владеет балансировщиком и TLS-сертификатами, а команда сервиса — только своими маршрутами. Ingress этого не различает: один YAML-файл, и либо разработчик получает права на весь балансировщик, либо platform-команда становится узким местом, вручную согласовывая каждый PR с новым path.
Что меняет Gateway API
Gateway API — не патч поверх Ingress, а другая модель ресурсов, спроектированная сразу с учётом ролей и типизации.
- Разделение ролей.
GatewayClassиGateway— зона ответственности platform-инженера: какая реализация балансировщика, на каких портах, с каким TLS.HTTPRoute— зона ответственности команды сервиса: свои host/path-правила, свой RBAC, без доступа к остальной инфраструктуре. - Типизированные маршруты вместо аннотаций. Header-based роутинг, canary по весам, rewrite path — это поля схемы
HTTPRoute, а не текстовые аннотации конкретного вендора. Один манифест переносится между Envoy Gateway, Cilium и облачным контроллером без переписывания. - Расширяемость через типы, а не хаки.
HTTPRouteдля HTTP,GRPCRouteдля gRPC,TCPRoute/TLSRouteдля L4 — конвертированный не через аннотации, а через отдельный типизированный ресурс с собственной схемой. - Кросс-namespace маршрутизация из коробки.
ReferenceGrantявно разрешаетHTTPRouteв одном namespace ссылаться наServiceв другом — то, что в Ingress решалось нестандартными флагами конкретного контроллера. - Policy attachment вместо аннотаций на ресурсе. Rate limiting, retry-политики, mTLS между сервисами оформляются отдельными CRD (
BackendTrafficPolicy,ClientTrafficPolicyи подобные, у каждой реализации свои), которые «прикрепляются» кGatewayилиHTTPRouteчерезtargetRef, не засоряя сам маршрут вендорским синтаксисом.
Модель ресурсов: GatewayClass → Gateway → HTTPRoute
Три ресурса, и каждый решает свой уровень задачи.
GatewayClass — кластерный ресурс, который объявляет доступную реализацию (Envoy Gateway, NGINX Gateway Fabric, облачный LB) примерно как StorageClass объявляет доступный тип диска. Его создаёт platform-команда или сам оператор реализации при установке.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
Gateway — конкретный экземпляр точки входа: порты, протоколы, TLS-сертификаты, ограничение на то, какие namespace могут к нему подключать маршруты. Тоже в зоне platform-команды.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public-gateway
namespace: infra
spec:
gatewayClassName: envoy-gateway
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
certificateRefs:
- name: wildcard-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels: { gateway-access: "allowed" }
HTTPRoute — маршруты конкретного сервиса, живёт в namespace команды и ссылается на Gateway через parentRefs. Здесь же canary-сплит по весам — без единой аннотации.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: checkout
namespace: checkout-team
spec:
parentRefs:
- name: public-gateway
namespace: infra
hostnames: ["checkout.example.com"]
rules:
- matches:
- path: { type: PathPrefix, value: /api }
backendRefs:
- name: checkout-stable
port: 80
weight: 90
- name: checkout-canary
port: 80
weight: 10
ingress2gateway: конвертация без переписывания вручную
SIG Network выпустил ingress2gateway 1.0 именно под волну миграций с EOL ingress-nginx. Инструмент читает существующие Ingress-ресурсы кластера (включая часть распространённых аннотаций) и генерирует эквивалентные Gateway/HTTPRoute.
# ставим CLI
go install sigs.k8s.io/ingress2gateway@latest
# читаем текущие Ingress из кластера и печатаем сгенерированные манифесты
ingress2gateway print --namespace checkout-team > gateway-generated.yaml
# то же самое, но по конкретному провайдеру аннотаций (например, nginx)
ingress2gateway print --providers=ingress-nginx --namespace checkout-team
Дальше сгенерированный YAML — черновик, а не финальный результат: нужно проверить parentRefs (совпадает ли имя и namespace Gateway, который вы уже создали), убедиться, что TLS подтянулся из правильного Gateway-listener’а, а не остался пустым, и прогнать через kubectl apply --dry-run=server, чтобы поймать ошибки схемы до реального применения.
Выбор реализации
Gateway API — это спецификация, а не рабочий контроллер; для работы нужна реализация, которая её поддерживает.
- Envoy Gateway — CNCF-проект поверх Envoy proxy, полная поддержка Gateway API, хороший выбор как самостоятельная замена ingress-nginx без привязки к остальному стеку.
- NGINX Gateway Fabric — официальная реализация от F5/NGINX, минимизирует миграционный шок для команд, уже привыкших к nginx-семантике (rewrite, rate limiting).
- Cilium — если CNI уже Cilium, Gateway API включается как часть существующего eBPF dataplane без отдельного балансировщика, с наименьшими накладными расходами.
- Облачные реализации — GKE Gateway controller, AWS Load Balancer Controller (поддержка Gateway API), Azure Application Gateway for Containers — вариант, если хочется управляемого L7 без своего control plane.
Критерий выбора почти всегда один: не «какая реализация технически мощнее», а «что уже стоит в кластере». Если CNI — Cilium, добавление отдельного L7-балансировщика ради Gateway API — лишний компонент в дата-плейне; если инфраструктура целиком в облаке и L7 уже отдан управляемому LB, естественнее включить Gateway API поддержку там же, а не поднимать Envoy Gateway рядом. Самостоятельная установка (Envoy Gateway, NGINX Gateway Fabric) имеет смысл там, где нужен контроль над конкретной версией и нет привязки к одному облаку.
Сравнение: Ingress и Gateway API
| Ingress | Gateway API | |
|---|---|---|
| Роли | Один ресурс, права на всё сразу | GatewayClass/Gateway — platform, HTTPRoute — команда |
| Canary/weight-роутинг | Через вендорские аннотации, не портируется | Поле weight в схеме backendRefs |
| Портируемость между контроллерами | Низкая — аннотации у каждого вендора свои | Высокая — общая типизированная схема |
| Протоколы | Только HTTP/HTTPS | HTTP, gRPC, TCP, TLS — отдельные типы маршрутов |
| Кросс-namespace ссылки | Нестандартные флаги контроллера | ReferenceGrant, явное разрешение |
| Статус проекта | ingress-nginx: EOL с марта 2026 | Активная разработка, GA с v1.0 |
Что нужно, чтобы мигрировать
Порядок действий, который не ломает продакшн-трафик в процессе:
- Установить CRD Gateway API —
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml. - Поставить одну реализацию — например, Envoy Gateway через Helm-чарт, и создать
GatewayClass+Gatewayдля нового публичного входа. - Прогнать существующие
Ingressчерезingress2gatewayдля каждого namespace и получить черновикHTTPRoute. - Проверить каждый сгенерированный
HTTPRouteвручную — особенно аннотации, у которых нет 1:1 аналога (rate limiting, кастомные заголовки, снипеты Lua/nginx.conf) — их придётся переносить черезExtensionRefконкретной реализации или переписывать логику отдельно. - Запустить
GatewayиHTTPRouteпараллельно со старымIngress, направив на новый вход небольшую долю трафика через DNS-вес или отдельный тестовый hostname — не выключатьIngressсразу. - Постепенно поднимать canary-вес в
HTTPRouteс 10% до 100%, наблюдая за метриками, и только после этого удалять старыеIngress-ресурсы.
Ловушки миграции
- Аннотации без прямого аналога. Кастомные
nginx.conf-снипеты, Lua-скрипты, специфичные для контроллера заголовки —ingress2gatewayих не переносит, потому что переносить нечего: эквивалента в спецификации нет. Такие случаи остаются на ручную проверку и часто требуютExtensionRefна CRD конкретной реализации. - TLS-секреты не там, где ожидает
Gateway. В Ingress TLS-секрет обычно лежит в том же namespace, что и сам ресурс.Gatewayчаще создаётся в infra-namespace, и секрет нужно туда явно скопировать или подключить черезReferenceGrant— иначеGatewayподвиснет в статусеProgrammed: Falseбез явной ошибки в логах. - Canary через weight — не то же самое, что canary по заголовкам или cookie. Простой
weight-сплит вbackendRefsпокрывает базовый сценарий, но sticky-сессии или роутинг по конкретному пользователю требует правилmatchesсheaders/cookie, которые нужно добавлять поверх сгенерированного черновика вручную. - Несколько
Ingress-контроллеров в одном кластере — несколькоGatewayClass. Если в кластере исторически жили ingress-nginx и, скажем, отдельный внутренний контроллер, не пытайтесь свести всё к одномуGatewayсразу — мигрируйте по одному классу трафика за раз. - RBAC на
HTTPRouteне настроен заранее. Смысл разделения ролей теряется, если у всех команд по-прежнему естьcluster-adminи они правятGatewayнапрямую вместо своихHTTPRoute. Прежде чем объявлять миграцию завершённой, стоит явно ограничитьRole/RoleBindingтак, чтобы команда сервиса могла редактировать толькоHTTPRouteв своём namespace, аGatewayоставался в руках platform-команды.
Как проверить, что всё работает
После применения манифестов — три быстрые проверки. Статус самого Gateway: kubectl get gateway public-gateway -n infra -o jsonpath='{.status.conditions}' должен показывать Programmed: True и Accepted: True. Статус конкретного маршрута: kubectl get httproute checkout -n checkout-team -o jsonpath='{.status.parents}' подтверждает, что Gateway принял этот HTTPRoute, а не просто создал ресурс без привязки. И живой трафик: curl -H "Host: checkout.example.com" https://<gateway-ip>/api/health несколько раз подряд должен показать распределение между checkout-stable и checkout-canary близкое к заданным весам.
Итог
ingress-nginx не сломается в одну ночь — он будет работать ровно до первой уязвимости, на которую уже не выйдет патч. Это делает миграцию не вопросом выбора «удобнее или нет», а вопросом времени: чем раньше начать перевод трафика на Gateway API параллельно со старым Ingress, тем меньше шансов встретить security-инцидент на мёртвом коде в проде. ingress2gateway закрывает большую часть рутинной конвертации, но ручная проверка каждого нестандартного правила — обязательный шаг, а не опция.