Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Сеть в Kubernetes: поды, сервисы, Ingress

Сеть Kubernetes для разработчика: IP подов, Service-дисcovery и Ingress

Как устроена кластерная сеть: плоская адресация подов, ClusterIP и DNS-имена сервисов, NodePort/LoadBalancer, Ingress-маршрутизация и диагностика связности.

Прямой ответ

Сетевая модель K8s держится на трёх утверждениях: (1) каждый под имеет свой IP в плоском адресном пространстве (под-к-поду напрямую без NAT через узлы — сетевой плагин CNI реализует overlay/маршруты — разработчику детали CNI обычно не нужны, важен факт: «curl на IP пода с другого пода работает»); (2) поды смертны и их IP меняется при пересоздании — поэтому существует Service — стабильная виртуальная точка доступа: ClusterIP (виртуальный IP, kube-proxy держит правила балансировки на реальные поды по label selector — селекторы) и DNS-имя (api.default.svc.cluster.local — внутри кластера обращаемся по имени сервиса, не по IP — «сервис-дискавери» встроен; короткое имя api работает в своём namespace); (3) трафик снаружи заходит через ограниченный набор путей: NodePort (порт на каждом узле — 30000–32767 — грубо, для dev), LoadBalancer (облачный LB перед NodePort — managed-стандарт), Ingress (HTTP-маршрутизация по хостам/путям через ingress-контроллер — nginx/Traefik — один внешний адрес на десятки сервисов — TLS-терминация здесь же — nginx-семантика внутри контроллера). Практический словарь разработчика: приложение зовёт соседний сервис как (имя сервиса = хост — конфигурация через env — [конфиг](./kubernetes-configmap-sekrety-konfiguratsiya.md)), наружу — Ingress-правило (host: api.example.com → service api:8000), внутри — ClusterIP хватает почти всегда; headless-сервис (clusterIP: None — DNS отдаёт IP подов напрямую — для stateful/«хочу конкретный под»). Диагностика связности — отдельный обязательный навык: DNS (nslookup из debug-пода), endpoints (kubectl get endpoints` — пустой = selector не нашёл подов — половина «сервис не отвечает»), NetworkPolicy (если включены — «всё запрещает по умолчанию» — явные разрешения).

Service: типы, DNS, балансировка

Типы (нарастающе): ClusterIP (дефолт — только внутри кластера — сервис-сервис), NodePort (+порт на узлах — снаружи по IP_узла:3xxxx — отладка/dev), LoadBalancer (+внешний LB от облака — публичный вход; каждый сервис — свой LB — дорого на десятках — потому Ingress поверх одного LB), ExternalName (CNAME на внешнее имя — «внешний API как внутренний сервис» — удобный абстракт). Механика ClusterIP: kube-proxy (iptables/IPVS-правила на узлах) — DNAT запросов на виртуальный IP в один из pod-IP (endpoints — живой список подов под selector'ом — readiness-поды только (readiness — неготовый под вне ротации — связь healthcheck'ов и сети)); балансировка — посессионно/по соединениям (не perfect round-robin — длинные соединения/keepalive к одному поду — «нагрузка легла на один под» при малом RPS — нормальна; sessionAffinity: ClientIP — при необходимости липкости). DNS: CoreDNS резолвит service.namespace.svc.cluster.local (короткие формы по контексту поиска: payments → свой ns, payments.billing → чужой); debug-пода с dnsutils — nslookup payments изнутри кластера — первая проверка «резолвится ли»; «connection refused vs no such host» — разные диагнозы (refused — под/порт не слушает; no such host — DNS/имя/namespace — диагностика переносится в кластерный контекст). Headless (clusterIP: None) — DNS возвращает все pod-IP (клиент выбирает сам — StatefulSet-идентичность, gRPC-балансировка на стороне клиента — gRPC с keepalive к одному ClusterIP — классическая «неравномерность» — headless/клиентский LB — gRPC-специфика).

Ingress и внешний трафик

Ingress — ресурс-правила (host/path → backend-сервис) + ingress-контроллер (исполнитель — nginx-ingress/Traefik/облачные — сам деплоймент в кластере, обычно за LoadBalancer); манифест: rules: - host: api.example.com, http: paths: - path: /, backend: service api port 8000 — аннотации — поведение (rewrite/лимиты/таймауты — nginx-ingress аннотации — «конфиг nginx через аннотации» — nginx-практика в кластерной обёртке: proxy-body-size для загрузок, timeouts для долгих запросов, rate-limit — знакомое); TLS — сертификаты в Secret (type: kubernetes.io/tls), автоматизация — cert-manager + Let's Encrypt (автопродление в кластерном виде — аннотация cluster-issuer — сертификаты сами); альтернатива новому классу — Gateway API (наследник Ingress-ресурса — выразительнее — новые кластеры смотрят туда; Ingress жив и доминирует пока). Паттерны входа: один Ingress на хост-сервис (api./admin./web. — один LB, маршруты по хостам — экономика), canary-аннотации (процент трафика на canary-сервис — стратегия средствами ингресса), WebSocket — поддержка из коробки (Upgrade-заголовки — nginx-ingress умеет — WebSocket-приложения за ингрессом живут; таймауты простоя — аннотациями). Восток-запад (сервис-сервис) — мимо ингресса (ClusterIP/DNS напрямую — ингресс на внутренние вызовы — лишний hop); NetworkPolicy — ACL-уровень (при наличии CNI с поддержкой: дефолт-запрет + явные allow по podSelector/namespace — «микросегментация» — включать осознанно: сломать связность легко, отлаживать — kubectl describe networkpolicy + тестовые curl из debug-подов).

Практический сценарий: «сервис не отвечает» — разбор по слоям

Исходник: новый сервис reports задеплоен; веб-фронтенд зовёт — 502 от фронтенда; внешний доступ — 404. Разбор по слоям (шаблон, который стоит выучить): (1) поды живы? kubectl get pods -l app=reports — Running/Ready 2/2 (не готовы — readiness — другой сценарий); (2) endpoints есть? kubectl get endpoints reports — ПУСТО — причина №1: selector сервиса app: report против label пода app: reports (опечатка — сервис не видит поды — kube-proxy некуда балансировать — connection refused/no endpoints); исправлен label — endpoints появились; (3) DNS резолвит? debug-под (kubectl run -it dns --image=busybox) — nslookup reports — IP ClusterIP вернулся (если нет — namespace в имени/CoreDNS — диагностика); (4) порт слушается? kubectl exec в под — curl localhost:8000/health — 200 (порт сервиса 8000 → targetPort 8000 совпал; расхождение port/targetPort — вторая частая причина «не отвечает» — сервис слушает 80, под — 8000); (5) фронтенд→reports — после фикса endpoints — 200; (6) внешний 404 — Ingress-правило отсутствовало вовсе (добавлен path /reports → reports:8000 с rewrite — аннотация rewrite-target — путь до приложения без префикса); TLS — существующий сертификат (wildcard) покрыл; (7) проверка извне — 200; (8) закрепление: чек-лист «под → endpoints → DNS → порт → policy → ingress» в runbook (инцидент-практика) — разбор следующего «не отвечает» занял 3 минуты. Дополнительный слой нашли позже: NetworkPolicy в namespace по умолчанию запрещал ingress-трафик из ns фронтенда (политики включили на прошлой неделе) — добавлен allow-правило по podSelector — урок: при включённых политиках диагностика начинается с них (describe networkpolicy — пустой список allow = всё запрещено).

FAQ

Почему запросы к сервису попадают на один под? Балансировка kube-proxy — по соединениям (iptables — случайный выбор на соединение; keepalive-соединение живёт на одном поду — при 10 rps одним клиентом — «всё на одном поде»); решения: короткие соединения/меньше keepalive-таймаут на клиенте (или пул с ротацией), клиентская балансировка по headless-сервису (gRPC-паттерн), service mesh (L7-балансировка на запрос — Istio/Linkerd — тяжёлая артиллерия); проверка равномерности — метрики rps по подам (Prometheus) — «неравномерность» часто мнимая на малых числах.

NodePort против LoadBalancer против Ingress — что когда? NodePort — dev/внутренний доступ без облака (порт 3xxxx — некрасиво для прода); LoadBalancer — один публичный сервис (БД-подобные/не-HTTP, или единственный API); Ingress — много HTTP(S)-сервисов за одним адресом (хосты/пути/TLS — экономия LB и сертификатов — стандарт прода); комбинация LoadBalancer(внешний LB) → ingress-контроллер → сервисы — типовая архитектура; TCP-протоколы за ингрессом — не живут (HTTP-ресурс — для TCP — LoadBalancer/отдельные пути).

Как приложениям искать друг друга без K8s-DNS? Почти никак не нужно: DNS-имена сервисов — контракт кластера (имя сервиса стабильно — поды меняются — 12-factor: адрес соседа — конфигурация/env); сервис-регистри (Consul/Eureka) в K8s — наследие VM-мира (дублируют встроенное — оправданы в гибридах/мульти-рантаймах); прямые pod-IP — запрещены практикой (IP смертен); «хардкод IP пода в конфиге» — антипаттерн, ломающийся при первом же пересоздании.

Что такое CNI и нужно ли его знать разработчику? CNI — плагин под-сети (Calico/Cilium/Flannel/облачные — реализуют плоскую адресацию и политики); разработчику достаточно модели (поды имеют IP, связность все-ко-всем по умолчанию, NetworkPolicy — если плагин поддерживает); знать свой плагин полезно в двух случаях: отладка связности (особенности — Cilium eBPF против iptables-Calico — разная диагностика) и NetworkPolicy-возможности (Calico-расширения против базовой спеки); выбор плагина — инфраструктурное решение (K8s-развёртывание — локально обычно Flannel — достаточно).

Источники: документация Kubernetes (services-networking, Service, Ingress) — ссылки в шапке.