Ingress controller: установка, архитектура, операционка
В lessons 1-2 мы разобрали Ingress как API: объект, rules, TLS. Теперь — про controller, который этот API реализует. Это критически важный кусок, потому что Ingress без controller — мёртвый объект в etcd. И когда что-то ломается в production, чаще всего проблема не в Ingress YAML, а в том, как настроен controller, его Service и сетевая инфраструктура вокруг.
Разберём ingress-nginx как наиболее популярный пример, потом — отличия cloud-managed controllers, потом — bare-metal сценарии с MetalLB.
Приватные registry: GHCR, ECR, GAR, Harbor
ingress-nginx через Helm
Стандартная установка ingress-nginx в любой кластер:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2 \
--set controller.service.type=LoadBalancer
Что устанавливается:
kubectl get all -n ingress-nginx
# NAME READY STATUS RESTARTS AGE
# pod/ingress-nginx-controller-7c4f5d8b9-abc12 1/1 Running 0 1m
# pod/ingress-nginx-controller-7c4f5d8b9-def34 1/1 Running 0 1m
#
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# service/ingress-nginx-controller LoadBalancer 10.96.123.45 1.2.3.4 80:30080/TCP,443:30443/TCP
# service/ingress-nginx-controller-admission ClusterIP 10.96.123.46 <none> 443/TCP
#
# NAME READY UP-TO-DATE AVAILABLE
# deployment.apps/ingress-nginx-controller 2/2 2 2
#
# NAME DESIRED CURRENT READY
# replicaset.apps/ingress-nginx-controller-7c4f5d8b9 2 2 2
Ключевые объекты:
- Deployment
ingress-nginx-controllerс 2 репликами — это nginx Pods + controller (Go-процесс, который watch-ит Ingress) - Service
ingress-nginx-controllerтипаLoadBalancer— публичная точка входа - Service
ingress-nginx-controller-admissionтипаClusterIP— ValidatingWebhook для валидации Ingress на creation/update - IngressClass
nginxсspec.controller: k8s.io/ingress-nginx
Внутри Pod: controller + nginx
Один Pod ingress-nginx-controller запускает два процесса:
nginx reload — это relatively cheap, но не free. Каждый reload означает spin-up новых worker-процессов и graceful shutdown старых. В кластере с тысячами Ingress и частыми изменениями reload-ы могут становиться bottleneck-ом. Поэтому ingress-nginx через Lua обновляет только endpoints без reload — это самая частая операция (масштабирование backend Pods).
Один LoadBalancer на всё
Самое важное в Ingress controller — один Service type: LoadBalancer обслуживает все приложения в кластере.
Сравните с альтернативой — Service type: LoadBalancer для каждого приложения: 10 приложений = 10 LB = 10 счетов. С Ingress controller — 1 LB на всё.
Service type: LoadBalancer vs NodePort
В облаке Service type: LoadBalancer создаёт настоящий cloud LB (через cloud-controller-manager) и записывает его IP в status.loadBalancer. Так работает в AWS, GCP, Azure, DigitalOcean, OVH.
На bare-metal (kubeadm cluster без cloud-провайдера) Service LoadBalancer зависает в <pending> — некому выделить IP. Два варианта:
Вариант 1: NodePort
spec:
service:
type: NodePort
nodePorts:
http: 30080
https: 30443
Controller-Service доступен на <любой-узел>:30080 и <любой-узел>:30443. Дальше внешний DNS указывает на IP узлов, или ставят hardware LB перед узлами вручную.
Минус: порты 30000-32767, не 80/443. Клиенты должны знать порт. Решается hostPort или внешним LB.
Вариант 2: MetalLB
MetalLB — controller для bare-metal, который эмулирует cloud LoadBalancer. Берёт диапазон IP (которыми вы располагаете в своей сети), и выделяет их Service-ам type: LoadBalancer.
# MetalLB IPAddressPool
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: production
namespace: metallb-system
spec:
addresses:
- 192.168.1.240-192.168.1.250
Два режима:
- Layer 2 (ARP) — один узел отвечает на ARP-запросы для VIP, при failover другой узел берёт ARP. Простой, но active/passive.
- BGP — MetalLB говорит BGP с router-ом сети, анонсирует VIP. Active/active, нужна BGP-инфраструктура.
С MetalLB Service type: LoadBalancer получает реальный IP, и ingress-nginx работает как в облаке.
MetalLB — стандартный выбор для on-prem кластеров и homelab. Если у вас kubeadm на baremetal или Minikube/kind, и нужно выставить ingress-nginx наружу — ставьте MetalLB. Без него LoadBalancer Service навечно в Pending.
DaemonSet vs Deployment
Controller можно поднять как Deployment (фиксированное число реплик) или DaemonSet (по одной на каждый узел).
Deployment (default)
spec:
controller:
replicaCount: 3
kind: Deployment
3 реплики, Kubernetes сам решает на каких узлах их запустить. За Service LoadBalancer трафик балансируется на эти Pods.
Плюс: чёткий контроль числа реплик, не зависит от размера кластера. Минус: лишний прыжок kube-proxy → controller-Pod на другом узле.
DaemonSet + hostNetwork
spec:
controller:
kind: DaemonSet
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
Один controller-Pod на каждом узле, использует hostNetwork: true — слушает прямо на портах узла (80/443).
Плюс: минимальный overhead (нет kube-proxy hop). Хорошо для bare-metal с external LB. Минус: занимает 80/443 на каждом узле — больше ничего на них нельзя поставить. Привязка к топологии узлов.
В production обычно используют Deployment + Service LoadBalancer. DaemonSet + hostNetwork — для bare-metal с внешним LB (F5 BIG-IP, hardware appliance).
ingress-nginx vs nginx-ingress: РАЗНЫЕ проекты
Это путаница, на которой ловят даже senior-инженеров.
| Поле | ingress-nginx | nginx-ingress |
|---|---|---|
| Owner | Kubernetes community (CNCF) | F5 / NGINX Inc. |
| Repo | kubernetes/ingress-nginx | nginxinc/kubernetes-ingress |
| License | Apache 2.0 | Apache 2.0 (Open Source) + Commercial (NGINX Plus) |
| Annotations prefix | nginx.ingress.kubernetes.io/ | nginx.org/ |
| controller string | k8s.io/ingress-nginx | nginx.org/ingress-controller |
| Helm chart | ingress-nginx/ingress-nginx | nginx-stable/nginx-ingress |
Различия в фичах:
- ingress-nginx — Lua-based dynamic config, OpenResty
- nginx-ingress — native nginx config, более чистая интеграция с nginx Plus
В большинстве туториалов, докладов с KubeCon, на CKAD-экзамене — речь про ingress-nginx (community). nginx-ingress встречается реже и в основном в крупных enterprise с лицензиями NGINX Plus.
Если в одном кластере поставить оба — у каждого свои annotations и своя IngressClass, они не будут конфликтовать. Но операционно мучительно: разработчики путаются в annotations, документация для одного не применима к другому. Выбирайте один и придерживайтесь.
Cloud-managed controllers
Альтернатива — controllers, которые не запускают свои nginx Pods, а конфигурируют native cloud LoadBalancer:
AWS Load Balancer Controller
helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
--set clusterName=my-cluster \
--namespace kube-system
Каждый Ingress становится настоящим AWS ALB:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules: [ ... ]
Плюсы:
- Нет nginx Pods в кластере (меньше ресурсов)
- Нативная интеграция с AWS WAF, ACM, Cognito (auth), Shield
- HTTP/2, gRPC support out of the box
- Health checks делает ALB сам
Минусы:
- Каждый Ingress = свой ALB (~$22/мес минимум за каждый). Можно использовать IngressGroup для объединения, но это требует annotations.
- Vendor lock-in на AWS
- Annotations несовместимы ни с ingress-nginx, ни с другими cloud-managed
GCE Ingress
В GKE есть built-in ingressClassName: gce, который создаёт Google Cloud Load Balancer (GCLB) — managed L7 LB с anycast IP и global presence.
Azure Application Gateway Ingress Controller (AGIC)
Аналогично — Ingress → Azure Application Gateway.
Полный путь трафика
Соберём в единую картину путь HTTP-запроса от пользователя до Pod:
Шесть hops, но они все либо в kernel (iptables), либо в пределах кластера. Latency overhead на правильно настроенном кластере — единицы миллисекунд.
DNS и external-dns
DNS — отдельный кусок. Имена app.example.com должны где-то резолвиться в IP LoadBalancer. Варианты:
- Ручные DNS-записи — простой A/CNAME в Route53/Cloudflare. Подходит для статичных адресов.
- external-dns controller — отдельный controller, который watch-ит Ingress и Service
LoadBalancer, и автоматически создаёт DNS-записи в провайдере (Route53, CloudDNS, Cloudflare, etc):
helm install external-dns external-dns/external-dns \
--set provider=aws \
--set txtOwnerId=my-cluster \
--set domainFilters[0]=example.com
С external-dns создал Ingress с host: new-app.example.com — DNS-запись появляется автоматически.
Несколько controllers в одном кластере
Можно поднять два разных Ingress controller-а в одном кластере — для разных задач:
# Public-facing — AWS ALB
helm install alb-ingress eks/aws-load-balancer-controller \
--set ingressClass=alb-public
# Internal-only — ingress-nginx с internal LB
helm install nginx-internal ingress-nginx/ingress-nginx \
--set controller.ingressClassResource.name=nginx-internal \
--set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-internal"=true
Тогда Ingress spec.ingressClassName: alb-public создаст public ALB, а spec.ingressClassName: nginx-internal пойдёт через internal nginx с internal NLB.
Это типичный enterprise-паттерн: разделение public и internal трафика на разные controllers с разными политиками безопасности.
Что смотреть когда не работает
Чек-лист для troubleshooting “Ingress не маршрутизирует”:
# 1. Controller установлен?
kubectl get pods -n ingress-nginx
kubectl get ingressclass
# 2. Controller получил external IP?
kubectl get svc -n ingress-nginx
# Если EXTERNAL-IP <pending> на bare-metal — нужен MetalLB
# 3. Ingress матчится с правильной IngressClass?
kubectl get ingress -A
# Колонка CLASS должна быть не <none>
# 4. Controller увидел Ingress?
kubectl describe ingress web
# Смотрим Events: должны быть Normal Sync от controller
# 5. Backend Service существует и имеет Endpoints?
kubectl get svc web
kubectl get endpoints web
# Endpoints должны быть не пустыми — иначе controller получит 503
# 6. DNS правильно резолвится в IP LB?
dig +short app.example.com
nslookup app.example.com
# 7. Логи самого controller-а
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=200
# Тут видно ошибки парсинга Ingress, ошибки connection к backend
90% проблем — на шагах 1-2 (controller не установлен или Service в Pending) и 5 (Service без Endpoints).