← До програми курсу

Модуль 7 / 19

Service, EndpointSlice, kube-proxy, CoreDNS і service discovery

KUBERNETES

Зміст
01

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 діагностуються незалежно
02

2. Теорія: Service і стабільна identity

Pod IP змінюються під час повторного створення, тому клієнти звертаються до Service. ClusterIP і DNS-ім’я стабільні протягом життя Service; selector визначає backend set, а EndpointSlice controller матеріалізує знайдені endpoints.

Service із selector не володіє Pods і не керує їх lifecycle. Зміна labels може додати чи прибрати Pod із трафіку без повторного створення Service. ClusterIP віртуальний: на ньому зазвичай немає окремого процесу-listener.

03

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.

04

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 до деяких спеціальних адрес задля безпеки.

05

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 або роботу застосунку.

06

6. Теорія: kube-proxy і data plane

kube-proxy спостерігає Services та EndpointSlices і програмує правила на кожному Node. В iptables mode пакет проходить NAT-правила до обраного endpoint; у nftables чи IPVS використовуються інші механізми. Конкретний режим перевіряють у конфігурації, а не вгадують.

kube-proxy не реалізує DNS і не виправляє порожній EndpointSlice. Connection tracking може зберігати вже обраний backend, тому послідовні запити не завжди демонструють рівномірний розподіл.

07

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
08

8. Лабораторна робота: backend і Service

Три nginx Pod повертають власний hostname. Окремий client Pod дозволяє перевіряти cluster DNS і data plane з правильного network context.

terminal
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=180s
terminal
cat > /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 yaml
09

9. Лабораторна робота: DNS і балансування

Порівняйте коротке ім’я, FQDN і звернення з іншого namespace. Кілька нових HTTP connections мають показати більше одного backend, хоча round-robin для кожного запиту не гарантується.

terminal
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-m07
10

10. Лабораторна робота: CoreDNS і kube-proxy

Зафіксуйте ClusterIP, фактичний proxy mode і host rules. На цьому стенді kube-proxy працює в iptables mode; в іншому середовищі команда пошуку правил має відповідати його реалізації.

terminal
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 wide
11

11. Діагностика: selector і targetPort

Спочатку selector перестає збігатися. DNS і ClusterIP зберігаються, але EndpointSlice втрачає backends і HTTP не працює. Після повернення selector зупиніть watch через Ctrl+C, коли addresses з’являться.

terminal
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 перевірка не проходить.

terminal
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

12. Контроль, критерії засвоєння та cleanup

  • Чому Service переживає заміну Pod?
  • Чи володіє Service обраними Pods?
  • Чим port відрізняється від targetPort?
  • Що зберігає EndpointSlice?
  • Чому DNS може працювати без endpoints?
  • Як звернутися до Service з іншого namespace?
  • Що робить kube-proxy?
  • Чому кілька запитів не гарантують ідеальний round-robin?
  • Чим headless Service відрізняється від ClusterIP?
terminal
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.