Ingress Controller - это точка входа для всего внешнего HTTP/HTTPS трафика в кластер Kubernetes. Он принимает запросы от клиентов и направляет их к нужным сервисам на основе правил, которые вы определяете в YAML-манифестах. В отличие от Service типа LoadBalancer, который работает на уровне L4 и оперирует IP-адресами и портами, Ingress анализирует содержимое запроса на уровне L7: заголовки, путь, имя хоста. Это даёт вам тонкий контроль над маршрутизацией без необходимости плодить десятки облачных балансировщиков.
Правильно настроенный Ingress решает сразу несколько задач SRE: обеспечивает TLS-терминацию для безопасного подключения, разделяет трафик по окружениям staging и production, реализует canary-развертывания с контролируемым весом трафика. В этой статье разберём архитектуру маршрутизации на примере Nginx Ingress Controller и Traefik, создадим гибкие правила на основе хостов, URL-путей и HTTP-заголовков, настроим мониторинг метрик и логирование для отслеживания каждого запроса. Все примеры проверены в продакшене и готовы к использованию.
Если вам нужны основы работы с Service и Ingress, начните с практического руководства по настройке внутреннего и внешнего доступа в Kubernetes. Там детально разобраны типы Service, селекторы и базовые правила Ingress с готовыми манифестами.
Архитектура маршрутизации в Kubernetes Ingress
Ingress состоит из двух компонентов: ресурса Ingress, который описывает правила маршрутизации в декларативном виде, и Ingress Controller - под-а, который эти правила исполняет. Контроллер постоянно отслеживает изменения в кластере через API-сервер и динамически обновляет свою конфигурацию. Когда вы применяете новый манифест Ingress, контроллер перечитывает правила и перезагружает внутренний прокси-сервер без прерывания трафика.
Сетевая цепочка выглядит так: внешний запрос приходит на внешний балансировщик (облачный или MetalLB), тот направляет его на NodePort или напрямую на Pod Ingress Controller. Контроллер анализирует HTTP-заголовки, сопоставляет запрос с правилами Ingress и выбирает Service, на который нужно отправить трафик. Service, в свою очередь, балансирует запросы между Pod'ами приложения. Эта архитектура позволяет менять правила маршрутизации без изменения кода приложений и без пересоздания облачных ресурсов.
Как Ingress Controller обрабатывает HTTP/HTTPS трафик
Жизненный цикл запроса внутри контроллера состоит из нескольких этапов. Сначала контроллер принимает TCP-соединение и, если настроен TLS, выполняет его терминацию - расшифровывает трафик и передаёт его дальше в открытом виде внутри кластера. Затем парсит HTTP-заголовки: извлекает значение Host, URI и любые кастомные заголовки, которые вы указали в правилах.
На этапе сопоставления контроллер последовательно проверяет правила Ingress. Порядок проверки: сначала точное совпадение хоста, затем префикс пути, затем заголовки. Если правило найдено, контроллер выбирает backend - имя Service и порт, указанные в манифесте. Далее выполняется балансировка: запрос направляется на один из Pod'ов, IP-адреса которых контроллер получает через Endpoints API. При изменении количества реплик или пересоздании Pod'ов список адресов обновляется автоматически.
Важный момент: Ingress Controller кэширует резолвинг DNS-имён Service локально. Если Service пересоздаётся с новым ClusterIP, контроллер может несколько секунд отправлять трафик на старый адрес. Для продакшена рекомендуется настраивать health checks на уровне Service и использовать readiness probes в Pod'ах.
Сравнение Nginx и Traefik: что выбрать для продакшена
Nginx Ingress Controller и Traefik - два самых распространённых решения. Выбор зависит от ваших требований к производительности, гибкости конфигурации и интеграции с экосистемой мониторинга. Приведу объективное сравнение на основе практического опыта эксплуатации обоих контроллеров в продакшен-кластерах.
Nginx Ingress Controller построен на проверенном годами ядре nginx. Он показывает стабильную производительность под высокой нагрузкой: на кластере с 50+ сервисами и 10 000 запросов в секунду потребление CPU держится в пределах 0.5-1 ядра. Конфигурация задаётся через аннотации в манифестах Ingress, что даёт детальный контроль над поведением: можно настроить кастомные ошибки, редиректы, rate limiting, аутентификацию. Минус - сложность отладки: аннотации не валидируются на уровне API, и опечатка в названии аннотации приведёт к тому, что правило просто не применится без каких-либо ошибок.
Traefik спроектирован как cloud-native решение с нативной поддержкой динамической конфигурации. Он автоматически обнаруживает сервисы через Kubernetes API, не требуя отдельного ресурса Ingress для каждого сервиса - можно использовать аннотации на Service или Traefik CRD. Поддержка HTTP/2 и gRPC работает из коробки, без дополнительных аннотаций. Встроенная панель мониторинга показывает текущие маршруты и статус бэкендов. Производительность Traefik сопоставима с Nginx на средних нагрузках, но при экстремальных значениях (свыше 50 000 запросов в секунду) Nginx потребляет меньше памяти. Детальное сравнение производительности и конфигурации для разных сценариев использования смотрите в статье Nginx vs HAProxy vs Traefik: практический выбор для маршрутизации и балансировки нагрузки.
Для большинства продакшен-сценариев я рекомендую Nginx Ingress Controller, если вам нужна максимальная производительность и тонкая настройка через аннотации. Traefik выбирайте, когда важна скорость развёртывания, автоматическое обнаружение сервисов и встроенная наблюдаемость.
Создание правил маршрутизации: хосты, пути и заголовки
Правила маршрутизации в Ingress описываются в секции spec.rules. Каждое правило может содержать хост, список путей и backend для каждого пути. Порядок правил в манифесте не влияет на приоритет - контроллер сам определяет наиболее специфичное совпадение. Самое длинное совпадение пути всегда побеждает: правило с путём /api/v2/users имеет приоритет над /api/v2, а то - над /api. Если ни одно правило не подошло, контроллер возвращает 404.
Рассмотрим три основных типа маршрутизации и практические примеры для каждого.
Маршрутизация по имени хоста: разделение сервисов по доменам
Это базовый сценарий: у вас есть несколько доменных имён, и каждое должно вести на свой сервис. Например, api.example.com обрабатывает REST API, а web.example.com отдаёт фронтенд. Ingress Controller при получении запроса смотрит на заголовок Host и выбирает соответствующий серверный блок.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 3000
Если запрос приходит с заголовком Host: api.example.com, он уходит на api-service. Если Host не совпадает ни с одним из указанных, контроллер возвращает дефолтный бэкенд (если он настроен) или 404. Этот подход удобен для разделения окружений: staging.example.com и production.example.com могут указывать на разные Service в одном кластере.
Маршрутизация по URL-пути: префиксы и точные совпадения
Когда все сервисы живут под одним доменом, трафик разделяется по пути запроса. Параметр pathType определяет, как контроллер сопоставляет путь: Prefix - совпадение по префиксу, Exact - точное совпадение. На практике Prefix используется чаще всего.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-based-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
- path: /static
pathType: Prefix
backend:
service:
name: cdn-service
port:
number: 80
Аннотация rewrite-target меняет путь, который видит backend. Без неё запрос на /api/users придёт в Pod с путём /api/users. С rewrite-target: / запрос придёт как /users. Это критически важно, когда ваше приложение не знает о префиксе /api и ожидает запросы в корень.
Типичная ошибка - создание двух правил с одинаковым префиксом, но разными бэкендами. Контроллер выберет то, которое имеет более длинное совпадение, но поведение может быть неочевидным. Всегда проверяйте конфигурацию в staging-окружении перед раскаткой в production.
Маршрутизация по HTTP-заголовкам: канареечные релизы и A/B тестирование
Продвинутый метод маршрутизации - направлять запросы на разные версии сервиса в зависимости от значений HTTP-заголовков. Это основа для A/B-тестирования и канареечных релизов. Nginx Ingress Controller поддерживает такую маршрутизацию через специальные аннотации canary.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-canary-header
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-canary
port:
number: 8080
Эта конфигурация направляет запросы на app-canary только если в запросе присутствует заголовок X-Canary: true. Все остальные запросы идут на основной сервис, описанный в отдельном Ingress без аннотаций canary. Внутренние тестировщики могут добавить этот заголовок в браузере или через прокси и проверять новую версию, не затрагивая остальных пользователей.
Настройка TLS-терминации для безопасного трафика
TLS-терминация на Ingress означает, что HTTPS-соединение заканчивается на контроллере, а дальше внутри кластера трафик идёт по HTTP. Это разгружает ваши приложения от операций шифрования и централизует управление сертификатами. Контроллер берёт на себя все криптографические операции, а вы управляете сертификатами через стандартные ресурсы Kubernetes.
Создание и подключение TLS-сертификата в Kubernetes
Сертификат и приватный ключ хранятся в Secret типа kubernetes.io/tls. Создайте Secret из существующих файлов или сгенерируйте самоподписанный сертификат для тестирования.
kubectl create secret tls example-tls \
--cert=path/to/tls.crt \
--key=path/to/tls.key \
-n default
Затем подключите Secret в манифесте Ingress в секции tls. Один Ingress может обслуживать несколько доменов с разными сертификатами.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
tls:
- hosts:
- api.example.com
secretName: example-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
После применения манифеста контроллер начнёт принимать HTTPS-запросы на порту 443. HTTP-запросы на порту 80 по умолчанию продолжают работать. Чтобы автоматически перенаправлять HTTP на HTTPS, добавьте аннотацию nginx.ingress.kubernetes.io/ssl-redirect: "true".
Автоматизация сертификатов с cert-manager
Ручное обновление сертификатов каждые 90 дней - это операционный ад. cert-manager автоматизирует выпуск и обновление сертификатов от Let's Encrypt или других ACME-провайдеров. Установите cert-manager через Helm, создайте ClusterIssuer для Let's Encrypt и добавьте аннотацию в 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
solvers:
- http01:
ingress:
class: nginx
В манифесте Ingress достаточно указать аннотацию cert-manager.io/cluster-issuer: "letsencrypt-prod" и добавить хост в секцию tls. cert-manager автоматически создаст Secret с сертификатом и будет обновлять его за 30 дней до истечения срока действия. Это решение работает в продакшене без ручного вмешательства.
Разделение трафика по окружениям: staging и production
Один Ingress Controller может обслуживать несколько окружений, изолируя трафик staging от production. Это снижает затраты на инфраструктуру: не нужно поднимать отдельный контроллер для каждого окружения. Два основных подхода - разделение по хостам и разделение по путям.
Разделение по хостам - самый безопасный вариант. Staging-окружение получает отдельный поддомен, например staging.example.com, и трафик на него физически не может попасть в production-сервисы, потому что правила привязаны к разным хостам. Минус - нужны отдельные DNS-записи и TLS-сертификаты для каждого окружения.
Разделение по путям использует один домен, но разные префиксы: example.com/production/ ведёт на prod-сервис, example.com/staging/ - на staging. Это проще в настройке DNS, но требует аккуратной работы с rewrite-target и создаёт риск случайного пересечения трафика, если в production-приложении есть путь, начинающийся с /staging. Для продакшена я рекомендую разделение по хостам как более надёжный и изолированный метод.
Реализация canary-развертываний через Ingress
Canary-развертывание - это стратегия, при которой новая версия приложения получает только часть трафика. Вы направляете 5-10% запросов на новую версию, мониторите метрики и при отсутствии ошибок постепенно увеличиваете долю до 100%. Ingress Controller позволяет реализовать это без установки service mesh, используя только аннотации.
Настройка canary по весу трафика
Самый распространённый метод - распределение по весу. Создайте два Ingress: основной для стабильной версии и canary-ресурс с аннотациями. Контроллер будет отправлять указанный процент запросов на canary-сервис.
# Основной Ingress для стабильной версии
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-stable
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-stable
port:
number: 8080
---
# Canary Ingress для новой версии
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-canary
port:
number: 8080
Эта конфигурация отправляет 10% запросов на app-canary, остальные 90% - на app-stable. Вес задаётся целым числом от 0 до 100. Измените canary-weight на 50, примените манифест - и трафик распределится поровну. При значении 100 весь трафик пойдёт на canary-версию, после чего можно удалить canary-аннотации и сделать новую версию основной.
Canary на основе HTTP-заголовков и cookie
Для более тонкого управления используйте маршрутизацию по заголовкам или cookie. Это позволяет направлять на новую версию только определённых пользователей - например, сотрудников компании или участников бета-программы.
Маршрутизация по cookie полезна, когда нужно, чтобы один и тот же пользователь всегда попадал на одну и ту же версию. Аннотация canary-by-cookie: "canary_version" с значением always направит пользователя на canary-сервис, если у него установлена соответствующая cookie. Это реализует sticky-сессии для canary-тестирования без изменения кода приложения.
Мониторинг и логирование маршрутизируемых запросов
Настройка маршрутизации без мониторинга - это работа вслепую. SRE должен видеть, сколько запросов проходит через Ingress, с какой задержкой, какие статус-коды возвращаются и на какие бэкенды уходит трафик. Nginx Ingress Controller из коробки отдаёт метрики в формате Prometheus и поддерживает структурированное логирование в JSON.
Сбор метрик с Nginx Ingress Controller для Prometheus
Для включения метрик добавьте аннотации в Service, который указывает на Ingress Controller, и настройте ServiceMonitor, если используете Prometheus Operator.
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller-metrics
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "10254"
spec:
selector:
app.kubernetes.io/component: controller
ports:
- name: metrics
port: 10254
targetPort: 10254
Ключевые метрики, которые нужно отслеживать: nginx_ingress_controller_requests - общее количество запросов с разбивкой по статус-кодам, nginx_ingress_controller_ingress_upstream_latency_seconds - задержка ответа от бэкенда, nginx_ingress_controller_nginx_process_connections - количество активных соединений. Настройте алерты в Prometheus на рост 5xx ошибок и увеличение latency выше порогового значения.
Готовые дашборды Grafana для Nginx Ingress Controller доступны в официальном репозитории - они показывают RPS, latency по перцентилям, распределение статус-кодов и utilisation ресурсов контроллера. Импортируйте дашборд с ID 9614 и адаптируйте под свои метрики. Для построения полного конвейера телеметрии от сбора логов до визуализации используйте руководство по маршрутизации логов и метрик в DevOps.
Настройка структурированного логирования
Логи в формате JSON проще парсить и анализировать в централизованных системах вроде EFK или Loki. Включите JSON-логирование через ConfigMap контроллера:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
data:
log-format-upstream: '{"time": "$time_iso8601", "remote_addr": "$remote_addr",
"host": "$host", "request_uri": "$request_uri", "status": $status,
"upstream_addr": "$upstream_addr",
"upstream_response_time": $upstream_response_time,
"request_time": $request_time}'
Поля upstream_response_time и request_time критически важны для диагностики: первое показывает, сколько времени отвечал бэкенд, второе - полное время обработки запроса контроллером. Если upstream_response_time близко к request_time, проблема на стороне приложения. Если request_time значительно больше - задержка в самом контроллере или сети.
Типичные ошибки при настройке Ingress и их решение
За годы эксплуатации Kubernetes-кластеров я собрал список ошибок, которые повторяются у большинства команд. Каждую проблему сопровождаю симптомом и способом исправления.
Неправильный pathType. Симптом: запросы на /api/v2 уходят не на тот сервис или возвращают 404. Причина: использование Exact вместо Prefix или наоборот. Для большинства API-сценариев используйте Prefix. Exact применяйте только когда нужно обслуживать строго один конкретный путь без подпутей.
Конфликт путей. Симптом: часть запросов уходит не на тот бэкенд. Причина: два правила с одинаковым префиксом, но разными сервисами. Контроллер выбирает правило с самым длинным совпадением, но при равенстве длин поведение не определено. Решение: проверяйте все Ingress в неймспейсе на пересечение путей командой kubectl get ingress -A и вручную анализируйте правила.
Отсутствие TLS Secret. Симптом: контроллер не стартует или возвращает ошибку при HTTPS-запросах. Причина: в секции tls указан Secret, которого нет в кластере. Решение: проверьте наличие Secret командой kubectl get secret и убедитесь, что он находится в том же неймспейсе, что и Ingress.
Неверные аннотации. Симптом: правило не применяется, но ошибок нет. Причина: опечатка в названии аннотации. Nginx Ingress Controller молча игнорирует неизвестные аннотации. Решение: сверяйте названия с официальной документацией, используйте автодополнение в IDE с плагином для Kubernetes.
Проблемы с сетевыми политиками. Симптом: Ingress создан, правила корректны, но запросы не доходят до Pod'ов. Причина: NetworkPolicy блокирует входящий трафик от неймспейса, где работает Ingress Controller, к неймспейсу приложения. Решение: создайте NetworkPolicy, явно разрешающую трафик от Pod'ов контроллера к вашим сервисам.
Тестируйте каждое изменение в staging-окружении. Конфигурация, которая работает на бумаге, может сломаться из-за особенностей конкретной версии контроллера или взаимодействия с другими ресурсами кластера. Для дополнительной защиты от нежелательного трафика ознакомьтесь с руководством по геофильтрации трафика в Kubernetes - там описаны методы ограничения доступа по регионам через Ingress и Network Policies.