Трендовые github проекты в нашем телеграм канале. Подпишись → Что делать с кластером, который держится на аннотациях nginx
Откройте любой production-кластер и посмотрите на Ingress-объекты. Скорее всего, в них найдётся набор вроде такого:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
Формально это Kubernetes Ingress. Фактически это конфигурация NGINX, которая по историческим причинам записана в аннотации Kubernetes. Пока сервисов пять, разница незаметна. Когда их триста и несколько кластеров, эти аннотации превращаются в слой знаний, который держат в голове два-три инженера.
Проект ingress-nginx завершил поддержку. Для многих команд это первый за годы повод остановиться и спросить: как маршрутизация трафика в кластере должна выглядеть ближайшие пять лет.
Ingress остаётся, заканчивается конкретный контроллер
Стоит разделить две вещи. Ресурс Kubernetes Ingress никуда не делся, NGINX тоже. Закрылась история одного контроллера. При этом основная сложность возникла раньше и не связана с поддержкой: Ingress начали применять для задач, под которые он изначально слишком простой.
Регулярные выражения в путях, нестандартный rewrite, канареечные развёртывания через веса, глобальные параметры в ConfigMap контроллера — всё это живёт поверх спецификации Ingress и зависит от реализации. Когда контроллер меняется, именно этот пласт требует ручной работы.
Gateway API как модель ответственности
Главная ценность Gateway API проявляется не в синтаксисе и не в новых возможностях роутинга. Он разделяет инфраструктуру и приложения на уровне API.
Платформенная команда отвечает за внешний балансировщик, TLS, сетевую безопасность и наблюдаемость. Она описывает Gateway и GatewayClass:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: platform-gateway
namespace: infra-gateway
spec:
gatewayClassName: envoy
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true"
Команда приложения работает только со своим маршрутом и не трогает сетевую инфраструктуру:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders
namespace: orders
spec:
parentRefs:
- name: platform-gateway
namespace: infra-gateway
hostnames:
- shop.example.internal
rules:
- matches:
- path:
type: PathPrefix
value: /api/orders
backendRefs:
- name: orders-service
port: 8080
Права на Gateway и HTTPRoute разводятся через RBAC и allowedRoutes. Разработчик самостоятельно управляет путями и бэкендами, но не может переопределить TLS или политику балансировщика. Для больших платформ это ближе к реальному распределению обязанностей, чем общий пул аннотаций.
Сценарий, который выглядит логичным и приводит к боли
«У нас 400 Ingress, пишем конвертер и переносим всё разом». Технически часть работы автоматизируется, и через пару дней выясняется, что 350 объектов — это простые host → path → service, а оставшиеся 50 содержат всю накопленную специфику: regex, кастомный rewrite, канарейки, зависимость от глобальной настройки в ConfigMap пятилетней давности.
Автоматическая конвертация YAML переносит форму, но не поведение. После неё начинается настоящая работа: проверить, что новый роутинг обрабатывает те же запросы так же, как старый. Сложные случаи разбираются поштучно, а не пакетом.
Миграция без миграции
Рабочий подход для инфраструктуры с десятками кластеров: не объявлять большой проект перевода. Старый ingress продолжает обслуживать существующие сервисы. Рядом разворачивается контроллер Gateway API.
Дальше меняется одно: внутренний Helm-чарт для новых сервисов перестаёт создавать kind: Ingress и начинает создавать HTTPRoute. Внутренняя документация описывает Gateway API как стандартный способ публикации. Платформенная команда строит вокруг новой модели шаблоны, проверки и наблюдаемость.
Через полгода большинство новых приложений уже не зависит от старого контроллера. Через год часть старых сервисов переписана или выведена из эксплуатации по своим причинам. Вместо миграционного проекта на 500 сервисов получается постепенное сокращение технического долга.
Начинать с аудита, а не с выбора контроллера
Частая ошибка — сразу спорить про реализацию: Envoy Gateway, Traefik, NGINX Gateway Fabric, Cilium. Выбор контроллера — не первый вопрос. Первый вопрос: что именно сейчас делает ваш ingress-nginx.
Список используемых nginx-аннотаций с частотой:
kubectl get ingress -A -o json \
| jq -r '.items[].metadata.annotations // {} | keys[]' \
| grep nginx.ingress.kubernetes.io | sort | uniq -c | sort -rn
Сервисы, где включены regex или канареечные развёртывания:
kubectl get ingress -A -o json \
| jq -r '.items[] | select(
(.metadata.annotations // {})["nginx.ingress.kubernetes.io/use-regex"] == "true"
or (.metadata.annotations // {})["nginx.ingress.kubernetes.io/canary"] == "true"
) | .metadata.namespace + "/" + .metadata.name'
Отдельно стоит зафиксировать глобальные параметры из ConfigMap контроллера и правила, которые заданы вне самих Ingress. Обычно после такого аудита картина проясняется: основная масса маршрутов простая, а вся сложность сосредоточена в небольшом числе сервисов, и с ними работают точечно.
Не у всех задача требует Gateway API. Если нужно быстро заменить неподдерживаемый компонент с минимальными изменениями, разумно взять другой ingress-контроллер и не трогать архитектуру. Gateway API оправдан там, где кластер стал внутренней платформой для десятков команд и вопрос звучит так: как дать сотням разработчиков управлять маршрутизацией, не выдавая доступ к сетевой инфраструктуре.
Практический вывод
Ingress останется в обиходе надолго: он простой и покрывает большинство сценариев. Завершение поддержки одного контроллера стоит воспринимать как повод открыть свои Ingress-объекты и посмотреть на них свежим взглядом.
Минимальный первый шаг для платформы, завязанной на ingress-nginx: перестать создавать новые зависимости от старой модели. Существующие приложения работают дальше, production никто не ломает. Новые сервисы получают HTTPRoute и стандартный шаблон публикации. Дальше время работает на платформу — каждый новый сервис появляется в новой архитектуре, каждый старый мигрирует отдельно, и в какой-то момент ingress-nginx превращается из критического компонента в обычную legacy-зависимость.