Що потрібно знати перед міграцією: пʼять несподіваних особливостей поведінки Ingress-NGINX
Як було оголошено в листопаді 2025 року, Kubernetes припинить підтримку Ingress-NGINX у березні 2026 року. Незважаючи на широке використання, Ingress-NGINX має безліч несподіваних стандартних налаштувань і побічних ефектів, які, ймовірно, присутні у вашому кластері зараз. У цьому дописі описано ці особливості, щоб ви могли безпечно здійснити міграцію та свідомо вирішити, які з них зберегти. У дописі також порівнюється Ingress-NGINX та Gateway API і показано, як зберегти особливості Ingress-NGINX у Gateway API. Ризик, що повторюється в кожному розділі, є однаковим: на перший погляд правильно виконане перетворення все одно може спричинити перебої в роботі, якщо не врахувати особливості Ingress-NGINX.
Я припускаю, що ви, читачі, маєте певне уявлення про Ingress-NGINX та Ingress API. У більшості прикладів як бекенд використовується httpbin.
Також зверніть увагу, що Ingress-NGINX і NGINX Ingress — це два окремі контролери Ingress. Ingress-NGINX — це контролер Ingress, який підтримується та керується спільнотою Kubernetes і який буде виведений з експлуатації в березні 2026 року. NGINX Ingress — це контролер Ingress від F5. Обидва використовують NGINX як рівень обробки даних, але в іншому не повʼязані між собою. Відтепер у цій публікації ми будемо обговорювати лише Ingress-NGINX.
1. Збіги регулярних виразів базуються на префіксах і не чутливі до регістру
Припустимо, ви хочете перенаправити всі запити зі шляхом, що складається лише з трьох великих літер, до сервісу httpbin. Ви можете створити наступний Ingress з анотацією nginx.ingress.kubernetes.io/use-regex: "true" та регулярним виразом /[A-Z]{3}.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "/[A-Z]{3}"
pathType: ImplementationSpecific
backend:
service:
name: httpbin
port:
number: 8000
Однак, оскільки регулярні вирази не враховують префікси та регістр, Ingress-NGINX перенаправляє будь-який запит із шляхом, що починається з будь-яких трьох літер, на httpbin:
curl -sS -H "Host: regex-match.example.com" http://<your-ingress-ip>/uuid
Результат буде схожим на:
{
"uuid": "e55ef929-25a0-49e9-9175-1b6e87f40af7"
}
Примітка: Точка доступу /uuid httpbin повертає випадковий UUID. UUID у тілі відповіді означає, що запит був успішно перенаправлений до httpbin.
За допомогою Gateway API ви можете використовувати HTTP path match з type типу RegularExpression для зіставлення шляхів за регулярним виразом. Зіставлення RegularExpression залежать від конкретної реалізації, тому перевірте реалізацію Gateway API, щоб переконатися в семантиці зіставлення RegularExpression. Популярні реалізації Gateway API на основі Envoy, такі як Istio1, Envoy Gateway та Kgateway, виконують повне співставлення з урахуванням регістру.
Отже, якщо ви не знаєте, що шаблони Ingress-NGINX є префіксами і не чутливі до регістру, і, не знаючи про це, клієнти або застосунки надсилають трафік до /uuid (або /uuid/some/other/path), ви можете створити такий маршрут HTTP.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
parentRefs:
- name: <your gateway> # Змінюйте це залежно від вашого випадку використання
rules:
- matches:
- path:
type: RegularExpression
value: "/[A-Z]{3}"
backendRefs:
- name: httpbin
port: 8000
Однак, якщо ваша реалізація Gateway API виконує повне співставлення з урахуванням регістру, вищевказаний маршрут HTTP не буде відповідати запиту зі шляхом /uuid. Таким чином, вищевказаний маршрут HTTP спричинить збій, оскільки запити, які Ingress-NGINX маршрутизував до httpbin, завершаться з помилкою 404 Not Found на шлюзі.
Щоб зберегти співставлення регулярних виразів без урахування регістру, ви можете використовувати наступний маршрут HTTP.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
parentRefs:
- name: <your gateway> # Змінюйте це залежно від вашого випадку використання
rules:
- matches:
- path:
type: RegularExpression
value: "/[a-zA-Z]{3}.*"
backendRefs:
- name: httpbin
port: 8000
Крім того, вищезазначені проксі підтримують прапорець (?i) для позначення збігів, нечутливих до регістру. Використовуючи цей прапорець, шаблон може бути таким: (?i)/[a-z]{3}.*.
2. nginx.ingress.kubernetes.io/use-regex застосовується до всіх шляхів хоста у всіх (Ingress-NGINX) Ingresses
Тепер припустимо, що у вас є Ingress з анотацією nginx.ingress.kubernetes.io/use-regex: "true", але ви хочете маршрутизувати запити з шляхом /headers до httpbin. На жаль, ви зробили помилку і встановили шлях /Header замість /headers.
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "<some regex pattern>"
pathType: ImplementationSpecific
backend:
service:
name: <your backend>
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress-other
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "/Header" # тут помилка, має бути /headers
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Більшість очікує, що запит до /headers поверне відповідь 404 Not Found, оскільки /headers не відповідає Exact шляху /Header. Однак, оскільки Ingress regex-match-ingress має анотацію nginx.ingress.kubernetes.io/use-regex: "true" і хост regex-match.example.com, всі шляхи з хостом regex-match.example.com розглядаються як регулярні вирази у всіх (Ingress-NGINX) Ingress. Оскільки шаблони регулярних виразів є префіксними збігами, що не чутливі до регістру, /headers збігається з шаблоном /Header, і Ingress-NGINX маршрутизує такі запити до httpbin. Виконання команди
curl -sS -H "Host: regex-match.example.com" http://<your-ingress-ip>/headers
дасть наступний результат:
{
"headers": {
...
}
}
Примітка: Точка доступу /headers httpbin повертає заголовки запиту. Те, що відповідь містить заголовки запиту в тілі, означає, що запит був успішно перенаправлений на httpbin.
Gateway API не перетворює і не інтерпретує збіги Exact і Prefix як шаблони регулярних виразів. Тому, якщо ви перетворили вищезазначені Ingresses у наступний маршрут HTTP і зберегли типи помилок і збігів, запити до /headers будуть відповідати кодом 404 Not Found замість 200 OK.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
rules:
...
- matches:
- path:
type: Exact
value: "/Header"
backendRefs:
- name: httpbin
port: 8000
Щоб зберегти відповідність префіксів без врахування регістру, можна змінити
- matches:
- path:
type: Exact
value: "/Header"
на
- matches:
- path:
type: RegularExpression
value: "(?i)/Header"
А ще краще, ви могли б виправити помилку і змінити шаблон на
- matches:
- path:
type: Exact
value: "/headers"
3. Переписування цільового обʼєкта передбачає використання регулярного виразу
У цьому випадку припустимо, що ви хочете переписати шлях запитів із /ip на /uuid перед їх маршрутизацією до httpbin, і, як у розділі 2, ви хочете маршрутизувати запити із шляхом /headers до httpbin. Однак ви випадково зробили помилку і встановили шлях /IP замість /ip і /Header замість /headers.
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-target-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: "/uuid"
spec:
ingressClassName: nginx
rules:
- host: rewrite-target.example.com
http:
paths:
- path: "/IP"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-target-ingress-other
spec:
ingressClassName: nginx
rules:
- host: rewrite-target.example.com
http:
paths:
- path: "/Header"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Анотація nginx.ingress.kubernetes.io/rewrite-target: «/uuid» призводить до того, що запити, які відповідають шляхам в Ingress rewrite-target-ingress, мають свої шляхи перетворені на /uuid перед тим, як бути перенаправленими на бекенд.
Незважаючи на те, що жоден Ingress не має анотації nginx.ingress.kubernetes.io/use-regex: "true", наявність анотації nginx.ingress.kubernetes.io/rewrite-target в Ingress rewrite-target-ingress призводить до того, що всі шляхи з хостом rewrite-target.example.com розглядаються як шаблони регулярних виразів. Іншими словами, nginx.ingress.kubernetes.io/rewrite-target тихо додає анотацію nginx.ingress.kubernetes.io/use-regex: "true", разом з усіма побічними ефектами, описаними вище.
Наприклад, шлях запиту до /ip перезаписується на /uuid, оскільки /ip відповідає префіксу /IP, що не чутливий до регістру, в Ingress rewrite-target-ingress. Після виконання команди
curl -sS -H "Host: rewrite-target.example.com" http://<your-ingress-ip>/ip
результат буде схожим на:
{
"uuid": "12a0def9-1adg-2943-adcd-1234aadfgc67"
}
Як і в прикладі nginx.ingress.kubernetes.io/use-regex, Ingress-NGINX обробляє path інших інгресів з хостом rewrite-target.example.com як префіксні шаблони, що не розрізняють регістр. Виконання команди
curl -sS -H "Host: rewrite-target.example.com" http://<your-ingress-ip>/headers
дає результат, який виглядає так
{
"headers": {
...
}
}
Ви можете налаштувати перезапис шляхів у Gateway API за допомогою фільтра перезапису HTTP URL, який не перетворює ваші збіги Exact та Prefix у шаблони регулярних виразів. Однак, якщо ви не знаєте про побічні ефекти анотації nginx.ingress.kubernetes.io/rewrite-target і не розумієте, що /Header і /IP є помилками, ви можете створити такий маршрут HTTP.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: rewrite-target-route
spec:
hostnames:
- rewrite-target.example.com
parentRefs:
- name: <your-gateway>
rules:
- matches:
- path:
type: Exact
value: "/IP"
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplaceFullPath
replaceFullPath: /uuid
backendRefs:
- name: httpbin
port: 8000
- matches:
- path:
# Це точне співпадіння, незалежно від інших правил.
type: Exact
value: "/Header"
backendRefs:
- name: httpbin
port: 8000
Як і в розділі 2, оскільки /IP тепер є типом збігу Exact у вашому маршруті HTTP, запити до /ip будуть відповідати кодом 404 Not Found замість 200 OK. Аналогічно, запити до /headers також будуть відповідати кодом 404 Not Found замість 200 OK. Таким чином, цей маршрут HTTP порушить роботу застосунків і клієнтів, які покладаються на маршрути /ip і /headers.
Щоб виправити це, ви можете змінити збіги в маршруті HTTP на збіги регулярних виразів і змінити шаблони шляхів на збіги префіксів, що не чутливі до регістру, як показано нижче.
- matches:
- path:
type: RegularExpression
value: "(?i)/IP.*"
...
- matches:
- path:
type: RegularExpression
value: "(?i)/Header.*"
Або ви можете залишити тип збігу Exact і виправити помилки.
4. Запити, в яких відсутній кінцевий слеш, перенаправляються на той самий шлях з кінцевим слешем
Розглянемо наступний Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: trailing-slash-ingress
spec:
ingressClassName: nginx
rules:
- host: trailing-slash.example.com
http:
paths:
- path: "/my-path/"
pathType: Exact
backend:
service:
name: <your-backend>
port:
number: 8000
Ви можете очікувати, що Ingress-NGINX відповість на /my-path кодом 404 Not Found, оскільки /my-path не збігається з Exact шляхом /my-path/. Однак Ingress-NGINX перенаправляє запит на /my-path/ з кодом 301 Moved Permanently, оскільки єдина відмінність між /my-path і /my-path/ — це кінцевий слеш.
curl -isS -H "Host: trailing-slash.example.com" http://<your-ingress-ip>/my-path
Результат виглядає так:
HTTP/1.1 301 Moved Permanently
...
Location: http://trailing-slash.example.com/my-path/
...
Те саме стосується випадків, коли ви змінюєте pathType на Prefix. Однак перенаправлення не відбувається, якщо шлях є шаблоном регулярного виразу.
Сумісні реалізації Gateway API не конфігурують жодних перенаправлень без попередження. Якщо клієнти або сервіси нижчого рівня залежать від цього перенаправлення, міграція до Gateway API, який не конфігурує перенаправлення запитів явно, спричинить збій, оскільки запити до /my-path тепер будуть відповідати кодом 404 Not Found замість 301 Moved Permanently. Ви можете явно налаштувати перенаправлення за допомогою фільтра перенаправлення HTTP-запитів наступним чином:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: trailing-slash-route
spec:
hostnames:
- trailing-slash.example.com
parentRefs:
- name: <your-gateway>
rules:
- matches:
- path:
type: Exact
value: "/my-path"
filters:
requestRedirect:
statusCode: 301
path:
type: ReplaceFullPath
replaceFullPath: /my-path/
- matches:
- path:
type: Exact # або Prefix
value: "/my-path/"
backendRefs:
- name: <your-backend>
port: 8000
5. Ingress-NGINX нормалізує URL-адреси
Нормалізація URL-адреси — це процес перетворення URL-адреси в канонічну форму перед її зіставленням з правилами Ingress і маршрутизацією. Особливості нормалізації URL-адреси визначені в RFC 3986, розділ 6.2, але ось декілька прикладів
- видалення сегментів шляху, які є лише
.:my/./path -> my/path - сегмент шляху
..видаляє попередній сегмент:my/../path -> /path - видалення послідовних косих рисок у шляху:
my//path -> my/path
Ingress-NGINX нормалізує URL-адреси перед їх порівнянням з правилами Ingress. Наприклад, розглянемо наступний Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-normalization-ingress
spec:
ingressClassName: nginx
rules:
- host: path-normalization.example.com
http:
paths:
- path: "/uuid"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Ingress-NGINX нормалізує шлях наступних запитів до /uuid. Тепер, коли запит відповідає шляху Exact /uuid, Ingress-NGINX відповідає або кодом 200 OK, або кодом 301 Moved Permanently до /uuid.
Для наступних команд
curl -sS -H "Host: path-normalization.example.com" http://<your-ingress-ip>/uuid
curl -sS -H "Host: path-normalization.example.com" http://<your-ingress-ip>/ip/abc/../../uuid
curl -sSi -H "Host: path-normalization.example.com" http://<your-ingress-ip>////uuid
результати схожі на
{
"uuid": "29c77dfe-73ec-4449-b70a-ef328ea9dbce"
}
{
"uuid": "d20d92e8-af57-4014-80ba-cf21c0c4ffae"
}
HTTP/1.1 301 Moved Permanently
...
Location: /uuid
...
Ваші бекенди можуть покладатися на реалізацію API Ingress/Gateway для нормалізації URL-адрес. При цьому більшість реалізацій API Gateway мають стагжартно увімкнену функцію нормалізації шляхів. Наприклад, Istio, Envoy Gateway та Kgateway нормалізують сегменти . та .. без додаткових налаштувань. Для отримання детальнішої інформації перегляньте документацію для кожної реалізації API Gateway, яку ви використовуєте.
Підсумок
Оскільки ми всі поспішаємо реагувати на виведення з експлуатації Ingress-NGINX, сподіваюся, що ця публікація додасть вам впевненості в тому, що ви зможете безпечно та ефективно здійснити міграцію, незважаючи на всі складнощі Ingress-NGINX.
SIG Network також працює над підтримкою найпоширеніших анотацій Ingress-NGINX (та деяких несподіваних поведінок) в Ingress2Gateway, щоб допомогти вам перетворити конфігурацію Ingress-NGINX на Gateway API та запропонувати альтернативи для непідтримуваних поведінок.
SIG Network випустила Gateway API 1.5 сьогодні (27 лютого 2026 року), яка вдосконалює такі функції, як ListenerSet (що дозволяє розробникам додатків краще управляти сертифікатами TLS) та фільтр HTTPRoute CORS, що дозволяє конфігурувати CORS.
Ви можете використовувати Istio виключно як контролер Gateway API без інших функцій сервісної мережі. ↩︎