Ingress: пускаем трафик снаружи

Рейтинг: 48.7% · 7 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 26
Зарегистрирован: 12 май 2026, 03:23

Ingress: пускаем трафик снаружи

Сообщение anton_k8s »

АкадемияKubernetes на практикеГлава 7 из 19
Оглавление курса (19)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи (вы здесь)
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
В пятой главе мы открыли подам сетевой доступ через Service и упёрлись в ограничение: ClusterIP виден только внутри кластера, NodePort выдаёт неудобные порты вроде 30080, а LoadBalancer в облаке стоит денег за каждый сервис. Ingress решает задачу одной точкой входа: весь внешний HTTP-трафик заходит на один адрес, а дальше разлетается по доменам и путям к нужным сервисам. Поднимем ingress-nginx, опишем правила маршрутизации и прикрутим TLS.

Что такое Ingress на самом деле:

Главное, на чём спотыкаются новички: Ingress сам по себе ничего не делает. Это просто объект с правилами вида "запросы на shop.local/api отдавай сервису api-svc". Чтобы правила ожили, в кластере должен работать ingress-контроллер, реверс-прокси (nginx, traefik, haproxy), который следит за Ingress-объектами и на лету перестраивает свой конфиг.

Для учёбы возьмём ingress-nginx: он до сих пор стоит в огромном числе кластеров и в minikube включается одной командой. Но сразу важная оговорка про его статус. В ноябре 2025 проект Kubernetes официально объявил retirement ingress-nginx, и в марте 2026 закончилась даже best-effort поддержка: новых релизов и security-фиксов больше не будет. InGate, который анонсировали как преемника, тоже свёрнут, до рабочего состояния он не дошёл. Контекст у решения тяжёлый: годы нехватки мейнтейнеров плюс история IngressNightmare весной 2025 (CVE-2025-1974 и смежные, CVSS 9.8), когда через admission webhook контроллера можно было без аутентификации выполнить код и дотянуться до всех секретов кластера. Вывод для практики: учиться на ingress-nginx локально нормально, концепции (правила, классы, TLS-секреты) одинаковы у всех контроллеров, но новые продакшен-инсталляции в 2026 на нём не делают. Пути миграции: реализации Gateway API (Envoy Gateway, Cilium, Istio, NGINX Gateway Fabric) либо живые Ingress-контроллеры вроде Traefik, HAProxy Ingress или NGINX Ingress Controller от F5 (это отдельный проект, не путайте с ingress-nginx).

В minikube контроллер включается аддоном:

Код: Выделить всё

minikube addons enable ingress
kubectl get pods -n ingress-nginx
Дождитесь, пока под ingress-nginx-controller перейдёт в Running. С kind сложнее: кластер нужно создавать с пробросом портов 80 и 443 (extraPortMappings в конфиге кластера), а контроллер ставить отдельным манифестом для kind. Если вы на kind, проще пересоздать кластер, во второй главе мы делали это за минуту.

Правила маршрутизации:

Возьмём два сервиса из прошлых глав: web-svc (фронтенд) и api-svc (бэкенд), оба типа ClusterIP на порту 80. Разведём трафик двумя способами сразу: на shop.local делим по пути, а всё, что пришло на отдельный хост api.shop.local, целиком уходит на бэкенд. Файл ingress.yaml:

Код: Выделить всё

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "20m"
spec:
  ingressClassName: nginx
  defaultBackend:
    service:
      name: web-svc
      port:
        number: 80
  rules:
  - host: shop.local
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-svc
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-svc
            port:
              number: 80
  - host: api.shop.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-svc
            port:
              number: 80
Поле ingressClassName говорит, какой контроллер должен обрабатывать этот объект. Если поле не задать, объект достанется контроллеру дефолтного класса, того, у которого на IngressClass висит аннотация ingressclass.kubernetes.io/is-default-class: "true". Проверить, есть ли дефолтный класс в кластере, можно командой kubectl get ingressclass, он помечен в выводе. Надёжнее всё равно писать явно. Второе правило с host: api.shop.local это name-based virtual hosting: контроллер читает заголовок Host и разводит запросы по разным доменам с одного IP. defaultBackend ловит всё, что не совпало ни с одним правилом, например запросы по голому IP без знакомого Host; без него контроллер ответит собственным 404.

pathType: Prefix сравнивает путь по сегментам: /api совпадёт с /api и /api/users, но не с /apiv2. Есть ещё Exact (точное совпадение) и ImplementationSpecific (трактовку пути определяет контроллер). Последний не экзотика: в ingress-nginx регулярные выражения в path разрешены только с ним. Никакой автоконвертации нет, наоборот: начиная с ingress-nginx 1.12 опция strict-validate-path-type включена по умолчанию, и validating webhook отклонит Ingress, у которого при Prefix или Exact в path есть что-то кроме букв, цифр, '-', '_' и '/'. Об этом ниже в граблях. Более специфичный путь побеждает, поэтому /api уйдёт на бэкенд, а всё остальное на фронтенд. Аннотация proxy-body-size заодно поднимает лимит на тело запроса: у nginx по умолчанию 1m, и загрузка файлов падает с ошибкой 413.

Применяем и проверяем:

Код: Выделить всё

kubectl apply -f ingress.yaml
grep -q shop.local /etc/hosts || echo "$(minikube ip) shop.local api.shop.local" | sudo tee -a /etc/hosts
curl http://shop.local/api/health
curl http://api.shop.local/health
Проверка через grep не случайна: tee -a дописывает строку не глядя, и при повторном запуске вы наплодите в /etc/hosts дублей, а потом будете гадать, на какой IP на самом деле ходит curl. На Linux этого хватает. На macOS с docker-драйвером IP миникуба напрямую недоступен: запустите в отдельной вкладке minikube tunnel и пропишите в /etc/hosts 127.0.0.1 вместо minikube ip.

TLS:

Шифрование вешается на тот же Ingress. Для локальной разработки сертификат генерируется через mkcert или openssl, в проде его выдаст Let's Encrypt (обычно через cert-manager, но это отдельная история). Кладём сертификат в секрет специального типа tls, секреты мы разбирали в шестой главе:

Код: Выделить всё

kubectl create secret tls shop-tls --cert=shop.crt --key=shop.key

# в spec ингресса, рядом с rules:
  tls:
  - hosts:
    - shop.local
    secretName: shop-tls
После apply контроллер начнёт отдавать shop.local по https, а http сам редиректить на https. Это поведение ingress-nginx по умолчанию, отключается аннотацией ssl-redirect со значением "false".

Gateway API, куда всё движется:

Сам Ingress API из Kubernetes никуда не денется, но он заморожен: новые возможности в него не добавляют. Развитие ушло в Gateway API, который стабилен с версии 1.0 ещё с конца 2023 и давно обкатан в проде. Идея та же, декларативная маршрутизация, но устройство другое: вместо одного объекта три уровня с разделением ролей. GatewayClass описывает реализацию (аналог IngressClass), Gateway это сама точка входа с листенерами, её заводит платформенная команда, а HTTPRoute с правилами маршрутизации пишут разработчики приложений. Всё, что в Ingress делалось вендорскими аннотациями (матчинг по заголовкам, веса для канареек, переписывание путей), в HTTPRoute является штатными полями спеки, поэтому конфиг переносится между контроллерами без переделки. Новые проекты в 2026 разумно стартовать сразу с Gateway API, тем более что после retirement ingress-nginx миграция многим и так предстоит. Но Ingress знать обязательно: в работающих кластерах его тонны, и поддерживать это кому-то придётся ещё годы.

Типичные грабли:

Nginx отвечает 404, хотя сервис живой. Запрос дошёл до контроллера, но не совпал ни с одним правилом, а defaultBackend не задан. Чаще всего виноват Host: запрос по голому IP не попадёт в правило с host: shop.local. Добавьте к curl флаг -H с нужным заголовком Host и проверьте снова.

Ingress создан, но контроллер его игнорирует. Обычно забыли ingressClassName, а дефолтного класса в кластере нет. Кстати, два класса, помеченных default одновременно, ничего не ломают, хотя старая формулировка об этом до сих пор гуляет по докам: начиная с Kubernetes 1.25 admission-плагин DefaultIngressClass в такой ситуации берёт самый свежий класс по creationTimestamp и сам проставляет его в ingressClassName, а до 1.25 API-сервер просто отклонял создание Ingress без поля с ошибкой. Полагаться на этот автоматический выбор не стоит, держите ровно один default. Смотрите kubectl describe ingress shop-ingress: если поле Address пустое дольше минуты, объект никто не взял. Список классов и пометку default покажет kubectl get ingressclass.

Браузер ругается на сертификат "Kubernetes Ingress Controller Fake Certificate". Это заглушка контроллера: секрет не найден. Либо опечатка в secretName, либо секрет лежит в другом namespace. Ingress, секрет и сервисы должны жить в одном namespace.

Бэкенд получает запросы с префиксом /api и сам отвечает 404. Ingress по умолчанию не режет путь. В ingress-nginx это лечится аннотацией rewrite-target: в path пишется регулярка с capture-группами, например /api(/|$)(.*), а в nginx.ingress.kubernetes.io/rewrite-target ставится /$2. Только сначала смените pathType: с регуляркой он обязан быть ImplementationSpecific, сам контроллер ничего не конвертирует. Оставите Prefix, как в нашем примере выше, и apply на актуальной версии упадёт: с ingress-nginx 1.12 опция strict-validate-path-type включена по умолчанию, и validating webhook отклонит такой объект ещё на входе. Цену понимать обязательно: матчинг с регуляркой перестаёт быть честным Prefix и привязан к конкретному контроллеру. При переезде, например на Gateway API, такое правило придётся переписать (там для этого есть штатный фильтр URLRewrite).

Что усвоили:

Ingress это правила, контроллер это исполнитель, и без второго первое бесполезно. Мы подняли ingress-nginx, развели трафик по хостам и путям, поставили catch-all через defaultBackend, повесили TLS и научились читать 404 контроллера. Сам Ingress API остаётся в Kubernetes, но развитие ушло в Gateway API, а ingress-nginx с марта 2026 не получает даже security-фиксов, так что для продакшена выбирайте контроллер из живых. Дальше займёмся данными: в восьмой главе разберём Volumes и PersistentVolumeClaim, чтобы поды переставали терять файлы при рестарте.
👍5 ❤️1 🔥1 😄 🤔
✔ Лучший ответ сформирован автоматически — tornerd
anton_k8s писал(а):Ingress сам по себе ничего не делает а если поставить два контроллера сразу, nginx и traefik? они подерутся за объекты или каждый возьмет свое по ingressClassName? у нас в проде traefik, хочу локально повторить ту же схему
Перейти к ответу →
Аватара пользователя
tornerd
Сообщения: 1
Зарегистрирован: 21 май 2026, 11:40

Re: Ingress: пускаем трафик снаружи

Сообщение tornerd »

✔ Лучший ответ — сформирован автоматически
anton_k8s писал(а):Ingress сам по себе ничего не делает
а если поставить два контроллера сразу, nginx и traefik? они подерутся за объекты или каждый возьмет свое по ingressClassName? у нас в проде traefik, хочу локально повторить ту же схему
👍1 ❤️2 🔥1 😄 🤔
Аватара пользователя
muttdogg
Сообщения: 2
Зарегистрирован: 12 май 2026, 18:08

Re: Ingress: пускаем трафик снаружи

Сообщение muttdogg »

спасибо за абзац про мак, я на m2 с docker драйвером полчаса не мог понять почему curl висит. minikube tunnel реально надо держать открытым в отдельной вкладке, закрыл терминал и все отвалилось
👍2 ❤️1 🔥 😄 🤔
Аватара пользователя
ksanders
Сообщения: 1
Зарегистрирован: 11 май 2026, 18:55

Re: Ingress: пускаем трафик снаружи

Сообщение ksanders »

маленькое дополнение: аддон minikube помечает класс nginx как дефолтный (аннотация ingressclass.kubernetes.io/is-default-class), поэтому у меня все заработало даже без ingressClassName. но в манифестах все равно пишу явно, в облачном кластере дефолта может и не быть
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
snell99
Сообщения: 2
Зарегистрирован: 11 май 2026, 02:58

Re: Ingress: пускаем трафик снаружи

Сообщение snell99 »

а если домена нет и хочется ходить просто по ip кластера, можно правило вообще без host? или без записи в /etc/hosts никак
👍 ❤️1 🔥 😄 🤔1
Ответить
← Предыдущая глава
ConfigMap и Secret: выносим конфигурацию
Следующая глава →
Хранилище: Volumes и PersistentVolumeClaim

Все главы курса «Kubernetes на практике»

Поделиться темой: ✈ Telegram VK

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей