Сервис типа LoadBalancer в Kubernetes создаёт отдельный внешний IP на каждое приложение — на VPS без облачного балансировщика это либо вообще не работает, либо требует ручной настройки MetalLB. Ingress решает задачу иначе: один контроллер и один внешний IP обслуживают маршрутизацию по доменам сразу для всех сервисов в кластере.
Что такое Ingress и зачем он в кластере
Ingress — это правило маршрутизации HTTP и HTTPS трафика внутрь кластера по имени домена и по пути в URL, а не отдельный сетевой объект. Само правило ничего не делает без Ingress Controller — программы, которая читает эти правила и настраивает по ним реальный прокси. ingress-nginx — самый распространённый контроллер, построенный поверх обычного nginx.
Без Ingress каждый сервис пришлось бы публиковать через NodePort или отдельный LoadBalancer, что на 10 приложениях в кластере означает 10 внешних адресов и портов вместо одного домена и одного IP с маршрутизацией по поддоменам.
Как работает ingress-nginx
Ingress Controller и Ingress-ресурс
Controller — это под, который слушает внешний трафик на портах 80 и 443, читает объекты типа Ingress через Kubernetes API и на лету перегенерирует конфиг nginx внутри себя. Ingress-ресурс — это YAML-манифест с правилами: какой домен на какой Service и порт отправлять запрос.
Маршрутизация по домену и по пути
Один Ingress-объект может описывать сразу несколько правил: app.example.com ведёт на один Service, api.example.com — на другой, а /admin внутри app.example.com — на третий. Контроллер сортирует правила по специфичности сам, вручную порядок задавать не нужно.
| Способ публикации | Внешних IP | Маршрутизация по домену |
|---|---|---|
| NodePort | По одному на узел | Нет |
| LoadBalancer | Один на сервис | Нет |
| Ingress | Один на весь кластер | Да |
Установка ingress-nginx через Helm
Официальный чарт ставит контроллер, RBAC-роли и Service типа LoadBalancer или NodePort, в зависимости от окружения.
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
На VPS без облачного балансировщика Service стоит переключить на hostNetwork или NodePort и направить домен напрямую на IP узла — MetalLB нужен только когда важно единое виртуальное IP на несколько узлов.
Ingress-ресурс с TLS через cert-manager
cert-manager автоматически запрашивает и продлевает сертификаты, а Ingress-ресурс просто указывает, для какого домена и в какой Secret его положить.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts: ["app.example.com"]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
Автоматический выпуск сертификатов Let's Encrypt
ClusterIssuer описывает, каким способом cert-manager подтверждает владение доменом — через HTTP-01 challenge, который проходит прямо через уже настроенный Ingress.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
Если кластер развёрнут через k3s, ingress-nginx часто уже входит в поставку по умолчанию — Traefik, который ставится с k3s "из коробки", можно заменить на ingress-nginx или оставить, смотря какие функции нужны. Отдельные VPS под ноды кластера удобно поднимать через Terraform, описав их конфигурацию как код заранее.
Чек-лист и типичные ошибки
- Аннотация cert-manager.io/cluster-issuer указана точно так же, как имя ClusterIssuer, опечатка не даёт никакой ошибки, просто сертификат не выпускается.
- DNS-запись домена уже указывает на внешний IP Ingress Controller до запроса сертификата — HTTP-01 challenge иначе не пройдёт проверку.
- pathType указан явно (Prefix или Exact) — без него старые версии Kubernetes используют устаревшее поведение маршрутизации.
- Secret с TLS-сертификатом лежит в том же namespace, что и Ingress-ресурс, а не в namespace контроллера.