Модуль 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 віртуальний: на ньому зазвичай немає окремого процесу-listener.
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 забороняє proxy до деяких спеціальних адрес задля безпеки.
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.