Ingress: концепция и архитектура L7 routing
В модуле 08 мы разобрали Service — это L4 (TCP/UDP) балансировка. Чтобы выставить приложение наружу, можно создать type: LoadBalancer Service — облако даст внешний IP, и трафик пойдёт в Pods. Но если у вас десять микросервисов, каждый со своим доменом и своим SSL-сертификатом, получится десять LoadBalancer Services, десять внешних IP и десять счетов за cloud LB. И никакого host-based / path-based routing — Service не умеет смотреть в HTTP-заголовки, он работает на уровне портов.
Ingress решает обе проблемы. Это L7 (HTTP/HTTPS) routing объект, который позволяет принять трафик на один LoadBalancer и развести его по нескольким backend Service в зависимости от Host: заголовка и URL path. Плюс TLS-termination, плюс annotations для тонкой настройки.
Но Ingress принципиально устроен иначе, чем большинство объектов Kubernetes. Это и API-объект, и нечто внешнее. Разберём.
Reverse proxy: NGINX, HAProxy, TLS termination, кэширование
Ingress = API + Controller
Когда вы делаете kubectl apply -f ingress.yaml, в etcd ложится объект. И всё. Никакого встроенного “Ingress-демона” в Kubernetes нет. В отличие от Deployment (его watch-ит DeploymentController внутри kube-controller-manager) или Service (его реализует kube-proxy на каждом узле), Ingress по умолчанию никем не обрабатывается.
Чтобы Ingress начал работать, в кластере должен быть установлен Ingress controller — отдельные Pods, которые watch-ят Ingress-объекты и реально проксируют трафик. Это самый частый источник недоразумений у новичков: создали Ingress, ничего не работает, “почему?”. Потому что controller не установлен.
То есть Ingress — это контракт между декларацией и реализацией. Один и тот же YAML Ingress будет работать одинаково (плюс-минус annotations) что на nginx, что на Traefik, что на cloud-провайдере. А кто его реально обрабатывает — отдельный вопрос инфраструктуры.
Минимальный Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Ключевые поля:
apiVersion: networking.k8s.io/v1— обязательно v1, с v1.22 старыеextensions/v1beta1иnetworking.k8s.io/v1beta1удалены полностьюspec.ingressClassName— какой controller этот Ingress обрабатывает (если в кластере их несколько)spec.rules— список правил маршрутизации (host+paths)backend.service.name + port— куда форвардить запрос
Если ingressClassName не указан и в кластере нет default IngressClass — controller, скорее всего, проигнорирует этот Ingress. В старых кластерах (до v1.18) использовалась annotation kubernetes.io/ingress.class, но она deprecated. На свежих кластерах всегда задавайте spec.ingressClassName.
Зачем Ingress существует — три причины
1. Экономия cloud LoadBalancer
Cloud LB (AWS NLB/ALB, GCP TCP/HTTP LB, Azure LB) — это деньги. ~250 в месяц. С Ingress controller один LoadBalancer Service обслуживает controller, а тот по host/path разводит сотни приложений. Один счёт.
2. L7-возможности, которых нет у Service
Service видит TCP-пакеты и порты. Ingress видит HTTP-заголовки. Это даёт:
- Host-based routing:
api.example.com → api-service,www.example.com → web-service— оба на одном IP - Path-based routing:
/api → api-service,/static → cdn-service - TLS termination: certificate в Secret, controller сам делает SSL handshake
- HTTP-tuning: rewrite, redirect, auth, rate limit (через annotations)
3. Централизованная точка для cross-cutting concerns
WAF, rate limiting, mTLS, JWT validation, logs/metrics — всё это удобнее иметь в одном месте (Ingress controller), а не повторять в каждом сервисе.
ingressClassName и IngressClass
В кластере может быть несколько Ingress controllers одновременно. Например, ingress-nginx для внутренних сервисов и AWS ALB Controller для публичных. Чтобы Ingress знал, какой controller его обрабатывает, есть объект IngressClass:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: alb
spec:
controller: ingress.k8s.aws/alb
Каждый controller “владеет” своим значением spec.controller — это просто строка, по которой controller узнаёт “это про меня”. Ingress ссылается на IngressClass через spec.ingressClassName: nginx, и обрабатывает его именно nginx controller.
Default IngressClass
Annotation ingressclass.kubernetes.io/is-default-class: "true" на одной из IngressClass делает её default — Ingress без spec.ingressClassName будет обработан именно этой default IngressClass.
kubectl get ingressclass
# NAME CONTROLLER PARAMETERS AGE
# nginx k8s.io/ingress-nginx <none> 7d
# alb ingress.k8s.aws/alb <none> 7d
Только одна IngressClass должна быть default в кластере. Иначе controller получит ambiguous state, и Kubernetes admission webhook (если включён) выдаст ошибку при создании второго default-объекта.
Архитектура: где живёт controller
Сам controller — это Pods, обычно Deployment или DaemonSet (зависит от controller-а). Их выставляют наружу через Service type: LoadBalancer (в облаке) или type: NodePort (bare-metal без MetalLB).
То есть для пользователя путь такой:
- DNS-запрос
app.example.comотдаёт IP cloud LoadBalancer - Запрос приходит на LB, LB форвардит на NodePort одного из узлов кластера
- kube-proxy на узле проксирует в Pod ingress-nginx controller
- nginx внутри Pod смотрит
Host:и path, находит matching Ingress rule - nginx делает upstream-запрос на ClusterIP backend Service
- kube-proxy балансирует на конечный application Pod
Шесть прыжков, но все они в kernel или в пределах кластера — overhead минимальный.
Какие Ingress controllers существуют
Это не часть Kubernetes core — это экосистема. Каждый со своими сильными и слабыми сторонами:
| Controller | Backend | Кому подходит |
|---|---|---|
| ingress-nginx | nginx | Самый популярный community-проект. Open-source. Богатые annotations. По умолчанию в большинстве туториалов. |
| NGINX Inc. nginx-ingress | nginx | Коммерческий продукт от nginx.com. Похож на ingress-nginx, но РАЗНЫЕ проекты. Часто путают. |
| Traefik | Traefik | Native cloud-friendly, автогенерация TLS через Let’s Encrypt из коробки, удобный dashboard. |
| HAProxy | HAProxy | Высокая производительность, привычный для тех, кто знает HAProxy. |
| Envoy-based (Contour, Emissary) | Envoy | Современный data plane, HTTP/2 + gRPC native, основа Istio/Linkerd. |
| Cloud-managed (AWS ALB, GCE Ingress, Azure App Gateway) | Сами cloud LB | Полностью managed, controller просто создаёт настоящий cloud LB на каждый Ingress. Дороже, но zero-ops. |
ingress-nginx vs nginx-ingress — это два разных проекта. ingress-nginx (kubernetes.io/ingress-nginx) — community, бесплатный, ставится по умолчанию. nginx-ingress (nginx.com) — коммерческий от F5/NGINX Inc. У них разные annotations, разные баги, разные CVE. На собеседовании и в production важно знать, какой именно стоит.
Cloud-managed Ingress: другая модель
Cloud-managed Ingress (AWS ALB Controller, GCE Ingress, Azure Application Gateway Ingress) работают иначе:
- Не запускают свои Pods-проксисов в кластере
- Controller просто читает Ingress-объекты и создаёт настоящий managed cloud LoadBalancer (ALB, GCLB, AGW)
- Cloud LB сам делает HTTP-routing и проксирует прямо в Pods (через target groups)
Плюс: zero-ops, нативная интеграция с WAF, certificate manager, IAM. Минус: каждый Ingress = новый облачный LB = деньги. Часть annotations несовместима между cloud-провайдерами.
Без controller — Ingress не работает
Самая частая ошибка у новичков на CKAD:
kubectl apply -f ingress.yaml
kubectl get ingress
# NAME CLASS HOSTS ADDRESS PORTS AGE
# web nginx app.example.com 80 30s
ADDRESS пустой — потому что controller не установлен в кластере. Объект Ingress лежит в etcd, но никто его не обрабатывает.
Проверка:
# Есть ли IngressClass в кластере?
kubectl get ingressclass
# Есть ли controller pods (обычно в namespace ingress-nginx, traefik, kube-system)?
kubectl get pods -A | grep -i ingress
kubectl get pods -A | grep -i traefik
Если ничего нет — нужно установить controller, обычно через Helm:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace
После установки ADDRESS в Ingress появится — это IP LoadBalancer-а, на котором поднялся controller.
На CKAD-экзамене controller обычно уже установлен в среде — нужно только создать Ingress и проверить, что DNS/curl работают. Но в реальной жизни первый шаг при настройке любого нового кластера — поставить ingress controller.
status.loadBalancer.ingress
После того, как controller обработает Ingress, он записывает обратно в status объекта IP/hostname LB:
kubectl get ingress web -o yaml | yq '.status'
# loadBalancer:
# ingress:
# - hostname: a1b2c3d4-12345.eu-west-1.elb.amazonaws.com # AWS ALB
# - ip: 35.241.123.45 # GCP/Azure
Это значение — то, на что должен указывать DNS-запись app.example.com. Часто это автоматизируется через external-dns controller, который читает Ingress.host и создаёт DNS-записи в Route53/CloudDNS.