С ingress-nginx на Envoy Gateway без простоя: 27 Ingress, 39 HTTPRoute
ingress-nginx ретирован и блокирует апгрейды Kubernetes. Как мы перевели 16 публичных хостов на Envoy Gateway: подняли новый вход рядом со старым и в самом конце поменяли только адрес.
В Optimacros кластер Kubernetes обслуживает публичные маркетинговые сайты компании. Community ingress-nginx ретировали в марте 2026 года, а его последние релизы ограничивают версию Kubernetes: одна линейка упирается в 1.33, финальная — в 1.35. Апгрейд до 1.36 без замены входа был невозможен.
Итог: 16 хостов переехали на Gateway API, 27 объектов Ingress превратились в 39 HTTPRoute, ни одного расхождения с прежним поведением, и прод не падал.
Схема, которая сделала переезд безопасным
Ключ был не в манифестах, а в порядке. Новый вход поднимается на свободном адресе рядом со старым, и в самом конце меняется только адрес.
- Envoy Gateway на запасном адресе, пока прод остаётся на ingress-nginx.
- Сверка всех 16 хостов на обоих адресах.
- Адреса меняются местами — по одной аннотации на каждый. Это единственный момент хоть какого-то влияния.
- Старый вход остаётся жив на запасном адресе, так что откат — одна аннотация.
Сверка — это не «страница открылась». По каждому хосту я сравнивал код ответа и код редиректа через заголовок Host и SNI на обоих адресах одновременно. Совпали 16 из 16.
Ловушка 1: домен кластера не всегда cluster.local
Этот кластер унаследовал от kubespray нестандартный домен. С дефолтом чарта прокси-под резолвил свой xDS-сервер в несуществующее имя, получал NXDOMAIN и висел в 1/2 Running с no healthy upstream. cat /etc/resolv.conf в любом поде покажет настоящий search-домен. Тот же домен попадает в SAN сертификатов, которые генерирует чарт, поэтому одной правки значения мало — сертификаты тоже нужно перевыпустить.
Ловушка 2: Helm-hook не перезапускается рефрешем
Генератор сертификатов — это pre-install и pre-upgrade hook. Если Application уже Synced, Argo CD не видит работы и не запускает его, а ServiceAccount хука тоже удаляется после отработки. Я отрендерил этот набор через helm template, убрал hook-аннотации и применил руками.
Ловушка 3: basic auth только в формате {SHA}
ingress-nginx хранил хеши apr1. SecurityPolicy в Envoy Gateway отвергает их с unsupported htpasswd format: please use {SHA}, а ключ должен называться .htpasswd. Где пароль был известен, я пересчитал хеш. Где существовал только хеш apr1, пароль восстановить было нельзя — только ротировать. Старый ключ я оставил рядом с новым: пока оба формата лежат в одном Secret, работают оба входа, и откат остаётся живым.
Ловушка 4: аннотации без прямого аналога
| ingress-nginx | Gateway API |
|---|---|
ssl-redirect |
отдельный HTTPRoute на listener :80 с RequestRedirect, статус 308, как у nginx |
auth-type: basic |
SecurityPolicy.basicAuth на отдельный маршрут, иначе пароль спросят на всём сайте |
rewrite-target с regex |
часто это просто замена префикса: ReplacePrefixMatch |
backend-protocol: HTTPS |
BackendTLSPolicy, которому нужен стабильный CA |
| именованный порт бэкенда | в backendRefs.port только числа |
Сервер Argo CD генерирует свежий сертификат в памяти при каждом старте, так что CA, из которого можно построить BackendTLSPolicy, просто нет. За внешней терминацией TLS штатный ответ — server.insecure=true, и одновременно нужно снять аннотацию HTTPS-бэкенда со старого Ingress.
Ловушка 5: инвентаризация врёт, пока не проверишь по сети
- Восемь TLS-секретов оказались двумя сертификатами: один wildcard лежал пятью копиями под разными именами и уже покрывал apex.
- Двух упомянутых секретов не существовало вовсе. Эти хосты годами отдавали fake-сертификат ingress-контроллера, и никто не замечал.
- Два Ingress были остатками удалённого приложения: нет DNS, нет Service, 503 на проде. Их удалили, а не переносили.
Мелочи, съевшие время
- Папка kustomize применяет только то, что перечислено в
resources:. Файл, положенный рядом, молча игнорируется. - Политика Kyverno, клонировавшая TLS-секрет при появлении Ingress, на Gateway не срабатывает. Нужно отдельное правило и RBAC на
gateways. - App-of-apps обновляется сверху вниз: рефреш ребёнка, пока родитель на старой ревизии, ничего не даёт.
- Ссылка из Gateway на Secret в другом namespace требует ReferenceGrant в namespace владельца.
Что взять с собой
- Поднимайте новый вход рядом со старым и меняйте только адрес. Тогда переключение и откат — по одной аннотации.
- Перед переключением сверьте коды ответа и редиректа по каждому хосту на обоих адресах.
- Проверьте домен кластера, прежде чем ставить что-то, что резолвит внутрикластерные имена.
- Сверяйте TLS-секреты с тем, что реально отдаёт прод.
- Не удаляйте старый вход в день переключения. Это ваш откат.