Введение: зачем управлять трафиком в Kubernetes
Kubernetes по умолчанию разрешает свободное сетевое взаимодействие между всеми подами в кластере. Любой контейнер может обратиться к любому другому контейнеру в любом пространстве имен. Для разработки это удобно, для production - неприемлемо. Без контроля трафика база данных с платежными данными оказывается в одной плоской сети с фронтендом, доступным из интернета. Одна скомпрометированная зависимость в поде открывает злоумышленнику весь кластер.
Эта статья дает полный набор инструментов для управления сетевым трафиком. Вы настроите Ingress-контроллеры для приема внешних запросов, примените NetworkPolicy для изоляции микросервисов, ограничите исходящие соединения через Egress-политики и познакомитесь с продвинутыми возможностями Istio. Все примеры проверены на практике и готовы к применению в ваших кластерах.
Сетевую модель Kubernetes можно представить как три уровня. Первый - связность подов через CNI-плагин, который назначает IP-адреса и обеспечивает маршрутизацию. Второй - абстракция Service для стабильного обнаружения подов. Третий - политики и контроллеры, которые определяют, какой трафик разрешен. Именно третий уровень мы детально разберем: Ingress для входящего трафика, NetworkPolicy для изоляции, Egress для контроля исходящих соединений и сервис-меш для сценариев, выходящих за рамки стандартных возможностей Kubernetes.
Если вы проектируете кластер для продакшена, начните с полного руководства по сетям Kubernetes, где разобраны CNI-плагины и базовая связность. Для настройки Service и Ingress с готовыми манифестами используйте практическое руководство по Service и Ingress.
Ingress: маршрутизация внешних запросов к сервисам
Ingress решает задачу приема HTTP и HTTPS трафика из-за пределов кластера и маршрутизации его к нужным сервисам. Без Ingress вам пришлось бы создавать для каждого сервиса отдельный LoadBalancer, что дорого и неудобно. Один Ingress-контроллер обрабатывает все входящие соединения и направляет их по правилам, описанным в Ingress-ресурсах.
Что такое Ingress и Ingress-контроллер
Ingress - это API-объект Kubernetes, который описывает правила маршрутизации: на какой хост и путь пришел запрос, к какому сервису его направить. Сам по себе Ingress-ресурс ничего не делает. Это просто набор правил в YAML.
Ingress-контроллер - это под или набор подов, которые читают Ingress-ресурсы и применяют их. Контроллер запускает внутри себя прокси-сервер (NGINX, HAProxy, Traefik, Envoy) и динамически обновляет его конфигурацию при изменении Ingress-ресурсов или сервисов. Схема взаимодействия проста: внешний запрос приходит на IP контроллера, контроллер смотрит на Host-заголовок и URL-путь, находит подходящее правило Ingress и проксирует запрос на соответствующий Service, который уже направляет трафик к конкретным подам.
Ключевое различие, которое часто упускают: Ingress-контроллер работает на уровне L7 (HTTP/HTTPS), анализируя заголовки и пути. Service типа LoadBalancer или NodePort работает на L4 (TCP/UDP) и не разбирает HTTP. Если вам нужна маршрутизация по URL, SSL-терминация, редиректы - вам нужен Ingress.
Установка и настройка NGINX Ingress Controller
NGINX Ingress Controller - самый популярный контроллер в экосистеме Kubernetes. Он использует NGINX как прокси-движок и поддерживает практически все возможности, описанные в спецификации Ingress: SSL-терминацию, редиректы, rewrite правил, rate limiting, аутентификацию через аннотации.
Установка через Helm занимает несколько минут. Добавьте репозиторий и установите чарт:
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.service.type=LoadBalancer
Если у вас нет облачного LoadBalancer, замените тип сервиса на NodePort. После установки проверьте, что под контроллера запущен:
kubectl get pods -n ingress-nginx
Теперь создайте первый Ingress-ресурс. Предположим, у вас есть сервис web-app в пространстве имен default, который слушает порт 80. Манифест Ingress для маршрутизации запросов с домена myapp.example.com:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 80Поле ingressClassName обязательно в Kubernetes 1.18+. Аннотация rewrite-target указывает NGINX переписывать URL перед отправкой в бэкенд. Проверьте, что правило применилось:
kubectl get ingress kubectl describe ingress web-app-ingress
Для настройки TLS добавьте секцию tls в манифест. Предварительно создайте Secret с сертификатом:
kubectl create secret tls myapp-tls --cert=cert.pem --key=key.pem
Затем дополните Ingress:
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tlsNGINX Ingress Controller автоматически настроит SSL-терминацию для указанного хоста. Для автоматического получения сертификатов от Let's Encrypt интегрируйте cert-manager. Подробнее о мониторинге метрик NGINX Ingress читайте в руководстве по маршрутизации для SRE.
Установка и настройка Traefik как Ingress-контроллера
Traefik - это современный прокси с динамической конфигурацией. Он автоматически обнаруживает сервисы Kubernetes через API-сервер и обновляет маршруты без перезагрузки. Traefik имеет встроенную поддержку Let's Encrypt, middleware для обработки запросов и собственные CRD (Custom Resource Definitions) - IngressRoute, которые расширяют стандартный Ingress.
Установка через Helm:
helm repo add traefik https://traefik.github.io/charts helm repo update helm install traefik traefik/traefik \ --namespace traefik \ --create-namespace
Traefik использует CRD IngressRoute вместо стандартного Ingress. Пример маршрутизации с TLS от Let's Encrypt:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: web-app-route
spec:
entryPoints:
- websecure
routes:
- match: Host(`myapp.example.com`)
kind: Rule
services:
- name: web-app
port: 80
tls:
certResolver: letsencryptДля работы Let's Encrypt нужно настроить certResolver в статической конфигурации Traefik. Передайте параметры при установке Helm:
--set additionalArguments="{--certificatesresolvers.letsencrypt.acme.tlschallenge=true,--certificatesresolvers.letsencrypt.acme.email=admin@example.com,--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json}"Traefik поддерживает middleware - промежуточные обработчики запросов. Например, можно добавить Basic Auth, rate limiting или редирект с HTTP на HTTPS без правки кода приложения. Middleware определяется как отдельный CRD и ссылается в IngressRoute. Это дает гибкость, недоступную в стандартном Ingress без аннотаций.
Выбор между NGINX и Traefik сводится к приоритетам. NGINX проверен годами, имеет огромную базу знаний и предсказуемую производительность. Traefik удобен в микросервисных средах с частыми изменениями: автоматическое обнаружение сервисов сокращает объем ручной конфигурации. Если вы используете Docker и Kubernetes вместе, Traefik обеспечит единый подход к маршрутизации. Для высоконагруженных систем с жесткими требованиями к latency чаще выбирают NGINX.
NetworkPolicy: изоляция трафика между подами
Ingress решает проблему внешнего доступа, но не ограничивает трафик внутри кластера. Kubernetes по умолчанию разрешает все соединения между подами. NetworkPolicy меняет это поведение: вы явно описываете, какие входящие и исходящие соединения разрешены для группы подов. Политики действуют на уровне L3/L4 - IP-адреса, порты, протоколы.
Важное требование: NetworkPolicy реализуется CNI-плагином. Не все плагины поддерживают политики. Calico, Cilium, Weave Net поддерживают. Flannel - нет. Проверьте документацию вашего CNI перед внедрением политик. Без поддержки CNI вы можете создать NetworkPolicy, она будет висеть в кластере, но не будет применяться - трафик останется неограниченным.
Создание политик для ограничения входящего трафика
Защитим бэкенд-сервис от несанкционированного доступа. У нас есть поды с меткой app=backend, которые должны принимать запросы только от подов с меткой app=frontend на порт 8080. Все остальные входящие соединения запрещены.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-ingress-policy
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080Разберем ключевые поля. podSelector определяет целевые поды, к которым применяется политика. policyTypes указывает направление трафика: Ingress (входящий), Egress (исходящий) или оба. Секция ingress описывает разрешенные источники. В примере разрешен только один источник - поды с меткой app=frontend. Поле ports ограничивает разрешенные порты.
Проверьте эффект политики. До применения любой под в кластере мог обратиться к backend на любой порт. После применения только frontend-поды могут подключиться к порту 8080. Запустите тестовый под и попробуйте достучаться до backend:
kubectl run test --rm -it --image=busybox -- sh wget -O- --timeout=3 http://backend-service:8080
Если тестовый под не имеет метки app=frontend, соединение зависнет и упадет по таймауту.
Стратегия deny-all - хорошая отправная точка для production. Создайте политику, которая запрещает весь входящий трафик в пространство имен, а затем добавляйте разрешающие правила для конкретных сервисов:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- IngressПустой podSelector означает «все поды в пространстве имен». Отсутствие секции ingress означает «нет разрешенных входящих соединений». Это базовый уровень изоляции, который предотвращает случайный доступ между сервисами.
Контроль исходящего трафика (Egress) с помощью NetworkPolicy
Ограничение исходящего трафика не менее важно, чем входящего. Скомпрометированный под не должен устанавливать соединения с внешними серверами злоумышленника или обращаться к внутренним сервисам, которые ему не нужны для работы. Egress-политики решают эту задачу.
Типичный сценарий: поды приложения должны обращаться только к внешнему API по определенному IP-адресу и к DNS-серверу для разрешения имен. Все остальные исходящие соединения запрещены.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-egress-policy
namespace: default
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53Политика разрешает два исходящих направления. Первое - IP-блок 203.0.113.0/24 на порт 443 (внешний API). Второе - DNS-сервер Kubernetes (kube-dns или CoreDNS) на порт 53 UDP. Без явного разрешения DNS поды не смогут разрешать доменные имена, даже если IP-адрес API разрешен. Это частая ошибка: приложение падает с ошибкой разрешения имени, хотя доступ к API по IP работает.
Проверка Egress-политики аналогична Ingress: запустите тестовый под с нужной меткой и выполните запрос к внешнему ресурсу. Для диагностики используйте nslookup и curl:
kubectl exec -it myapp-pod -- nslookup example.com kubectl exec -it myapp-pod -- curl -v https://203.0.113.1
Если политика работает корректно, DNS-запросы выполняются, HTTPS-соединение к разрешенному IP устанавливается, а любые другие исходящие соединения блокируются.
Для геофильтрации трафика на уровне NetworkPolicy можно комбинировать IP-блоки по странам. Подробный разбор этого сценария с готовыми манифестами смотрите в руководстве по геофильтрации в Kubernetes.
Продвинутое управление трафиком с Istio
NetworkPolicy и Ingress закрывают базовые потребности: изоляцию на L3/L4 и внешний HTTP-доступ. Когда требуется управление трафиком на уровне L7 между сервисами - распределение нагрузки по весам, повторные попытки при ошибках, автоматическое взаимное шифрование - в дело вступает сервис-меш. Istio - один из самых распространенных сервис-мешей, который внедряет sidecar-прокси (Envoy) в каждый под и централизованно управляет всем трафиком.
Переход к сервис-мешу оправдан, когда у вас десятки микросервисов, и вам нужны: канареечные развертывания с точным контролем процента трафика, сквозная взаимная TLS-аутентификация между всеми сервисами, сбор метрик и трейсинг без изменения кода, политики авторизации на уровне HTTP-заголовков и JWT-токенов. Если у вас три сервиса и простой Ingress - Istio добавит сложности без соразмерной пользы.
Настройка шлюза и виртуальных сервисов для внешнего трафика
В Istio аналогом Ingress выступают два объекта: Gateway и VirtualService. Gateway описывает, какие порты и протоколы открыты для входящего трафика на границе меша. VirtualService определяет правила маршрутизации для трафика, прошедшего через Gateway.
Создадим Gateway для приема HTTP-трафика на порту 80:
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: myapp-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "myapp.example.com"Селектор istio: ingressgateway указывает, что этот Gateway применяется к стандартному Istio Ingress Gateway, который установлен в кластере. Теперь VirtualService для маршрутизации по URI:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp-vs
spec:
hosts:
- "myapp.example.com"
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /api
route:
- destination:
host: api-service
port:
number: 8080
- match:
- uri:
prefix: /
route:
- destination:
host: web-service
port:
number: 80Запросы на /api уходят к api-service, все остальные - к web-service. По сравнению со стандартным Ingress, Istio дает больше возможностей: маршрутизация по заголовкам, распределение трафика по весам между разными версиями сервиса, fault injection для тестирования отказоустойчивости.
Обеспечение безопасности с помощью mTLS и авторизации
Взаимная TLS-аутентификация (mTLS) - одна из ключевых фич Istio. Когда mTLS включен, все соединения между подами автоматически шифруются, и каждая сторона проверяет сертификат другой стороны. Это защищает трафик от перехвата внутри кластера и гарантирует, что сервисы общаются только с легитимными контрагентами.
Включение строгого mTLS во всем пространстве имен:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICTРежим STRICT требует mTLS для всех соединений. Под без sidecar-прокси Envoy не сможет подключиться к сервисам в этом пространстве имен. Режим PERMISSIVE разрешает и mTLS, и обычный трафик - удобно при миграции.
AuthorizationPolicy позволяет управлять доступом на уровне сервис-аккаунтов. Например, разрешим доступ к payment-service только от сервис-аккаунта checkout-service:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payment-access
namespace: production
spec:
selector:
matchLabels:
app: payment
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/checkout-service"]Поле principals использует SPIFFE-идентификаторы, которые Istio автоматически назначает на основе сервис-аккаунтов Kubernetes. Это более гранулярный контроль, чем NetworkPolicy на основе меток подов: вы разрешаете доступ конкретному сервис-аккаунту, а не любому поду с определенной меткой.
Диагностика и решение типовых проблем с трафиком
Сетевые проблемы в Kubernetes сложны тем, что трафик проходит несколько уровней абстракции: подовая сеть, Service, Ingress-контроллер, возможно, сервис-меш. Систематический подход к диагностике экономит часы.
Первое правило: проверяйте связность с самого нижнего уровня. Может ли под A достучаться до пода B напрямую по IP? Если нет - проблема в CNI или NetworkPolicy. Если да, но недоступен Service - проблема в селекторах Service или в том, что под не готов (readiness probe).
Проверка связности и анализ логов
Запустите временный под с сетевыми утилитами для тестирования:
kubectl run debug --rm -it --image=nicolaka/netshoot -- bash
Образ nicolaka/netshoot содержит curl, dig, telnet, tcpdump и другие инструменты. Проверьте разрешение DNS:
dig +short my-service.default.svc.cluster.local
Если DNS не разрешается, проблема может быть в CoreDNS или в NetworkPolicy, блокирующей UDP 53. Проверьте доступность сервиса по IP:
curl -v http://10-244-1-5.default.pod.cluster.local:8080
IP-адрес пода можно узнать через kubectl get pod -o wide. Если прямой доступ по IP работает, а через Service нет - проверьте селекторы в Service:
kubectl describe service my-service
Сравните метки в поле Selector с метками подов. Несовпадение - самая частая причина неработающих сервисов.
Для диагностики Ingress анализируйте логи контроллера. NGINX Ingress Controller пишет детальные логи о каждом запросе и ошибках конфигурации:
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller --tail=100
Ищите строки с уровнем error или warn. Типичные ошибки: «no live upstreams» (сервис не имеет готовых подов), «could not find a server block» (неправильный хост в Ingress), «SSL certificate not found» (отсутствует или просрочен Secret с сертификатом).
NetworkPolicy не имеют логов по умолчанию. Для отладки используйте kubectl describe:
kubectl describe networkpolicy backend-ingress-policy
Проверьте, что podSelector соответствует целевым подам, а в секции ingress.from указаны правильные селекторы. Некоторые CNI, например Cilium, предоставляют инструменты для трассировки политик: cilium policy trace.
Лучшие практики и рекомендации для production
Начинайте с deny-all NetworkPolicy в каждом production-пространстве имен. Пустая политика, запрещающая весь входящий трафик, создает безопасный базовый уровень. Затем добавляйте разрешающие правила только для необходимых соединений. Это принцип нулевого доверия в рамках кластера: каждый сервис по умолчанию изолирован.
Выбирайте Ingress-контроллер под конкретные задачи. NGINX Ingress Controller показывает стабильную производительность под высокой нагрузкой и имеет обширную документацию. Traefik удобен в средах с частыми развертываниями благодаря автоматическому обнаружению сервисов. Не разворачивайте оба контроллера в одном кластере без веской причины - это усложняет диагностику.
Внедряйте сервис-меш только при реальной потребности. Istio решает сложные задачи, но добавляет накладные расходы: sidecar-прокси потребляют CPU и память, задержка каждого запроса увеличивается на доли миллисекунды, отладка усложняется. Если вам нужны только изоляция и базовый ingress-egress контроль - NetworkPolicy и Ingress-контроллера достаточно.
Настройте мониторинг сетевого трафика. Prometheus и Grafana с дашбордами для NGINX Ingress или Istio покажут паттерны трафика, задержки и ошибки. Без мониторинга вы узнаете о проблемах от пользователей. Для долгосрочного хранения логов Ingress-контроллера используйте централизованное логирование - Loki, Elasticsearch или облачные решения.
Регулярно обновляйте Ingress-контроллеры и CNI-плагины. Уязвимости в сетевых компонентах критичны: они находятся на границе кластера и обрабатывают весь входящий трафик. Подпишитесь на уведомления о релизах и планируйте обновления каждые 1-2 месяца. Перед обновлением в production проверяйте новую версию в staging-окружении с тестами связности.
При использовании облачных Kubernetes-сервисов, таких как Timeweb Cloud, убедитесь, что выбранный CNI-плагин поддерживает NetworkPolicy. Некоторые управляемые кластеры по умолчанию используют плагины без поддержки политик - проверьте это до развертывания production-нагрузок.