Задача
В кластере развёрнут Deployment на 3 реплики и Service поверх него:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
version: v1
spec:
containers:
- name: api
image: api:1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080Все три пода в статусе Running. Но один из них ещё не прошёл readiness probe — приложение внутри прогревает кэш.
Вопрос: сколько адресов увидит kubectl get endpoints api?
Что кажется очевидным
«Три пода запущены, селектор совпадает — значит, три endpoints». Или, если человек уже обжигался: «два, потому что один не готов».
Второй ответ ближе, но неполон — и упускает главное.
Как это работает на самом деле
Service не знает про Deployment вообще. Между ними нет прямой связи. Работает это так:
- Service объявляет селектор по меткам (
app: api). - Отдельный контроллер (endpoints controller / endpointslice controller) следит за подами, чьи метки подходят под селектор.
- В список попадают не все подходящие поды, а только готовые — то есть те, у которых
Readyв статусеTrue.
Ключевой момент, который многие упускают: Running ≠ Ready. Под может быть запущен, но не готов принимать трафик — ровно для этого и существует readiness probe. Пока она не прошла, под остаётся вне списка endpoints и трафик к нему не идёт.
Проверить разницу:
kubectl get pods -l app=api
# NAME READY STATUS RESTARTS AGE
# api-7d4b8c9f5-abc12 1/1 Running 0 5m
# api-7d4b8c9f5-def34 1/1 Running 0 5m
# api-7d4b8c9f5-ghi56 0/1 Running 0 30s ← Running, но не ReadyКолонка READY показывает 0/1 — контейнер запущен, но probe не пройдена.
Ещё одна тонкость: selector шире, чем кажется
Селектор Service — это app: api, и он не привязан к конкретному Deployment. Любой под с такой меткой в том же namespace попадёт в endpoints: под из другого Deployment, оставшийся под старой версии, случайно запущенный отладочный под.
Это регулярный источник загадочных багов: «почему часть запросов уходит на старую версию» — потому что где-то живёт под с той же меткой.
Endpoints и EndpointSlice
Исторически список адресов хранился в объекте Endpoints — одном на Service. При сотнях подов это создавало проблему: любое изменение переписывало весь объект целиком и рассылалось всем узлам.
Поэтому появился EndpointSlice — список нарезается на части (по умолчанию до 100 адресов в срезе), и изменение затрагивает только один срез. Сейчас это основной механизм, а Endpoints остаётся ради совместимости.
kubectl get endpointslices -l kubernetes.io/service-name=apiОтвет
Два. В endpoints попадут только те поды, которые прошли readiness probe. Третий появится там автоматически, как только станет Ready.
Но точный ответ звучит осторожнее: столько, сколько в namespace есть готовых подов с меткой app: api — и это число не обязано совпадать с числом реплик Deployment.
Что с этим делать
- Всегда задавайте readiness probe для сервисов, принимающих трафик. Без неё под попадает в endpoints сразу после старта — и получает запросы до того, как приложение готово их обслуживать.
- Проверяйте endpoints при отладке. Если сервис «не отвечает», первым делом смотрите не логи приложения, а список адресов:Пустой список — проблема в селекторе или в готовности подов, а не в сети.BASH
kubectl get endpointslices -l kubernetes.io/service-name=api -o yaml - Делайте метки селектора специфичными.
app: apiслишком широко; добавляйте что-то вродеapp.kubernetes.io/instance, чтобы случайный под не попал в балансировку. - Не путайте readiness и liveness. Провал readiness убирает под из endpoints (трафик перестаёт идти), провал liveness перезапускает контейнер. Об этом — в задаче 7.
Дальше: Задача 2. Под удалён, трафик идёт.