Маршрутизация запросов в Kubernetes Ingress: от правил до мониторинга для SRE | AdminWiki

Маршрутизация запросов в Kubernetes Ingress: от правил до мониторинга для SRE

20 июля 2026 13 мин. чтения
Содержание статьи

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-аннотации и сделать новую версию основной.

Для более тонкого управления используйте маршрутизацию по заголовкам или 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.

Поделиться:
Сохранить гайд? В закладки браузера