Learning Platform
Глоссарий Troubleshooting
Урок 10.03 · 22 мин
Продвинутый
ingress-nginxHelmMetalLBAWS ALBGCE IngressDaemonSetLoadBalancer Service

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 запускает два процесса:

Архитектура одного controller Pod
Pod: ingress-nginx-controllerОдин Pod содержит и controller-процесс (Go), и nginx (C). Они общаются через файловую систему и сигналы. PID 1 — controller, он запускает nginx как child process и пишет ему конфиги.
Controller process (Go)Главный процесс. Watch-ит Ingress, Service, Endpoints, Secret через Kubernetes API. На каждое изменение пересчитывает nginx.conf и upstream-конфигурацию, пишет файлы, шлёт сигнал nginx-у на reload (SIGHUP).
nginx processСам nginx. Слушает 80/443 (контейнер expose-ит эти порты). Worker processes обрабатывают HTTP-запросы. При SIGHUP master-процесс плавно reload-ит конфиг: новые workers стартуют со свежим конфигом, старые завершают активные коннекты.
reload via SIGHUP
Lua dynamic configingress-nginx использует OpenResty (nginx + Lua). Endpoints (IP Pods из backend Service) обновляются ДИНАМИЧЕСКИ через Lua API без reload — только при изменении Ingress/Service spec идёт полный reload.
NOTE

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 обслуживает все приложения в кластере.

Один cloud LB на десятки Ingress
Cloud LB (AWS NLB / GCP LB)Единственный cloud LoadBalancer на весь кластер. Создан Service type: LoadBalancer перед ingress-nginx. Один publicIP. Один счёт от cloud-провайдера. Все Ingress используют его.
ingress-nginx Pods2-3 Pods controller-а за LB. Все Ingress в кластере routed через эти Pods на основе host/path. Pods могут быть Deployment с replicaCount или DaemonSet (по одной реплике на узел).
Ingress 'web' app1.example.comМаршрутизация для app1 — controller матчит host app1.example.com → backend web-service.
Ingress 'api' api.example.comМаршрутизация для api — тот же controller, тот же LB, та же IP. Cost не растёт.
Ingress 'shop' shop.example.comТретий Ingress на том же controller. Можно ставить десятки и сотни — LoadBalancer и controller-Pods переиспользуются.

Сравните с альтернативой — 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 работает как в облаке.

TIP

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-nginxnginx-ingress
OwnerKubernetes community (CNCF)F5 / NGINX Inc.
Repokubernetes/ingress-nginxnginxinc/kubernetes-ingress
LicenseApache 2.0Apache 2.0 (Open Source) + Commercial (NGINX Plus)
Annotations prefixnginx.ingress.kubernetes.io/nginx.org/
controller stringk8s.io/ingress-nginxnginx.org/ingress-controller
Helm chartingress-nginx/ingress-nginxnginx-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.

WARNING

Если в одном кластере поставить оба — у каждого свои 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.

ingress-nginx vs AWS ALB Controller
Модель 1: ingress-nginxIn-cluster reverse proxy. Один cloud LB (Network LB у AWS) перед nginx Pods. Nginx делает HTTP routing внутри. Дешевле, портативнее, но нужна экспертиза в nginx tuning. Все Ingress используют один cloud LB.
vs
Модель 2: AWS ALB ControllerCloud-managed routing. На каждый Ingress (или IngressGroup) создаётся настоящий AWS ALB через cloud API. Нет nginx Pods. ALB делает routing сам и проксирует прямо в Pods через VPC CNI target groups. Дороже, но zero-ops, нативная интеграция с AWS-сервисами.

Полный путь трафика

Соберём в единую картину путь HTTP-запроса от пользователя до Pod:

End-to-end: внешний клиент → Pod backend
1. ClientБраузер делает HTTPS-запрос к app.example.com. DNS-resolve возвращает IP cloud LoadBalancer.
DNS → IP LB
2. Cloud LoadBalancerCloud LB (AWS NLB / GCP TCP LB) принимает TCP-коннект на 443. Балансирует на один из узлов кластера на NodePort (30443) того Service, который относится к ingress-nginx.
node NodePort
3. kube-proxy на узлеkube-proxy через iptables / IPVS правила перенаправляет на ClusterIP Service ingress-nginx-controller, а оттуда — на один из его Endpoints (IP конкретного nginx Pod).
TLS termination
4. ingress-nginx Pod (nginx)nginx терминирует TLS (использует Secret app-tls), читает HTTP-заголовки, матчит Host:app.example.com и path /api в свой Ingress rule. Находит backend Service api-service:80.
upstream HTTP
5. kube-proxy для backend Servicenginx делает HTTP-запрос на ClusterIP api-service:80. kube-proxy через iptables перенаправляет на endpoint (IP Pod) api-service. Это уже plain HTTP внутри кластера.
6. Application PodФинальный application Pod получает запрос на свой port, обрабатывает, отдаёт response. Response идёт обратно по тому же пути: Pod → nginx → kube-proxy → cloud LB → клиент.

Шесть hops, но они все либо в kernel (iptables), либо в пределах кластера. Latency overhead на правильно настроенном кластере — единицы миллисекунд.


DNS и external-dns

DNS — отдельный кусок. Имена app.example.com должны где-то резолвиться в IP LoadBalancer. Варианты:

  1. Ручные DNS-записи — простой A/CNAME в Route53/Cloudflare. Подходит для статичных адресов.
  2. 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).


Проверка знанийKnowledge check
В bare-metal kubeadm кластере (без cloud-провайдера) вы установили ingress-nginx через Helm с дефолтными настройками. `kubectl get svc -n ingress-nginx` показывает `EXTERNAL-IP <pending>`, Ingress объекты создаются, но недоступны снаружи. Какие три способа решить это и в чём trade-off каждого?
ОтветAnswer
1) **MetalLB**: установить MetalLB, дать ему диапазон IP вашей сети, Service type=LoadBalancer получит реальный IP, и всё заработает как в облаке. Trade-off: нужна дополнительная инфраструктура (MetalLB Pods), и в Layer 2 режиме failover активный/пассивный с задержкой ARP. Подходит для homelab и небольших on-prem. 2) **Service type=NodePort**: переключить controller Service на NodePort, ingress-nginx будет доступен на каждом узле на port 30000-32767. Trade-off: непривычные порты (не 80/443), клиенты должны знать порт, либо нужен внешний hardware LB перед узлами. Подходит когда у вас уже есть внешний LB (F5, HAProxy на отдельной машине). 3) **DaemonSet + hostNetwork**: настроить controller как DaemonSet с hostNetwork: true, nginx слушает прямо на портах узлов (80/443). Trade-off: занимает 80/443 на каждом узле (ничто другое их не получит), привязка к узлам (если узел падает — клиенту нужен retry на другой IP). Подходит когда узлов мало и они стабильны. На production-уровне MetalLB — самый чистый путь.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. В bare-metal кластере (kubeadm без cloud-провайдера) установлен ingress-nginx. `kubectl get svc -n ingress-nginx` показывает `EXTERNAL-IP <pending>` навсегда. Почему?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5