← Назад к блогу

Kubernetes Gateway API: от нуля до героя

Ingress заморожен, ingress-nginx сняли с поддержки. Разбираем его преемника целиком: четыре типизированных ресурса, разделение между платформой и приложением, canary, TLS, мульти-тенант и меш — на одном живом магазине.

Виктор Ведмич · 14 мин чтения

kubernetes gateway-api networking

В марте 2026 ingress-nginx сняли с поддержки. На нём работала примерно половина всех кластеров Kubernetes — и теперь не будет ни релизов, ни патчей под уязвимости. Никогда. Вот что переводит Gateway API из разряда «надо бы как-нибудь посмотреть» в «разбираюсь прямо сейчас».

Давайте разберёмся. Это сопроводительная статья к докладу, который я веду как путь от нуля до героя: берём один интернет-магазин и маршрутизируем весь его трафик по-современному, наращивая схему по одной идее за раз. Всё не привязано к конкретному вендору и актуально на момент Gateway API v1.5 (февраль 2026). Слайды встроены прямо в текст — анимированные можно прокликать самому. Реализацию на AWS разберём отдельным постом.

Сразу одна оговорка про термины, потому что на ней спотыкаются все. Речь про Gateway API — стандарт Kubernetes. Это не «API Gateway», общий архитектурный паттерн (и заодно продукт AWS). Разные вещи.

Ingress всё. Вот что пришло на смену

В докладе я трачу на «почему сейчас» ровно один слайд — здесь сделаю так же. Четыре факта:

  • ingress-nginx сняли с поддержки (март 2026). На нём работала половина кластеров. Репозитории закрыли на запись, патчей под новые уязвимости не будет.
  • Gateway API — это стандарт. Версия v1 вышла в октябре 2023, v1.5 — в феврале 2026. Официальный преемник Ingress от SIG Network.
  • Его поддерживают все. GKE, AKS, Istio, Cilium, Envoy Gateway, NGINX Gateway Fabric и другие. Семь реализаций прошли conformance по v1.5 уже в день релиза.
  • Сам API Ingress пока работает, но он заморожен: стабилен, проработает ещё годы. Только вся новая функциональность теперь выходит в Gateway API. Начинаете сегодня — начинайте отсюда.

На этом история закончилась. Дальше — только «что» и «как».

Как запрос вообще доходит до пода?

Перед любым YAML — главная задача. У браузера есть адрес shop.example.com. Ваш код живёт в поде с внутренним IP вроде 10.1.2.7, и этот адрес меняется при каждом перезапуске и каждом масштабировании. Направить DNS на под нельзя в принципе. Кто-то должен перекинуть мост через эту пропасть.

От браузера до пода: Service закрывает внутреннюю половину пути, но внешний трафик до нужного Service всё ещё нужно довести. Эту недостающую деталь и описывает Gateway API.

Половину задачи Kubernetes уже решил через Service — стабильный виртуальный IP и DNS-имя перед группой подов. Внутри кластера связка Service → под работает. Но Service виден только изнутри. Кто-то по-прежнему должен принять внешний трафик на реальном хосте и порту и направить его в нужный Service. Эту деталь и описывает Gateway API: типизированный и привязанный к ролям способ задать точку входа и правила маршрутизации. Раньше этот пробел закрывал Ingress — Gateway API делает то же самое, но аккуратнее.

Чем Ingress перестал устраивать

Стоит чётко назвать структурные ошибки Ingress: Gateway API исправляет каждую из них самим устройством, а не новой порцией конфигов.

  1. Всё жило в аннотациях. TLS, переписывание путей, таймауты, CORS, canary — всё нетипизированными строками. У ingress-nginx их набралось больше сотни. Опечатку в ключе никто не заметит, и вы узнаете о ней в три часа ночи.
  2. Один объект, один владелец. Чтобы добавить простой маршрут по пути, разработчик правил тот же объект, где лежат TLS-секреты кластера и настройки балансировщика. Никакого разделения зон ответственности.
  3. Привязка к вендору через аннотации. Ключ nginx.ingress.kubernetes.io/... ничего не значит для Traefik или HAProxy. Сменить контроллер — значит переписать всё.
  4. Нет встроенной выразительности на L7. Маршрутизации по заголовкам, весов трафика, матчинга по методу — ничего этого в спецификации не было. Каждый контроллер прикручивал своё через — вы угадали — новые аннотации.

Запомните эти четыре пункта. Дальше каждый из них закроется на уровне самой модели.

Gateway API в одной фразе

Если запоминать одну фразу — вот она. Gateway API берёт монолитный объект Ingress и разбивает его на типизированные ресурсы: каждый принадлежит своей команде, и они связываются друг с другом, описывая путь трафика от края кластера до ваших подов.

Отсюда четыре свойства: модель построена вокруг ролей, всё типизировано и проверяется на лету, не зависит от вендора (один и тот же YAML работает везде) и один API покрывает сразу два направления — вход извне (north-south) и сервис-меш (east-west). Теперь докажем эту фразу и соберём ресурсы по одному.

Четыре ресурса

Сквозной пример — намеренно упрощённая версия Online Boutique от Google, чтобы он ложился на всё, что вы уже видели. Пять сервисов. Четыре из них (web, products, cart, orders) принадлежат команде магазина. Пятый, payments, живёт в неймспейсе другой команды. Запомните эту деталь: именно из-за неё дальше понадобится межнеймспейсная маршрутизация.

1. GatewayClass: шаблон

Шаблон уровня кластера, который называет контроллер, реализующий шлюзы этого класса. Ближайшая аналогия — StorageClass: вы не описываете диск, а просите диск класса fast-ssd, а дальше разбирается provisioner. Здесь так же. Класс отделяет вашу конфигурацию маршрутизации от реализации, и заводит его один раз тот, кто отвечает за инфраструктуру. Команды приложений к нему не прикасаются.

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: prod
spec:
  # контроллер, который запрограммирует реальную инфраструктуру
  controllerName: example.net/gateway-controller

2. Gateway: точка входа

Центральный объект всей модели. Когда вы создаёте Gateway, контроллер поднимает настоящий балансировщик или прокси. У него есть слушатели — порты, протоколы, TLS, и один Gateway — это одна входная дверь в кластер. Здесь же проходит граница ответственности за инфраструктуру: тут живут TLS-сертификаты, а allowedRoutes решает, каким неймспейсам разрешено цеплять маршруты. Это и есть рычаг управления в руках платформенной команды.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shop-gateway
  namespace: infra
spec:
  gatewayClassName: prod
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: Selector   # All | Same | Selector
          selector:
            matchLabels:
              gateway-access: "true"

3. HTTPRoute: правила

Здесь живут разработчики. HTTPRoute матчит запрос по хосту и пути и направляет его в нужный Service, а к Gateway цепляется через parentRefs. Это единственный ресурс, который трогает прикладной разработчик: типизированный, с проверкой по схеме, в собственном неймспейсе. Без прав на кластер, без доступа к TLS. Обратите внимание: parentRefs указывает на Gateway в неймспейсе infra — это межнеймспейсная ссылка. Для привязки к Gateway она разрешена и проходит проверку через его allowedRoutes.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
  namespace: shop
spec:
  parentRefs:
    - name: shop-gateway
      namespace: infra
  hostnames: ["shop.example.com"]
  rules:
    - matches:
        - path: { type: PathPrefix, value: / }
      backendRefs:
        - name: web
          port: 8080

4. Service и поды: пункт назначения

Хорошая новость: этот ресурс вы уже знаете. Обычные Service и поды Kubernetes, ничего специфичного для Gateway API. В цепочке он важен вот почему: поды живут недолго — перезапускаются, переезжают, их IP постоянно меняются, — а Service остаётся на месте. Поэтому HTTPRoute ведёт на Service, а не на под напрямую. backendRefs просто называет Service, который уже есть. Это мост из Gateway API в привычный вам Kubernetes.

Кто за что отвечает

Вот ядро всей идеи — та же цепочка, раскрашенная по зонам ответственности:

За GatewayClass отвечает тот, кто даёт инфраструктуру. За Gateway, его TLS и политику привязки — оператор кластера. За HTTPRoute — прикладной разработчик, в своём неймспейсе и без прав на кластер.

Вспомните беду Ingress: один объект, один владелец, разработчик вынужден лезть в конфиг кластера. Это разделение на три роли и есть структурное лекарство. Каждой команде достаётся ровно столько, сколько нужно, и ни каплей больше.

Собираем путь целиком

Теперь награда за терпение. Четыре ресурса плюс Service и под, собранные вместе, по следам одного запроса: от браузера через GatewayClass, Gateway, HTTPRoute и Service — к живому поду.

Бирюзовый — конфигурация платформы, янтарный — слой приложения, фиолетовый — рабочая нагрузка. Эта цепочка — хребет всего. Любая фича дальше будет вариацией ровно этой картинки.

Практика: маршрутизируем магазин

Пора деплоить. CRD Gateway API в Kubernetes не встроены — вы ставите их сами, и это удобно: они работают на любом Kubernetes 1.30 и новее. Чтобы получить свежую версию Gateway API, кластер обновлять не нужно — достаточно применить новые CRD.

# 1. Установить CRD Gateway API (Standard channel, GA-ресурсы)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/\
releases/download/v1.5.0/standard-install.yaml

# 2. Проверить, что CRD на месте
kubectl get crd | grep gateway.networking.k8s.io

# 3. Установить контроллер (NGINX Gateway Fabric, Istio, Cilium,
#    Envoy Gateway или облачный). Каждый приносит свой GatewayClass.
kubectl get gatewayclass
#   NAME   CONTROLLER                      ACCEPTED
#   prod   example.net/gateway-controller  True

CRD — это словарь, а контроллер — то, что реально следит за объектами и программирует инфраструктуру. Дождитесь ACCEPTED: True — и можно работать.

Дальше платформенная команда ставит входную дверь — пока с одним HTTP-слушателем — и вешает на неё лейбл-замок, который решает, кто вправе цепляться:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shop-gateway
  namespace: infra
spec:
  gatewayClassName: prod
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "true"

Теперь команда магазина вешает на свой неймспейс лейбл-ключ под этот замок и цепляет маршрут. Два YAML-файла, две команды — и ни одной не нужны права другой:

apiVersion: v1
kind: Namespace
metadata:
  name: shop
  labels:
    gateway-access: "true"      # ключ под замок Gateway
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
  namespace: shop
spec:
  parentRefs:
    - name: shop-gateway
      namespace: infra
  hostnames: ["shop.example.com"]
  rules:
    - backendRefs: [{ name: web, port: 8080 }]   # без матчера = матчим всё

Дальше проверьте каждый стык — этот шаг часто пропускают, а потом гадают, почему ничего не работает. Gateway API отдаёт подробный статус, чего Ingress толком не умел:

# Запрограммирован ли Gateway, получил ли адрес?
kubectl get gateway shop-gateway -n infra
#   NAME           CLASS  ADDRESS         PROGRAMMED
#   shop-gateway   prod   203.0.113.10    True

# Чисто ли прицепился маршрут?
kubectl get httproute web -n shop \
  -o jsonpath='{.status.parents[0].conditions}'
#   type: Accepted     status: "True"
#   type: ResolvedRefs status: "True"

# Послать настоящий запрос через входную дверь
curl -H "Host: shop.example.com" http://203.0.113.10/

Три проверки — три стыка: инфраструктура, привязка, трафик. Чтобы расширить магазин, каждая команда добавляет новые HTTPRoute (/products на products, /cart на cart) в своём неймспейсе, ни разу не трогая Gateway и не дёргая платформенную команду. Gateway — общая инфраструктура, маршруты — самообслуживание.

HTTPRoute во всю силу

Путь — это только начало. Матчинг по пути бывает трёх видов: Exact (ровно один путь, удобно для health-эндпойнтов), PathPrefix (префикс и всё, что под ним, — на нём вы построите 90% маршрутов) и RegularExpression — мощный, но в спецификации помечен как зависящий от реализации, поэтому сначала проверьте conformance вашего контроллера. С него часто спотыкаются при переезде с nginx.

Дальше — матчеры, которых у Ingress не было, и все они полноценные типизированные поля: по заголовку (трафик с x-version: beta — на новую сборку), по query-параметру (?debug=true — на отладочный бэкенд) и по методу (POST /orders — на сервис записи, GET /orders — на реплику для чтения). Их можно комбинировать: один матч способен требовать сразу и путь, и заголовок, и метод.

Любимая всеми фича — traffic splitting, canary встроен прямо в маршрут:

cart-v1 с весом 90, cart-v2 с весом 10. Настоящее взвешивание на L7 силами data plane, а не просто лишние реплики. Два backendRefs с весами — вот и весь механизм.
rules:
  - backendRefs:
      - name: cart-v1
        port: 8080
        weight: 90
      - name: cart-v2
        port: 8080
        weight: 10

Чтобы катить дальше, меняете веса: 90/10, потом 50/50, потом 0/100 — и следите за метриками на каждом шаге. Что-то пошло не так — возвращаете веса назад. Для простого canary не нужны ни Argo Rollouts, ни Flagger, хватает весов в HTTPRoute. Эти инструменты по-прежнему полезны для автоматического анализа, но сам примитив уже встроен.

Наконец, фильтры меняют запрос на лету — и тоже типизированно, без аннотаций. RequestHeaderModifier добавляет, перезаписывает или убирает заголовки. RequestRedirect отдаёт типизированный 301 или 302 с настоящим полем statusCode. URLRewrite переписывает путь перед отправкой, так что снаружи адрес остаётся /products, а сам сервис видит только /. Есть ещё RequestMirror — зеркалить трафик на второй бэкенд. Между матчингом, разделением и фильтрами HTTPRoute закрывает почти все реальные нужды маршрутизации — типизированно и переносимо.

Продакшн: TLS, мульти-тенант, межнеймспейсный доступ

Магазин маршрутизируется, но продакшну нужно больше. Сначала TLS — и заметьте, где он живёт: на слушателе Gateway, у платформенной команды, а не в маршруте разработчика.

listeners:
  - name: https
    port: 443
    protocol: HTTPS
    hostname: shop.example.com
    tls:
      mode: Terminate            # расшифровка здесь, дальше plain HTTP
      certificateRefs:
        - kind: Secret
          name: shop-tls         # у платформы, в неймспейсе infra

mode: Terminate означает, что Gateway расшифровывает трафик и отправляет дальше по кластеру обычный HTTP, а certificateRefs указывает на Secret в неймспейсе infra. Разработчики магазина, которые пишут HTTPRoute, не видят сертификат, не занимаются его ротацией и не получают доступ к секретам кластера. Одна команда отвечает за криптографию, другая — за маршрутизацию. Есть и mode: Passthrough для сквозного TLS — он работает в паре с TLSRoute.

Раз Gateway — общая входная дверь, к нему независимо цепляются сразу несколько команд: маршруты команды магазина, отдельная команда маркетинга со своими /promo и /campaigns из своего неймспейса, поддержка со своими /help и /status. TLS настроен один раз, на этом Gateway, и действует для всех. С единственным объектом Ingress так чисто не выйдет — а здесь это естественная форма API.

Но всё это доверенные команды магазина. Сервис payments, который мы пометили в самом начале, живёт в неймспейсе другой команды, и для него нужно явное разрешение:

Межнеймспейсный backendRef магазина к payments по умолчанию запрещён (RefNotPermitted). Команда payments — владелец цели — заводит ReferenceGrant и даёт согласие. Разрешение выдаёт владелец, а не тот, кто просит.

Главное здесь: команда магазина не может выдать себе доступ к payments сама. Согласие даёт команда payments — заводит ReferenceGrant в своём неймспейсе. Это настоящее рукопожатие безопасности, ровно то, что нужно, когда речь о деньгах. В Ingress такое без костылей было невозможно. ReferenceGrant дорос до канала Standard в v1.5 — теперь это стабильный API.

За пределами HTTP — и внутрь меша

У HTTPRoute есть родня. GRPCRoute (GA) обслуживает gRPC с матчингом на уровне методов. TLSRoute дорос до канала Standard в v1.5 — это TLS-passthrough на L4 с маршрутизацией по SNI; если вам попадались старые доклады, где он «экспериментальный», то это уже не так. TCPRoute и UDPRoute пока остаются экспериментальными. Для продакшна важны два канала: Standard стабилен и обратно совместим, Experimental ничего не гарантирует.

И есть целое второе направление трафика. GAMMA (Gateway API for Mesh Management and Administration) распространяет тот же API на трафик между сервисами внутри кластера:

Вход извне (бирюзовый) идёт через Gateway. Трафик восток-запад (янтарный) — это расширение GAMMA: parentRef у HTTPRoute указывает на Service, а не на Gateway. Одна эта замена перенаправляет тот же API маршрутизации на меш.

Весь фокус — в одной замене: parentRef у HTTPRoute указывает на Service, а не на Gateway. И вы получаете canary, маршрутизацию по заголовкам и разделение трафика для внутренних вызовов — в той же форме HTTPRoute, что уже изучили. GAMMA реализуют Istio, Linkerd и меш Cilium, так что это меш-политика без привязки к вендору. Один API, оба направления.

Весь магазин, все концепции сразу

Вот всё на одном экране, на нашем магазине: янтарный shop-gateway на 443 с TLS, неймспейс магазина с четырьмя HTTPRoute (включая canary 90/10 на cart) и отдельный неймспейс команды payments, до которого можно дотянуться только потому, что это разрешает ReferenceGrant.

GatewayClass за Gateway, ресурсы по зонам ответственности, матчинг по пути, разделение трафика, TLS на краю, привязка нескольких команд и межнеймспейсная безопасность через ReferenceGrant. От одного сервиса web — до этого.

Что хочется подчеркнуть: всё это время в деле были одни и те же пять ресурсов. После четырёх ресурсов мы не ввели ни одной новой базовой концепции — только складывали их во всё более богатые сценарии. В этой собираемости из кубиков и есть изящество Gateway API.

Эксплуатация в 2026

Три вещи, чтобы запускать его уверенно. Первое — что сейчас актуально. v1.5 (февраль 2026) — крупнейший релиз, и его тема — перевод экспериментальных фич в стабильные. Главная — ListenerSet: слушатели можно описывать отдельно и подмешивать в Gateway. Отсюда два выигрыша: больше прежнего лимита в 64 слушателя на одном Gateway и возможность для платформенной команды отдавать отдельные слушатели командам приложений. TLSRoute и ReferenceGrant дошли до Standard, а у HTTPRoute появился встроенный фильтр CORS — больше никаких аннотаций ради CORS. Релизы выходят примерно раз в четыре месяца, так что v1.6 ждите ближе к концу 2026.

Второе — и это самая полезная модель в голове: спецификация — это не реализация. Сам API не привязан к вендору и версионируется. Работу же делает контроллер, и каждая реализация сама решает, какие части спецификации поддержать. Поэтому поле может быть полностью в статусе GA в спецификации и всё равно не работать на вашем кластере — просто потому, что ваш контроллер его не реализовал. Классический пример — матчинг по пути через RegularExpression. Не угадывайте — сверяйтесь с отчётом о conformance вашего контроллера. Одна эта привычка снимает большинство вопросов «почему мой маршрут не работает».

Третье — грабли, на которые наступают при реальных миграциях:

  • Семантика путей не совпадает с nginx. Чувствительность к регистру, поведение префиксов, поддержка regex — всё чуть-чуть иначе. Проверьте каждое правило пути, не рассчитывайте на полное совпадение.
  • Межнеймспейсный доступ по умолчанию запрещён. Забыли ReferenceGrant — получите RefNotPermitted, и маршрут молча не запрограммируется. Помните: это защита, а не баг.
  • Читайте статусы. Accepted, ResolvedRefs, Programmed. Когда что-то не так, посмотрите kubectl get по статусу маршрута — он обычно прямо говорит, в чём дело.
  • Не каждая аннотация переносится один в один. Некоторые трюки nginx через config-snippet (сырой rate limiting, свой Lua) пока не имеют типизированного аналога в Gateway API. Заложите время на перенос их в policy-ресурсы конкретного контроллера.

От нуля до героя

Теперь вы знаете каждый базовый ресурс, понимаете, как они складываются, и можете провести приложение от единственного открытого сервиса до безопасной мульти-тенант системы с canary-выкатками — и всё это на одних и тех же пяти кубиках.

С чего начать на практике: поставьте Gateway API v1.5 на любой Kubernetes 1.30 или новее, выберите контроллер с пройденным conformance и откройте первый сервис ровно так, как мы сделали с web. Читайте статусы, сверяйтесь с conformance контроллера — и растите дальше.

Что почитать дальше

  • Слайды: Kubernetes Gateway API: от нуля до героя. Полная дека, на которой построена статья. Анимированные диаграммы удобно прокликать в режиме презентера.
  • Дальше: дека про реализацию на AWS — она переводит эту независимую от вендора основу в конкретику: контроллеры балансировщиков, ACM для сертификатов и VPC Lattice для меша.