Зачем вообще нужен Service и при чём тут kube-proxy
Pod’ы в Kubernetes живут недолго: пересоздаются, меняют IP, переезжают на другие ноды. Если ходить к Pod’ам напрямую по их IP, приложение развалится при первом же рескейле или рестарте. Поэтому в Kubernetes есть абстракция Service — стабильная точка входа (виртуальный IP и порт), за которой прячется динамический набор Pod’ов.
За «приземление» этой абстракции на реальные сетевые правила обычно отвечает kube-proxy. В большинстве кластеров он делает это, программируя правила на нодах: чаще всего через iptables (режим iptables), реже через IPVS (режим ipvs). Дальше фокус — на сценарии kube-proxy iptables, потому что именно там чаще всего приходится разбираться руками.
Важно: kube-proxy не является прокси в привычном смысле (не «слушает порт» и не форвардит трафик пользовательским процессом). Он настраивает dataplane (iptables/ipvs), чтобы пакеты, идущие на Service IP/порт, перенаправлялись на один из Pod’ов.
Три базовых типа Service: ClusterIP, NodePort, LoadBalancer
ClusterIP
ClusterIP — сервис доступен только внутри кластера. Kubernetes выделяет виртуальный IP из диапазона Service CIDR (например, 10.96.0.0/12), и весь трафик на этот IP перенаправляется на backend Pod’ы.
Типичный кейс: внутренние API, базы, кэши, микросервисы.
NodePort
NodePort — открывает порт на каждой ноде (обычно диапазон 30000-32767). При обращении к nodeIP:nodePort трафик попадает в Service и дальше — в Pod.
Типичный кейс: простой внешний доступ без облачного балансировщика, тестовые стенды, bare metal.
LoadBalancer
LoadBalancer — поверх Service появляется внешний балансировщик. В managed-кластерах это обычно интеграция с облачным LB. В on-prem/bare metal часто используют MetalLB или другие контроллеры. С точки зрения kube-proxy, LoadBalancer почти всегда опирается на механику NodePort/ClusterIP, а внешний балансировщик лишь доставляет трафик внутрь.

Как kube-proxy «сшивает» Service с Pod’ами
У Service есть селектор (labels), по которому выбираются Pod’ы. По факту Kubernetes создаёт объект Endpoints (или EndpointSlice), где перечислены IP:port реальных backend’ов. kube-proxy смотрит на Endpoints и строит правила, чтобы трафик на Service уходил в один из этих адресов.
Базовая проверка, которую делают первой при отладке Service:
kubectl get svc -n NAMESPACE
kubectl describe svc SERVICE -n NAMESPACE
kubectl get endpoints SERVICE -n NAMESPACE -o wide
kubectl get endpointslice -n NAMESPACE -l kubernetes.io/service-name=SERVICE -o wide
Если у Service нет Endpoints, kube-proxy обычно не сможет направить трафик «в никуда». Это одна из самых частых причин симптома clusterip unreachable.
kube-proxy и iptables: что именно он добавляет
В режиме iptables kube-proxy создаёт цепочки и правила NAT/фильтрации. На ноде можно увидеть «фирменные» цепочки вида KUBE-SERVICES, KUBE-NODEPORTS, KUBE-SVC-*, KUBE-SEP-*.
Диагностика на ноде (нужен root):
iptables -t nat -S | grep KUBE | head
iptables -t nat -S KUBE-SERVICES | head
iptables -t nat -S KUBE-NODEPORTS | head
На что смотреть:
- есть ли правила для нужного Service (по
ClusterIP:portи/или поNodePort); - есть ли привязка к цепочкам endpoint’ов (обычно
KUBE-SEP-*), иначе трафик будет «проваливаться»; - не перехватывают ли пакеты другие правила (локальный firewall, политики безопасности, nftables-совместимость).
Если вы как раз упёрлись в нюансы совместимости, полезно держать под рукой шпаргалку по iptables/nftables на Linux: как читать правила iptables и где мешает nftables-обвязка.
Практика: быстрая шпаргалка для kubernetes service debug
Когда «сервис не открывается», важно разделить проблему по слоям: DNS, Service/Endpoints, kube-proxy dataplane, CNI/маршрутизация, firewall, приложение в Pod’е.
1) Проверяем, что backend вообще жив
Сначала убедитесь, что Pod слушает порт и readiness зелёный:
kubectl get pods -n NAMESPACE -o wide
kubectl describe pod POD -n NAMESPACE
kubectl logs -n NAMESPACE POD --tail=200
Если readiness probe не проходит, Pod не попадёт в Endpoints — Service будет пустым.
2) Проверяем Endpoints и соответствие селектора
kubectl get svc SERVICE -n NAMESPACE -o yaml
kubectl get endpoints SERVICE -n NAMESPACE -o yaml
Частая ошибка: селектор Service не совпадает с labels Pod’ов. Тогда Endpoints пустой, а вы получаете «как будто сеть сломалась».
3) Проверяем доступ к ClusterIP изнутри кластера
Запускаем временный Pod и тестируем:
kubectl run -n NAMESPACE netshoot --rm -it --image=nicolaka/netshoot -- sh
Внутри:
nslookup SERVICE
nc -vz SERVICE PORT
curl -sS -m 3 http://SERVICE:PORT/
Если DNS не резолвит — это уже история про CoreDNS. Если DNS резолвит, но ClusterIP не отвечает — смотрим Endpoints, kube-proxy и CNI.
4) Проверяем kube-proxy и его режим
kubectl -n kube-system get ds kube-proxy -o wide
kubectl -n kube-system logs ds/kube-proxy --tail=200
Полезно понять, что используется: iptables или ipvs. В логах kube-proxy обычно видно, какой proxier активен. Если ожидали iptables, а включён IPVS (или наоборот), диагностика будет другой.
Симптом: clusterip unreachable
Под «ClusterIP недоступен» обычно скрывается один из сценариев:
- Нет Endpoints: селектор не совпал, readiness не проходит, Pod’ы не в Running.
- kube-proxy не применил правила: kube-proxy не запущен на ноде, упал, конфликтует с nftables-режимом, проблемы с conntrack.
- Проблема маршрутизации/overlay: CNI не поднялся, нет маршрутов между нодами, MTU/encapsulation, dropped пакеты.
- NetworkPolicy режет трафик к Pod’ам.
Если Service существует, DNS резолвит имя в ClusterIP, но TCP connect зависает или получает timeout — чаще всего проблема не в DNS, а в цепочке Service → Endpoints → правила dataplane → достижимость Pod IP.
Минимальный план действий:
kubectl get endpoints SERVICE -n NAMESPACE -o wide
kubectl get pods -n NAMESPACE -o wide --show-labels
kubectl describe svc SERVICE -n NAMESPACE
Дальше — тест напрямую до Pod IP (минуя Service). Если Pod IP недоступен с другой ноды, это CNI/маршрутизация/политики, а не kube-proxy:
kubectl get pod POD -n NAMESPACE -o wide
Затем из netshoot:
nc -vz POD_IP POD_PORT
Симптом: nodeport not working
Когда nodeport not working, часто путают два разных «не работает»:
- не доступно снаружи (ваша сеть/интернет) до
nodeIP:nodePort; - внутри кластера NodePort тоже не открывается.
Если изнутри кластера NodePort работает, а снаружи нет — в большинстве случаев виноваты security group/ACL/iptables на нодах, маршрутизация до nodeIP или отсутствие публичного IP.
Проверка на ноде: слушать процесс NodePort не будет (это нормально), но правила должны быть:
iptables -t nat -S KUBE-NODEPORTS | grep NODEPORT
Ещё одна классика: NodePort открыт на всех нодах, но вы пытаетесь подключаться к ноде, которая недоступна извне (например, internal-only). Тогда корректнее использовать внешний LB или выносить точку входа на edge.
NodePort и health checks
Если вы используете внешний балансировщик (включая on-prem решения) поверх NodePort, проверьте, как он делает health check. Часто проверки приходят с адресов, которые попадают под ограничения firewall или не учтены в allowlist.

LoadBalancer: где заканчивается kube-proxy и начинается инфраструктура
Service типа LoadBalancer обычно создаёт:
- ClusterIP (как базу);
- часто — NodePort (как точку входа для внешнего LB);
- объект статуса с внешним адресом.
kube-proxy отвечает за доставку трафика после того, как он попал на ноду (через NodePort или напрямую). А вот «попасть на ноду» — это уже зона ответственности облачного провайдера/MetalLB/вашей сети.
Endpoints и EndpointSlice: почему это важно в реальной отладке
Исторически Service опирался на объект Endpoints. В новых версиях активно используются EndpointSlice — это более масштабируемый вариант, особенно при большом количестве Pod’ов.
В отладке это полезно так:
- Если Endpoints пустой, но EndpointSlice есть — проверьте версии компонентов и корректность контроллеров (обычно они синхронизированы, но в проблемных кластерах встречаются несостыковки).
- Если в EndpointSlice адреса есть, но трафик не ходит — ищите проблему ниже: dataplane, CNI, политики.
ExternalTrafficPolicy: Local vs Cluster — что меняется и почему ломается доступ
Ключ externalTrafficPolicy (поле externalTrafficPolicy в Service) особенно важен для NodePort/LoadBalancer.
Два режима:
- Cluster (по умолчанию): трафик, пришедший на ноду, может быть отправлен на Pod’ы на других нодах. Это повышает шанс успешной доставки, но часто «прячет» реальный клиентский IP (в зависимости от схемы и SNAT).
- Local: трафик будет направлен только на Pod’ы, которые локально есть на той ноде, куда пришёл запрос. Клиентский IP сохраняется чаще, но появляется риск 503/timeout, если LB шлёт трафик на ноды без локальных endpoint’ов.
Если включили
externalTrafficPolicy: Localи внезапно получили «частично не работает», проверьте: есть ли backend Pod’ы на каждой ноде, куда направляет трафик внешний балансировщик.
Что делать на практике:
kubectl describe svc SERVICE -n NAMESPACE | grep -i ExternalTrafficPolicy -n
kubectl get endpoints SERVICE -n NAMESPACE -o wide
kubectl get pods -n NAMESPACE -o wide
Если LB балансирует по всем нодам, а Pod’ы есть только на части — либо меняйте политику обратно на Cluster, либо настраивайте LB так, чтобы он слал трафик только на «здоровые» ноды, либо обеспечьте присутствие Pod’ов на всех нодах (DaemonSet/anti-affinity/реплики).
Частые причины «всё настроено, но всё равно не ходит»
Конфликт iptables и nftables
На современных дистрибутивах iptables может быть «обёрткой» над nftables. В некоторых комбинациях версий ядра/iptables/nft и Kubernetes-компонентов возникают странные эффекты: правила как будто есть, но трафик не соответствует ожиданиям. Если вы подозреваете это, зафиксируйте, какой backend используется:
update-alternatives --display iptables
И проверьте, что в кластере выбран поддерживаемый путь для вашей версии Kubernetes и ОС. Если нужно навести порядок с firewall на ноде, пригодится отдельный разбор: практическое руководство по nftables для серверов.
Conntrack и перегрузка таблицы соединений
При большой нагрузке или сетевых штормах может упираться conntrack. Симптомы похожи на «случайные таймауты» к Service/Pod. Смотрите счётчики и лимиты на ноде:
sysctl net.netfilter.nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
Если nf_conntrack_count стабильно близок к максимуму — это инфраструктурная задача: тюнинг, снижение количества коротких соединений, keepalive, правильные таймауты.
NetworkPolicy блокирует трафик
Если вы используете CNI с поддержкой NetworkPolicy, убедитесь, что разрешён входящий трафик к Pod’ам со стороны нужных источников (namespaceSelector/podSelector/ports). При включённом default-deny Service будет существовать, Endpoints будут, но пакеты будут дропаться на уровне dataplane CNI.
Мини-чеклист: что собрать в тикет/инцидент
Если вы дебажите не в одиночку (или передаёте в поддержку/сетевикам), соберите минимум артефактов:
kubectl get svc,kubectl get endpoints,kubectl get endpointsliceпо проблемному сервису;- пример Pod IP и нода, где он запущен (
kubectl get pod -o wide); - как именно тестировали (изнутри кластера или снаружи), к какому адресу/порту;
- логи kube-proxy за момент проблемы;
- фрагменты
iptables -t nat -S KUBE-SERVICESиiptables -t nat -S KUBE-NODEPORTSпо нужному Service/NodePort.
Итоги
Service в Kubernetes — это не «магия», а довольно конкретная связка: Service IP/порт + Endpoints/EndpointSlice + правила, которые на нодах настраивает kube-proxy. Когда возникают симптомы вроде clusterip unreachable или nodeport not working, быстрее всего двигаться сверху вниз: Endpoints → проверка доступа к Pod IP → kube-proxy rules (iptables) → CNI/маршрутизация → firewall/политики → особенности externalTrafficPolicy для внешнего трафика.
Если хотите углубиться, в следующем материале можно разобрать типовые дампы iptables для Service и показать, как по цепочкам быстро понять: куда именно должен уйти пакет и на каком шаге он теряется.


