Ingress: HTTP-маршрутизация снаружи
Как пустить десятки HTTP-сервисов через один внешний адрес, маршрутизируя по домену и пути.
Ingress — это объект Kubernetes с правилами HTTP-маршрутизации (хост + путь → Service), который исполняет отдельный компонент — ingress controller.
В базовых разделах мы открывали приложение наружу через Service типа LoadBalancer. Это работает, но плохо масштабируется: на каждый сервис облако поднимает отдельный балансировщик с отдельным внешним IP, за каждый из которых вы платите. Десять микросервисов — десять балансировщиков и десять IP. А ещё LoadBalancer работает на уровне L4 (TCP/порт) и ничего не знает про HTTP: он не умеет смотреть на домен или путь запроса.
Зачем Ingress вместо кучи LoadBalancer
Ingress решает эту проблему: один внешний вход (один балансировщик, один IP) принимает весь HTTP/HTTPS-трафик и раскидывает его по внутренним сервисам по правилам уровня L7. Запрос на shop.example.com уходит во фронтенд, api.example.com/users — в сервис пользователей, api.example.com/orders — в сервис заказов. Снаружи это один адрес, внутри — много сервисов.
Аналогия: LoadBalancer — это отдельная входная дверь в каждую комнату прямо с улицы. Ingress — это одна парадная с консьержем, который по табличке на конверте relays вас в нужный кабинет.
Ingress сам по себе ничего не делает: нужен controller
Ключевой момент, на котором спотыкаются новички: объект Ingress — это только правила, декларация намерения. Их кто-то должен исполнять. Этот «кто-то» — ingress controller: под (обычно Deployment + Service типа LoadBalancer), внутри которого крутится реальный обратный прокси — чаще всего nginx, либо HAProxy, Traefik, Envoy. Controller следит за объектами Ingress в кластере и на лету перенастраивает прокси под их правила.
Поэтому в свежем кластере одного применения YAML с Ingress мало — правила будут лежать мёртвым грузом, пока не установлен controller. В minikube его включают аддоном:
minikube addons enable ingress
kubectl get pods -n ingress-nginx
В облаке ingress-nginx ставят манифестом или Helm-чартом; managed-кластеры часто предлагают свой controller.
Правила хостов и путей
Опишем маршрутизацию: домен api.example.com делим по префиксу пути между двумя сервисами.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: users-svc
port:
number: 80
- path: /orders
pathType: Prefix
backend:
service:
name: orders-svc
port:
number: 80
Поле ingressClassName говорит, какой controller обслуживает это правило (их в кластере может быть несколько). pathType: Prefix матчит всё, что начинается с /users; альтернатива — Exact (точное совпадение). Трафик controller направляет на Service по имени и порту — то есть Ingress стоит «над» сервисами, а не заменяет их.
Несколько объектов Ingress с одним хостом controller объединяет в один server-блок — это удобно, когда команды владеют своими сервисами и описывают правила в отдельных манифестах. Запрос, не подошедший ни под одно правило, уходит на default backend controller'а (обычно отдаёт 404). Полезные поведения за пределами базовой схемы — редиректы, ограничение скорости, размер тела запроса, basic-auth — задают аннотациями на объекте Ingress (например, nginx.ingress.kubernetes.io/...); это «ручки» конкретного controller, и в Traefik или Envoy они называются иначе.
TLS-терминация
Ingress — естественное место, чтобы завершать (терминировать) HTTPS: расшифровать TLS на входе, а внутрь кластера пустить уже обычный HTTP. Сертификат и ключ кладут в Secret типа kubernetes.io/tls, а в Ingress ссылаются на него:
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls-cert
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
Сам Secret создают из файлов сертификата:
kubectl create secret tls api-tls-cert \
--cert=tls.crt --key=tls.key
На практике сертификаты обычно не создают руками: ставят cert-manager, который автоматически выпускает и продлевает их через Let's Encrypt, складывая в нужный Secret. Ingress подхватывает обновлённый сертификат без вашего участия.
Как это работает под капотом
Controller (возьмём ingress-nginx) держит внутри настоящий nginx. Он подписан на API-сервер и наблюдает за объектами Ingress, Service и Endpoints. При любом изменении он генерирует новый nginx.conf с server- и location-блоками под ваши хосты и пути и делает nginx -s reload. Внешний трафик приходит на Service самого controller (тип LoadBalancer или NodePort), попадает в nginx, тот по заголовку Host и пути выбирает upstream и проксирует запрос прямо на поды бэкенда (через их Endpoints, минуя лишний kube-proxy hop). Так один прокси обслуживает сколько угодно правил.
Частые ошибки
Самая частая — применить Ingress в кластере без установленного controller и ждать, что заработает: внешнего IP у объекта так и не появится, а правила бездействуют. Вторая — забыть ingressClassName (или указать класс, которого нет): тогда ни один controller не возьмёт правило в работу. Третья — путаница с pathType: Exact вместо Prefix на /api приведёт к 404 на /api/users. Ещё про переписывание путей: nginx по умолчанию отдаёт бэкенду путь как есть; чтобы срезать префикс, нужна аннотация rewrite-target — её отсутствие или неверный синтаксис ломает маршрут. И помните: Ingress — это только HTTP/HTTPS; не-HTTP трафик (произвольный TCP/UDP, например база данных) через стандартный Ingress не пускают — для него используют Service или специальные расширения controller.
Итоги
- Ingress — правила L7-маршрутизации (хост + путь → Service); один внешний вход вместо балансировщика на каждый сервис.
- Правила исполняет ingress controller (nginx, Traefik, Envoy) — без него
Ingressбездействует. ingressClassNameпривязывает правило к нужному controller; маршрутизация идёт поhostиpath/pathType.- TLS-терминация: сертификат в
Secretтипа tls, ссылка вspec.tls; на практике автоматизируют cert-manager'ом. - Ingress стоит над сервисами и работает только с HTTP/HTTPS.