Модуль 7 / 19
Service, EndpointSlice, kube-proxy, CoreDNS и service discovery
Содержание
1. Цели и результат модуля
После модуля вы сможете проследить запрос от DNS-имени Service до Ready Pod, различать Service и EndpointSlice, объяснять роль kube-proxy/CoreDNS и локализовать сетевой отказ на конкретном слое.
- Service даёт стабильную виртуальную identity динамическому набору Pods
- EndpointSlice публикует backend addresses, ports и readiness
- CoreDNS отвечает за service discovery, а не за HTTP-доступность
- kube-proxy программирует data plane, но обычно не проксирует пакеты как пользовательский процесс
- Service selector и targetPort диагностируются независимо
2. Теория: Service и стабильная identity
Pod IP меняются при пересоздании, поэтому клиенты обращаются к Service. ClusterIP и DNS-имя стабильны в пределах жизни Service; selector определяет backend set, а EndpointSlice controller материализует найденные endpoints.
Service с selector не владеет Pods и не управляет их lifecycle. Изменение labels может добавить или убрать Pod из трафика без пересоздания Service. ClusterIP виртуален: на нём обычно нет отдельного слушающего процесса.
3. Теория: port, targetPort и типы Service
port — порт Service для клиента, targetPort — порт backend Pod по номеру или имени, protocol по умолчанию TCP. Именованный targetPort связывает Service с семантическим именем containerPort и позволяет менять номер согласованно в Pod template.
- ClusterIP — доступ внутри cluster network
- NodePort — порт на адресах Node и строительный блок некоторых интеграций
- LoadBalancer — запрос внешнего балансировщика у поддерживаемого provider
- ExternalName — DNS CNAME без proxy и EndpointSlice
- Headless Service с clusterIP: None возвращает addresses endpoints напрямую
На bare-metal LoadBalancer не появляется автоматически без отдельной реализации. External exposure будет разобран в модуле 8.
4. Теория: EndpointSlice
EndpointSlice масштабируемо хранит addresses, topology hints, conditions и ports для Service. Label kubernetes.io/service-name связывает slice с Service. Старый Endpoints API ограничен и не должен быть основным источником для новых интеграций.
Conditions ready, serving и terminating позволяют data plane учитывать lifecycle endpoint. Для selector-less Service EndpointSlices создаются вручную или внешним controller; API Server запрещает проксирование к некоторым специальным адресам ради безопасности.
5. Теория: CoreDNS и имена
Обычный Service получает A/AAAA запись вида service.namespace.svc.cluster-domain. В том же namespace короткое имя дополняется search suffix из resolv.conf; из другого namespace используют service.namespace или FQDN.
Headless Service возвращает Pod/endpoint addresses вместо ClusterIP. Stateful discovery может добавлять записи для именованных Pods. DNS TTL и клиентское кэширование означают, что изменение записей не обязано быть мгновенно видно долгоживущему процессу.
Успешный nslookup доказывает только DNS. Он не доказывает наличие ready endpoints, корректный targetPort, NetworkPolicy или работу приложения.
6. Теория: kube-proxy и data plane
kube-proxy наблюдает Services и EndpointSlices и программирует правила на каждом Node. В iptables mode пакет проходит правила NAT к выбранному endpoint; в nftables или IPVS используются другие механизмы. Конкретный режим проверяют в конфигурации, а не угадывают.
kube-proxy не реализует DNS и не исправляет пустой EndpointSlice. Connection tracking может сохранять уже выбранный backend, поэтому последовательные запросы не всегда демонстрируют равномерное распределение.
7. Методические указания
- Проверяйте запрос из Pod-клиента, а не только с Node
- Разделяйте DNS, ClusterIP, EndpointSlice, Pod IP и application response
- Сравнивайте selector Service с фактическими labels
- Проверяйте port, targetPort, protocol и именованный containerPort
- Учитывайте readiness и terminating endpoints
- Не используйте Pod IP как постоянный адрес
- Не очищайте conntrack или не перезапускайте CoreDNS без evidence
- Проверяйте фактический proxy mode перед чтением host rules
8. Лабораторная работа: backend и Service
Три nginx Pod возвращают собственный hostname. Отдельный client Pod позволяет проверять cluster DNS и data plane из правильного network context.
mkdir -p /tmp/k8s-m07
cat > /tmp/k8s-m07/workload.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: k8s-m07
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: service-lab
template:
metadata:
labels:
app.kubernetes.io/name: service-lab
spec:
containers:
- name: nginx
image: nginx:1.29-alpine
command: ["sh", "-c"]
args:
- printf 'served-by=%s\n' "$HOSTNAME" > /usr/share/nginx/html/index.html; exec nginx -g 'daemon off;'
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 2
---
apiVersion: v1
kind: Pod
metadata:
name: client
namespace: k8s-m07
spec:
containers:
- name: client
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
EOF
kubectl wait --for=condition=Ready node --all --timeout=120s
kubectl create namespace k8s-m07
kubectl create namespace k8s-m07-client
kubectl apply --dry-run=server -f /tmp/k8s-m07/workload.yaml
kubectl apply -f /tmp/k8s-m07/workload.yaml
kubectl -n k8s-m07 rollout status deployment/web --timeout=180s
kubectl -n k8s-m07 wait --for=condition=Ready pod/client --timeout=180scat > /tmp/k8s-m07/service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: web
namespace: k8s-m07
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: service-lab
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
EOF
kubectl apply --dry-run=server -f /tmp/k8s-m07/service.yaml
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m07/service.yaml
kubectl -n k8s-m07 get service web -o wide
kubectl -n k8s-m07 get endpointslice -l kubernetes.io/service-name=web -o wide
kubectl -n k8s-m07 get endpointslice -l kubernetes.io/service-name=web -o yaml9. Лабораторная работа: DNS и балансировка
Сравните короткое имя, FQDN и обращение из другого namespace. Несколько новых HTTP connections должны показать более одного backend, хотя round-robin для каждого запроса не гарантируется.
kubectl -n k8s-m07 exec client -- cat /etc/resolv.conf
kubectl -n k8s-m07 exec client -- nslookup web.k8s-m07.svc.cluster.local
for attempt in $(seq 1 6); do
kubectl -n k8s-m07 exec client -- wget -qO- http://web
done
kubectl run client-cross -n k8s-m07-client --image=busybox:1.37 --restart=Never --command -- sleep 3600
kubectl -n k8s-m07-client wait --for=condition=Ready pod/client-cross --timeout=120s
kubectl -n k8s-m07-client exec client-cross -- nslookup web.k8s-m07.svc.cluster.local
kubectl -n k8s-m07-client exec client-cross -- wget -qO- http://web.k8s-m0710. Лабораторная работа: CoreDNS и kube-proxy
Зафиксируйте ClusterIP, фактический proxy mode и host rules. На этом стенде kube-proxy работает в iptables mode; в другом окружении команда поиска правил должна соответствовать его реализации.
cluster_ip=$(kubectl -n k8s-m07 get service web -o jsonpath='{.spec.clusterIP}')
printf 'Service ClusterIP: %s\n' "$cluster_ip"
kubectl -n kube-system get configmap kube-proxy -o jsonpath='{.data.config\.conf}'
kubectl -n kube-system get pods -l k8s-app=kube-proxy -o wide
sudo iptables-save -t nat | grep -F "$cluster_ip"
kubectl -n kube-system get configmap coredns -o yaml
kubectl -n kube-system get deployment coredns
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide11. Диагностика: selector и targetPort
Сначала selector перестаёт совпадать. DNS и ClusterIP сохраняются, но EndpointSlice теряет backends и HTTP не работает. После возврата selector остановите watch через Ctrl+C, когда addresses появятся.
kubectl -n k8s-m07 patch service web --type=merge \
-p '{"spec":{"selector":{"app.kubernetes.io/name":"missing"}}}'
kubectl -n k8s-m07 get pods --show-labels
kubectl -n k8s-m07 get service web -o jsonpath='selector={.spec.selector}{"\n"}'
kubectl -n k8s-m07 get endpointslice -l kubernetes.io/service-name=web -o yaml
kubectl -n k8s-m07 exec client -- wget -T 3 -qO- http://web || true
kubectl -n k8s-m07 patch service web --type=merge \
-p '{"spec":{"selector":{"app.kubernetes.io/name":"service-lab"}}}'
kubectl -n k8s-m07 get endpointslice -l kubernetes.io/service-name=web -wЗатем addresses остаются, но targetPort указывает на порт без listener. Это другой слой отказа: EndpointSlice не пуст, однако TCP/HTTP проверка не проходит.
kubectl -n k8s-m07 patch service web --type=merge \
-p '{"spec":{"ports":[{"name":"http","protocol":"TCP","port":80,"targetPort":81}]}}'
kubectl -n k8s-m07 get endpointslice -l kubernetes.io/service-name=web -o wide
kubectl -n k8s-m07 exec client -- wget -T 3 -qO- http://web || true
kubectl -n k8s-m07 patch service web --type=merge \
-p '{"spec":{"ports":[{"name":"http","protocol":"TCP","port":80,"targetPort":"http"}]}}'
kubectl -n k8s-m07 exec client -- wget -qO- http://webПорядок: resolv.conf/nslookup → Service/ports → EndpointSlice/conditions → Pod labels/readiness → прямой Pod IP → policies и node data plane → application logs.
12. Контроль, критерии освоения и cleanup
- Почему Service переживает замену Pod?
- Владеет ли Service выбранными Pods?
- Чем port отличается от targetPort?
- Что хранит EndpointSlice?
- Почему DNS может работать без endpoints?
- Как обратиться к Service из другого namespace?
- Что делает kube-proxy?
- Почему несколько запросов не гарантируют идеальный round-robin?
- Чем headless Service отличается от ClusterIP?
kubectl delete namespace k8s-m07 k8s-m07-client --wait=true
rm -rf /tmp/k8s-m07
kubectl get namespace k8s-m07 k8s-m07-client 2>&1 || trueМодуль освоен, если вы прослеживаете DNS → ClusterIP → EndpointSlice → Ready Pod, различаете пустой selector и неверный targetPort и восстанавливаете доступ минимальным patch.