Service в Kubernetes не отвечает: проверяем selector и EndpointSlice
После развёртывания API имя api.demo.svc.cluster.local успешно разрешается, но клиент получает тайм-аут. DNS подтвердил только существование имени Service. Он ещё ничего не сказал о том, есть ли за этим адресом работающее приложение. Начнём с цепочки, которую можно проверить по частям: Service выбирает Pod по меткам, контроллер создаёт адреса целей, сетевой компонент направляет к ним трафик.
Смотрим на Service в нужном namespace
kubectl -n demo get service api -o yaml
kubectl -n demo get pods --show-labels
kubectl -n demo get endpointslice -l kubernetes.io/service-name=api -o yamlИмя api в командах — имя Service, а не обязательно Deployment. Метка kubernetes.io/service-name связывает EndpointSlice с нужным Service. Срезов может быть несколько, поэтому важно получить весь список. Старый объект Endpoints встречается в существующих инструментах, но для нового расследования удобнее читать EndpointSlice, где явно представлены адреса, порты и условия готовности.
Предположим, selector содержит app: api, а у Pod единственная похожая метка app.kubernetes.io/name: api. Значения одинаковы, ключи различаются, совпадения нет. Kubernetes не угадывает связь по имени Deployment. Если selector содержит несколько пар, Pod должен удовлетворять всем. Дополнительная метка track: stable способна исключить все новые Pod с track: canary.
kubectl -n demo get pods -l app=api,track=stable --show-labelsПустой результат этой команды подтверждает конкретную проблему выбора. Исправьте либо selector Service, либо метки в spec.template.metadata.labels Deployment в источнике конфигурации. Выбор зависит от задуманного маршрута трафика. Если стабильная и экспериментальная версии должны быть разделены, удаление track из selector соединит их и изменит поведение релиза. Сначала решите, какие реплики должны обслуживать этот адрес.
Сверяем выбор Pod и порт приложения
Для API с портом 8080 согласованная часть Service выглядит так. Это самостоятельный манифест Service; Pod с соответствующими метками и HTTP-приложением уже должны существовать в demo.
apiVersion: v1
kind: Service
metadata:
name: api
namespace: demo
spec:
selector:
app: api
track: stable
ports:
- name: http
port: 80
targetPort: 8080Клиент обращается к порту 80 Service. Целевой процесс должен слушать 8080 на доступном интерфейсе внутри Pod. Объявление containerPort в YAML само по себе не открывает сокет. Если вместо числа указан targetPort: http, проверьте одноимённый порт контейнера и его число. Переименование порта в шаблоне Pod без изменения Service создаёт другой класс ошибки, который исправление меток не устранит.
Адрес найден, но он не готов
В EndpointSlice адрес Pod может присутствовать с conditions.ready: false. Это отличается от отсутствия подходящих Pod: связь найдена, но экземпляр не прошёл условия готовности. Сопоставьте addresses с IP из get pods -o wide, затем откройте describe конкретного Pod. Причиной может быть readiness probe, стартующий sidecar или readiness gate. Перезапуск сетевого компонента не сделает HTTP-проверку приложения успешной.
kubectl -n demo get pods -l app=api,track=stable -o wide
kubectl -n demo describe pod api-7d9b8f-demoОбычный Service не направляет трафик к неготовым целям, но publishNotReadyAddresses меняет использование этого признака. Удаляющиеся Pod дополнительно имеют условия terminating и serving; для их разбора смотрите все conditions, а не только короткую колонку READY. Если Service вообще не имеет selector, автоматического выбора Pod по меткам нет: EndpointSlice могут создаваться другим контроллером или вручную, и расследовать нужно этот источник адресов.
Когда адреса верны, проверяем сетевой путь
Если нужный IP присутствует, ready=true и порт совпадает, переходите к запросу из того же клиентского Pod, где возникла ошибка. Если в контейнере уже есть curl, сравните прямое обращение к IP Pod и обращение через Service. Используйте найденный фактический IP вместо 10.0.0.25:
kubectl -n demo exec client -- curl --max-time 3 http://10.0.0.25:8080/readyz
kubectl -n demo exec client -- curl --max-time 3 http://api.demo.svc.cluster.local:80/readyzРаботает прямой адрес, но не Service — исследуйте обработку Service и ограничения политики для этого пути. Не работает даже прямой адрес — проверьте сокет приложения, NetworkPolicy и связь между узлами. Установка curl в рабочий контейнер меняет его среду; для воспроизводимого расследования лучше заранее иметь разрешённый диагностический образ.
Не начинайте с port-forward как единственной проверки Service. Перенаправление через API может выбрать конкретный Pod и создать путь, отличающийся от обычного клиентского трафика. Оно полезно для проверки обработчика приложения, но успешный ответ не доказывает работу ClusterIP. В записи расследования указывайте, откуда отправлен запрос и каким адресом пользовались: клиентский Pod → Pod IP и клиентский Pod → Service отвечают на разные вопросы.
После исправления меток посмотрите не только на появление адресов, но и на их принадлежность. Один слишком общий selector способен добавить чужой тестовый Pod с похожим app. Сверьте ownerReferences и ожидаемый набор реплик. Иначе доступность восстановится, но часть запросов начнёт обслуживать другой выпуск приложения, что значительно сложнее заметить по простому HTTP 200.
Зафиксируйте точку разрыва: selector не находил Pod, адрес был неготов или запрос терялся после выбора готовой цели. Тогда исправление имеет проверяемую причину, а следующий тайм-аут не придётся снова объяснять общим словом «сеть».
Ещё по теме
- DNS в Kubernetes отвечает медленно: проверяем CoreDNS, search и ndots
- Forbidden в Kubernetes: проверяем ServiceAccount, Role и RoleBinding
Продолжить изучение
Хотите увереннее разворачивать приложения и разбирать сбои в кластере? Сети, конфигурация, хранилища и обновления входят в программу «Kubernetes с нуля до PRO».
Продолжить на практике
- Kubernetes с нуля до PRO ↗
Разворачивайте и сопровождайте приложения в Kubernetes: от первого Pod до релизов, сетей, хранилищ, масштабирования и диагностики. Итоговый проект — сервис с базой данных, мониторингом и проверенным откатом.
