В Kubernetes есть классическая ловушка: Service создан, DNS-имя резолвится, ClusterIP выдан, а запросы заканчиваются таймаутом. В логах Ingress/контроллера всплывает «kubernetes no endpoints», а kubectl get ep показывает пустоту. Обычно это не «магия сети», а вполне конкретная причина: Service не смог сопоставить свои селекторы с Pod, либо Pod не попали в готовые endpoints.
Ниже — практичная схема диагностики: от kubectl describe service и проверки selector/labels до нюансов targetPort и механизма EndpointSlice. Логика простая: идём по цепочке, фиксируем, на каком шаге всё «рассыпалось», и правим точечно.
Что такое Endpoints и почему это критично для Service
Service в Kubernetes — это стабильная точка входа (VIP + DNS) и правила маршрутизации. Но фактически он «работает» только когда существует список бэкендов — IP:port конкретных Pod. Исторически этот список публиковался в объекте Endpoints. Сейчас чаще используется EndpointSlice, но смысл одинаковый: контроллер смотрит на Service, находит подходящие Pod и публикует их адреса как endpoints.
Если endpoints пустые, то трафик направлять некуда: kube-proxy (или dataplane CNI) не сможет проксировать запросы, а Ingress/контроллер обычно исключит upstream и будет ругаться «no endpoints».
Главная мысль: Service не ищет Pod «по имени». Он выбирает Pod по labels, которые совпадают с selector в Service.
Быстрый чек-лист: как понять, почему kubectl get ep пустой
Начинайте с трёх проверок и не перескакивайте шаги. Если на шаге 3 вы не находите Pod по селектору — дальнейшие проверки портов и сети почти всегда бессмысленны.
Шаг 1: посмотреть Service и его selector
kubectl get svc -n NAMESPACE
kubectl describe service SERVICE_NAME -n NAMESPACE
kubectl get service SERVICE_NAME -n NAMESPACE -o yaml
В kubectl describe service внимательно смотрите:
Selector— по каким меткам выбираются Pod;PortиTargetPort— куда именно пойдёт трафик на стороне Pod;Endpoints— если там<none>, бэкенды не найдены или не опубликованы;- Events — иногда прямо подсказывают, что мешает публикации.
Шаг 2: проверить Endpoints и EndpointSlice
kubectl get endpoints -n NAMESPACE
kubectl get endpoints SERVICE_NAME -n NAMESPACE -o wide
kubectl get endpointslices -n NAMESPACE -l kubernetes.io/service-name=SERVICE_NAME
Важно: в части кластеров «истина» живёт в EndpointSlice. Поэтому если kubectl get ep пустой или выглядит странно — сразу проверяйте slices.
Шаг 3: найти Pod, которые должны попасть под selector
Возьмите селектор из Service и выберите Pod по меткам. Например, если в Service:
selector:
app: web
tier: frontend
То проверка выглядит так:
kubectl get pods -n NAMESPACE -l app=web,tier=frontend -o wide
kubectl describe pod POD_NAME -n NAMESPACE
Если выборка Pod пустая — вы почти нашли причину: неверные labels на Pod, неверный selector в Service или объекты находятся в разных namespace.
Самая частая причина no endpoints: selector и labels не совпали
В реальных инцидентах чаще всего проблема не в CNI и не в kube-proxy, а в том, что Service выбирает «никого».
Типовые сценарии рассинхронизации меток
- Переименовали label в Deployment, но забыли обновить Service.
- Разные окружения: у Pod метка
env=prod, а Service ожидаетenv=stage. - Helm values: селектор в шаблоне Service зависит от values, а релиз обновили частично.
- Service без selector: тогда endpoints должны быть созданы вручную, но их нет.
Как быстро сравнить selector и labels
Ваша цель — добиться, чтобы выборка Pod по селектору Service возвращала ровно те Pod, которые должны обслуживать трафик.
kubectl get service SERVICE_NAME -n NAMESPACE -o jsonpath='{.spec.selector}'
kubectl get pods -n NAMESPACE --show-labels
Если меток много, удобнее посмотреть метки конкретного Pod:
kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.metadata.labels}'
Практическое правило: сначала добейтесь, чтобы
kubectl get pods -l ...показывал нужные Pod. Только после этого имеет смысл разбираться сtargetPort, readiness и сетевыми нюансами.
Вторая частая причина: Port и targetPort не соответствуют реальности
Даже если Pod выбраны правильно, Service может отправлять трафик «не туда». В этом случае endpoints обычно не пустые, но симптомы похожи: таймауты, 502/504 на Ingress, «приложение не отвечает».
Коротко:
port— порт Service (куда стучатся клиенты внутри кластера);targetPort— порт на Pod, куда Kubernetes направляет трафик.
targetPort может быть числом или именем порта. Ошибка часто прячется именно в «именах», особенно когда в Pod несколько контейнеров.
Проверка сводится к сопоставлению Service → targetPort → фактический порт приложения.
kubectl describe service SERVICE_NAME -n NAMESPACE
kubectl get pod POD_NAME -n NAMESPACE -o yaml
В манифесте Pod смотрите containers → ports. Kubernetes не требует указывать containerPort, но для людей это сильно упрощает диагностику.
Если вы используете Ingress, полезно держать под рукой обзор по вариантам контроллеров и типовым ошибкам маршрутизации: сравнение Ingress-контроллеров и практические нюансы.

Почему endpoints могут быть пустыми даже при правильных labels
Бывает, что селектор совпал и Pod существуют, но endpoints не появляются или публикуются «не готовыми». Чаще всего виновата готовность Pod.
Pod не Ready: адреса не добавляются
По умолчанию Service публикует только Ready-адреса. Поэтому сначала проверьте состояние:
kubectl get pods -n NAMESPACE -l app=web -o wide
kubectl describe pod POD_NAME -n NAMESPACE
Особенно внимательно смотрите:
- ошибки
readinessProbe(неверный путь, порт, таймауты); - события ImagePullBackOff, CrashLoopBackOff, проблемы с PVC;
- зависимости приложения (БД, очередь), из-за которых readiness не проходит.
publishNotReadyAddresses: когда нужно и почему опасно
Опция Service publishNotReadyAddresses позволяет публиковать адреса Pod даже если они не Ready. Это полезно для некоторых stateful-сценариев (bootstrap/peer discovery), но для обычных веб-сервисов часто приводит к флаппингу и случайным 502/504.
Service без selector: endpoints должны быть «ручными»
У Service может не быть selector. Это нормально, когда вы подключаете внешние бэкенды (вне кластера) или строите нестандартную схему. Но тогда Kubernetes сам endpoints не соберёт.
Признаки простые: в kubectl describe service поле Selector пустое, а kubectl get ep не меняется от каких-либо labels на Pod.
В таком варианте должны существовать отдельные объекты Endpoints или EndpointSlice, созданные вручную (или оператором). Если их нет — это и есть причина «no endpoints».
EndpointSlice: чем отличается и как читать
EndpointSlice — современная замена «монолитному» Endpoints. Она масштабируется лучше (много Pod — много slices), хранит больше метаданных и стабильнее ведёт себя в больших кластерах.
Минимальная практика для админа: уметь найти slices по Service и увидеть, какие адреса и порты опубликованы.
kubectl get endpointslices -n NAMESPACE -l kubernetes.io/service-name=SERVICE_NAME
kubectl describe endpointslice ENDPOINTSLICE_NAME -n NAMESPACE
В описании смотрите:
- список endpoints и условия ready/serving/terminating;
- список ports (имя порта и номер);
- привязку к Service по лейблу
kubernetes.io/service-name.
Нюансы, которые ломают доступ, когда «вроде всё правильно»
Namespace: Service и Pod должны быть в одном пространстве имён
Service выбирает Pod только внутри своего namespace. Если Service в default, а Deployment в app, endpoints не появятся никогда.
kubectl get svc -A | grep SERVICE_NAME
kubectl get pods -A -l app=web
Headless Service вместо обычного
Для StatefulSet часто используют headless Service (clusterIP: None). Endpoints при этом будут, но поведение DNS и ожидания клиентов отличаются. Если вы ожидаете балансировку на ClusterIP, а создали headless — будет ощущение «не работает».
Named ports и одинаковые имена в разных контейнерах
Если targetPort задан именем, Kubernetes ищет порт с таким именем в Pod. При нескольких контейнерах и копипасте легко получить ситуацию, когда имя совпало, а номер порта — не тот (или нужный порт вообще в другом контейнере). Если именованные порты не нужны — используйте числовой targetPort.
Диагностика «с конца»: сравниваем port-forward на Pod и на Service
Когда непонятно, проблема в Service/Endpoints или в приложении, удобно сравнить порт-форвард на Pod и на Service. Если Pod отвечает, а Service — нет, почти наверняка проблема в Service, endpoints или targetPort.
kubectl port-forward -n NAMESPACE pod/POD_NAME 18080:8080
kubectl port-forward -n NAMESPACE service/SERVICE_NAME 18081:80
Порт-форвард не проверяет весь сетевой тракт кластера, но отлично подсвечивает типовые ошибки конфигурации Service.
Рецепты исправления: что менять и где
Если не совпали labels и selector
- Приведите
.spec.selectorService к фактическим меткам Pod (или наоборот), чтобы выборка Pod была однозначной. - Проверьте, что селектор не слишком общий, иначе Service может «подхватить» чужие Pod.
Если targetPort неверный
- Исправьте
targetPortна реальный порт приложения. - Если используете именованные порты, убедитесь, что имя совпадает в Service и в Pod.
Если Pod не Ready
- Чините readiness: путь, порт, таймаут, зависимости (БД/очереди), DNS.
- Смотрите события Pod и логи контейнера — это обычно быстрее, чем «ковырять сеть».
Мини-шпаргалка команд для инцидента no endpoints
kubectl describe service SERVICE_NAME -n NAMESPACE
kubectl get endpoints SERVICE_NAME -n NAMESPACE -o wide
kubectl get endpointslices -n NAMESPACE -l kubernetes.io/service-name=SERVICE_NAME
kubectl get pods -n NAMESPACE -l key=value -o wide
kubectl describe pod POD_NAME -n NAMESPACE

Итог: логика проста, если идти по цепочке
Сообщение «kubernetes no endpoints» почти всегда означает разрыв в одной из связок:
- Service selector не находит Pod по labels;
- Pod найдены, но не попали в endpoints из-за статуса Ready;
- endpoints есть, но
targetPortведёт не на тот порт; - вы смотрите в
Endpoints, а актуальная картина вEndpointSlice; - Service вообще без selector и требует ручных endpoints.
Держите в голове трассировку: Service → selector → Pod → readiness → endpoints/endpointslice → targetPort → приложение. Если идти ровно по ней, большинство проблем находится за 5–10 минут даже в незнакомом кластере.
Если вы разворачиваете сервисы под нагрузкой и важна предсказуемая сетка, ресурсы и изоляция на узлах, удобнее держать инфраструктуру на управляемых виртуалках: VDS часто даёт больше контроля над сетью, CNI и лимитами, чем «сборная солянка» из разрозненных сред.


