Learning Platform
Глоссарий Troubleshooting
Урок 10.01 · 22 мин
Средний
IngressIngressClassL7Ingress controlleringress-nginx

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: декларация vs реализация
kubectl apply ingress.yamlПользователь создаёт API-объект Ingress. apiVersion: networking.k8s.io/v1. Объект ложится в etcd через kube-apiserver. На этом этапе ничего ещё не маршрутизируется — это просто декларация желаемого состояния.
watch
Ingress Controller PodsОтдельные Pods (обычно nginx, Traefik или Envoy), которые через Kubernetes API watch-ят все Ingress-объекты. При создании или изменении Ingress controller перегенерирует свою конфигурацию (nginx.conf, или Envoy xDS) и применяет её.
reverse proxy
Backend ServicesController проксирует HTTP-запросы в backend Service по матчам host/path из Ingress rules. Service дальше балансирует через kube-proxy в Pods.

То есть 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 — куда форвардить запрос
WARNING

Если 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) — это деньги. ~1525/месяцзакаждый.Длядесятисервисов—15-25/месяц за каждый. Для десяти сервисов — 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.

Множественные IngressClass в одном кластере
IngressClass nginxОбъект IngressClass с spec.controller = k8s.io/ingress-nginx. Маркирует, что эту IngressClass обрабатывает Pod-ы ingress-nginx controller (видят свой controller string и берут в работу Ingress-ы с ingressClassName: nginx).
IngressClass albПараллельная IngressClass с spec.controller = ingress.k8s.aws/alb. Обрабатывается отдельным контроллером — AWS Load Balancer Controller, который для каждого такого Ingress поднимает реальный AWS Application Load Balancer.
ingressClassName: ...
Ingress (internal)Ingress с spec.ingressClassName: nginx. Обрабатывается только nginx controller, AWS ALB Controller его игнорирует — он не его IngressClass.
Ingress (public)Ingress с spec.ingressClassName: alb. AWS ALB Controller увидит и поднимет настоящий AWS ALB. 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
NOTE

Только одна IngressClass должна быть default в кластере. Иначе controller получит ambiguous state, и Kubernetes admission webhook (если включён) выдаст ошибку при создании второго default-объекта.


Архитектура: где живёт controller

Сам controller — это Pods, обычно Deployment или DaemonSet (зависит от controller-а). Их выставляют наружу через Service type: LoadBalancer (в облаке) или type: NodePort (bare-metal без MetalLB).

Архитектура ingress-nginx в облаке
External DNSВнешний DNS-сервер (Route53, Cloudflare, корпоративный). Записи app.example.com → IP cloud LoadBalancer. Один A/CNAME запись для всех приложений за этим LB.
HTTPS
Cloud LoadBalancerCloud LB (AWS NLB, GCP TCP LB) — один для всего кластера. Создан Service type: LoadBalancer перед ingress-nginx Pods. Реальная плата за этот объект, поэтому один LB обслуживает десятки/сотни Ingress.
kube-proxy → ingress-nginx Pod
ingress-nginx DeploymentDeployment с 2-3 репликами nginx. Каждый Pod держит nginx-процесс, который слушает 80/443. Внутри Pod-а — controller (Go-процесс), который watch-ит Ingress-объекты и перегенерирует nginx.conf.
match Host + Path → upstream
Backend Service AClusterIP Service для api-service. nginx делает upstream-запрос в clusterIP:port этого Service, kube-proxy балансирует в Pods.
Backend Service BClusterIP Service для web-service. Параллельная маршрутизация: тот же nginx может разводить запросы в разные Services по host/path.

То есть для пользователя путь такой:

  1. DNS-запрос app.example.com отдаёт IP cloud LoadBalancer
  2. Запрос приходит на LB, LB форвардит на NodePort одного из узлов кластера
  3. kube-proxy на узле проксирует в Pod ingress-nginx controller
  4. nginx внутри Pod смотрит Host: и path, находит matching Ingress rule
  5. nginx делает upstream-запрос на ClusterIP backend Service
  6. kube-proxy балансирует на конечный application Pod

Шесть прыжков, но все они в kernel или в пределах кластера — overhead минимальный.


Какие Ingress controllers существуют

Это не часть Kubernetes core — это экосистема. Каждый со своими сильными и слабыми сторонами:

ControllerBackendКому подходит
ingress-nginxnginxСамый популярный community-проект. Open-source. Богатые annotations. По умолчанию в большинстве туториалов.
NGINX Inc. nginx-ingressnginxКоммерческий продукт от nginx.com. Похож на ingress-nginx, но РАЗНЫЕ проекты. Часто путают.
TraefikTraefikNative cloud-friendly, автогенерация TLS через Let’s Encrypt из коробки, удобный dashboard.
HAProxyHAProxyВысокая производительность, привычный для тех, кто знает 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.
WARNING

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-провайдерами.

ingress-nginx vs cloud-managed ALB
ingress-nginx — модель прокси-в-кластереController (nginx Pods) физически прокcирует трафик внутри кластера. Один LoadBalancer Service на все Ingress объекты, проксирование на L7 происходит в nginx Pod-ах. Cheap и portable.
vs
AWS ALB Controller — модель cloud-managedController ничего не проксирует. Он лишь читает Ingress объекты и через AWS API создаёт настоящий ALB с listener rules, target groups (указывающими на Pods через VPC CNI или Instance Mode). Трафик идёт ALB → Pods напрямую, controller только конфигурирует.

Без 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.

TIP

На 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.


Проверка знанийKnowledge check
Вы создали Ingress с правильным YAML, всё валидируется, `kubectl get ingress` показывает объект, но `ADDRESS` колонка пустая, и curl на хост не работает. Какие три причины наиболее вероятны и как диагностировать?
ОтветAnswer
Причина 1: в кластере не установлен Ingress controller. Диагностика: `kubectl get ingressclass` — пусто или нет IngressClass с нужным `spec.controller`. `kubectl get pods -A | grep ingress` — нет controller-Pods. Решение: поставить controller (helm install ingress-nginx). Причина 2: `spec.ingressClassName` не указан и в кластере нет default IngressClass. Controller игнорирует Ingress без явной class или default-метки. Решение: добавить `ingressClassName: nginx` или пометить IngressClass annotation `ingressclass.kubernetes.io/is-default-class: 'true'`. Причина 3: controller установлен, но его LoadBalancer Service в Pending — на bare-metal без MetalLB или без cloud-provider Service type=LoadBalancer никогда не получит external IP. Решение: проверить `kubectl get svc -n ingress-nginx`, поставить MetalLB либо переключить на NodePort и завести внешний LB вручную.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Вы создали Ingress объект через `kubectl apply -f ingress.yaml`. `kubectl get ingress` показывает объект, но колонка ADDRESS пустая, и curl на host не работает. Самая частая причина?

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

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

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

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