🐳 Розділ 14 · Питання #11

Що таке Service в Kubernetes

Поди в Kubernetes ефемерні: вони часто перезапускаються, переміщуються на інші вузли і щоразу отримують абсолютно нову динамічну IP-адресу:


🟢 Junior Level

30-секундна відповідь

Service (Сервіс) — це абстрактний об’єкт Kubernetes, що надає єдину стабільну точку входу (віртуальну IP-адресу та DNS-ім’я) і балансувальник навантаження транспортного рівня (L4) для динамічної групи подів.

Поди в Kubernetes ефемерні: вони часто перезапускаються, переміщуються на інші вузли і щоразу отримують абсолютно нову динамічну IP-адресу:

  • Якби мікросервіси зверталися один до одного напряму за IP подів, будь-яке перезавантаження ламало б усю систему.
  • Service вирішує цю проблему: сервіс отримує постійну віртуальну адресу (ClusterIP), яка ніколи не змінюється протягом усього життєвого циклу сервісу.
  • Клієнти звертаються за стабільним іменем (наприклад, http://payment-service:80), а Kubernetes автоматично перенаправляє та балансує мережеві пакети між здоровими подами.

Наочна аналогія з життя

Уявіть кол-центр великого банку:

  • Оператори підтримки — це поди (вони йдуть на перерву, змінюються під час чергування, їхня кількість зростає в години пік).
  • Service — це єдиний номер гарячої лінії (наприклад, 0-800-100-000).
  • Клієнту не потрібно знати домашній телефон або прізвище конкретного чергового оператора. Клієнт набирає один спільний номер, а телефонна станція автоматично з’єднує його з будь-яким вільним оператором на лінії.

Базовий приклад маніфесту Service

apiVersion: v1
kind: Service
metadata:
  name: payment-service
spec:
  type: ClusterIP # Доступний лише всередині кластера
  selector:
    app: payment-service # Шукає всі поди з міткою app=payment-service
  ports:
  - name: http
    port: 80         # Порт, на якому Service слухає запити
    targetPort: 8080 # Порт всередині контейнера Pod (куди пересилати трафік)

Всередині кластера будь-який інший застосунок звертається до нього через DNS:

curl http://payment-service:80/api/v1/pay

🟡 Middle Level

Як Service знаходить поди: Селектори та Endpoints

Зв’язок між Service та подами є слабкозв’язаним і будується на мітках (Labels):

  1. У секції spec.selector сервіс вказує критерії пошуку (наприклад, app: payment-service).
  2. Kubernetes безперервно сканує поди з такими мітками.
  3. IP-адреси всіх подів, які успішно пройшли перевірку readinessProbe, автоматично заносяться в дочірній системний об’єкт — Endpoints (або EndpointSlice): ```yaml apiVersion: v1 kind: Endpoints metadata: name: payment-service subsets:
    • addresses:
      • ip: 10.244.1.15
      • ip: 10.244.2.34 ports:
      • port: 8080 ```
  4. Якщо под падає або зависає (readiness probe зазнає невдачі), контролер миттєво видаляє його IP зі списку Endpoints, і трафік припиняє на нього надходити.

Розв’язання доменних імен (CoreDNS)

Кожен створений Service автоматично реєструється у вбудованому DNS-сервері кластера (CoreDNS). Повне доменне ім’я (FQDN) має вигляд: \(\mathbf{<service\text{-}name>.<namespace>.svc.cluster.local}\)

  • У межах одного й того самого Namespace достатньо звернутися за коротким ім’ям: http://payment-service.
  • При виклику з іншого Namespace вказується двокомпонентне ім’я: http://payment-service.prod.

4 основні типи Service

| Тип | Де доступний | Для чого використовується | | :— | :— | :— | | ClusterIP (дефолт)| Лише всередині K8s кластера | Внутрішня міжсервісна взаємодія мікросервісів. | | NodePort | На виділеному порту кожного фізичного вузла (Node) | Локальне тестування, налагодження (діапазон 30000–32767). | | LoadBalancer | У публічному інтернеті | Створює зовнішній балансувальник хмарного провайдера (AWS NLB, GCP LB). | | ExternalName | Всередині кластера як CNAME-аліас | Прокидання зовнішніх баз даних (наприклад, db.rds.amazonaws.com) у DNS K8s. |


🔴 Senior Level

Як працює ClusterIP під капотом: Магія kube-proxy

Senior-інженер зобов’язаний розуміти, що ClusterIP — це фіктивна віртуальна IP-адреса (Virtual IP / VIP).

  • У кластері НЕМАЄ фізичного мережевого інтерфейсу, якому була б призначена ця IP-адреса.
  • За обробку пакетів відповідає агент kube-proxy, що функціонує на кожному вузлі в одному з трьох режимів:
[ Пакет на ClusterIP:80 ]
           │
           ▼
[ Ядро Linux: Netfilter / iptables / IPVS ]
           │ (DNAT підміна IP призначення на льоту)
           ▼
[ Реальний IP пода: 10.244.2.34:8080 ]

1. Режим iptables (за замовчуванням у більшості кластерів)

  • kube-proxy не перехоплює трафік сам. Він лише транслює конфігурацію сервісів у ланцюжки правил iptables ядра Linux у таблиці nat.
  • Для балансування використовується модуль ядра statistic з імовірнісним механізмом (Random Probability):
    • Для 3 подів перше правило спрацьовує з імовірністю $1/3$, друге — $1/2$, третє забирає залишок.
  • Проблема масштабування: додавання та перевірка правил вимагає лінійного обходу $O(N)$. При $10\,000+$ сервісів перерахунок правил починає утилізувати до 100% CPU одного ядра процесора.

2. Режим IPVS (IP Virtual Server)

  • Використовує транспортний L4-балансувальник, вбудований безпосередньо в ядро Linux.
  • Зберігає відповідності в хеш-таблицях з константною складністю пошуку $O(1)$.
  • Підтримує складні алгоритми розподілу: round-robin, least connections, source hashing. Ідеальний для великих кластерів під високим навантаженням.

3. eBPF (Cilium CNI)

  • Повністю виключає kube-proxy і стек iptables/conntrack.
  • Програми eBPF перехоплюють пакети безпосередньо на мережевому сокеті ядра через інтерфейс XDP (eXpress Data Path), забезпечуючи мінімально можливу затримку (Zero-overhead networking).

EndpointSlice API: Захист etcd від Watch Storm

У ранніх версіях Kubernetes (до 1.21) усі IP-адреси подів сервісу зберігалися в одному монолітному об’єкті Endpoints.

  • Якщо у сервісу 5000 подів, то при падінні лише одного пода пересилався весь об’єкт розміром 2 МБ на всі вузли кластера.
  • Це спричиняло перевантаження API-сервера та etcd (Watch Storm).
  • Сучасне рішення — EndpointSlice: K8s автоматично нарізає список ендпоінтів на компактні слайси по 100 адрес у кожному. При зміні одного пода оновлюється лише один слайс розміром у пару кілобайт.

Headless Service (clusterIP: None)

Якщо у специфікації вказати clusterIP: None:

  • Kubernetes НЕ виділяє віртуальний VIP і не налаштовує правила балансування в kube-proxy.
  • При DNS-запиті до сервісу CoreDNS повертає повний список A-записів з реальними IP-адресами всіх здорових подів.
  • Застосування: розподілені Stateful-системи (Kafka, Cassandra, MongoDB), де Java-клієнт повинен сам керувати з’єднаннями до конкретних нод-лідерів і реплік.

4 Tricky Questions

1. Чому спроба надіслати ICMP-запит ping <ClusterIP> зсередини пода завжди завершується 100% втратою пакетів, хоча HTTP-запити через curl працюють миттєво?

Відповідь: Віртуальна IP-адреса сервісу (ClusterIP) існує виключно як правило підміни мережевих адрес (DNAT) у підсистемі ядра Netfilter (iptables або IPVS). Ці правила налаштовані суворо для протоколів TCP та UDP на певних портах. Протокол ICMP (на якому працює утиліта ping) не має портів і не обробляється правилами трансляції сервісу. Оскільки мережевого інтерфейсу з таким IP фізично не існує в природі, ядру немає кому доставити ICMP Echo Request, і пакет мовчки відкидається.


2. Як налаштувати Service в Kubernetes для підключення до зовнішньої СУБД Oracle/PostgreSQL, розташованої поза кластером, використовуючи внутрішній Service Discovery?

Відповідь: Створюється Service БЕЗ селектора (selector), і вручну оголошується об’єкт Endpoints з тим самим ім’ям:

apiVersion: v1
kind: Service
metadata:
  name: external-oracle-db
spec:
  ports:
  - port: 1521
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-oracle-db
subsets:
  - addresses:
      - ip: 192.168.10.50 # IP фізичного сервера БД поза K8s
    ports:
      - port: 1521

Java-застосунок всередині кластера використовує стабільне ім’я jdbc:oracle:thin:@external-oracle-db:1521/XEPDB1. Якщо фізичний IP бази даних зміниться, достатньо змінити маніфест Endpoints без перезапуску та переконфігурації мікросервісів.


3. У чому відмінність port від targetPort у специфікації Service, і що станеться, якщо targetPort пропущено?

Відповідь:

  • port: це TCP/UDP порт, який відкриває сам Service на своєму віртуальному ClusterIP. Саме до цього порту звертаються клієнти всередині кластера.
  • targetPort: це реальний порт у контейнері пода, на якому слухає процес застосунку (наприклад, порт 8080 вбудованого Tomcat у Spring Boot). Ядро Linux транслює запити з port на targetPort.
  • Якщо targetPort не вказано: Kubernetes за замовчуванням вважає, що значення targetPort суворо дорівнює значенню port.

4. Чому використання властивості sessionAffinity: ClientIP у Service може зламати балансування навантаження в мікросервісній архітектурі?

Відповідь: При ввімкненні sessionAffinity: ClientIP kube-proxy скеровує всі запити від однієї IP-адреси клієнта на один і той самий фіксований под протягом заданого тайм-ауту. В архітектурі з API Gateway (Ingress-контролер, Spring Cloud Gateway) абсолютно всі вхідні запити надходять до сервісу з однієї й тієї самої IP-адреси шлюзу. У результаті sessionAffinity прив’яже весь вхідний клієнтський трафік до одного-єдиного пода, перевантажуючи його до падіння через вичерпання CPU або OOM, у той час як решта подів сервісу простоюватимуть із нульовим навантаженням.


🎯 Шпаргалка для інтерв’ю

Головне про Service

  • Сутність: стабільний мережевий фасад (L4 VIP + DNS) для ефемерних подів.
  • Пошук подів: через spec.selector за мітками (Labels).
  • Зв’язуючий об’єкт: Endpoints / EndpointSlice (зберігає IP живих подів).
  • DNS: доступний за ім’ям service-name (у тому ж namespace) або FQDN service.namespace.svc.cluster.local.
  • Реалізація: агент kube-proxy через правила ядра iptables (за замовчуванням) або IPVS ($O(1)$ для високих навантажень).
  • Headless: clusterIP: None — CoreDNS повертає IP усіх подів напряму (для StatefulSet/Kafka).

Типи Service

| Тип Service | Область видимості | Призначення | | :— | :— | :— | | ClusterIP | Лише всередині кластера K8s | Стандарт за замовчуванням для міжсервісного спілкування | | NodePort | Порт 30000–32767 на всіх вузлах | Доступ без хмарного балансувальника | | LoadBalancer| Інтернет (зовнішній хмарний IP)| Продакшен точка входу через хмарний LB | | ExternalName| Внутрішній CNAME редирект | Підключення зовнішніх систем за доменним ім’ям |

Червоні прапорці на співбесіді (Чого говорити не можна)

  • ❌ «Service — це окремий контейнер з Nginx, який працює і проксує трафік» — Service це абстракція, реалізована через правила ядра iptables/IPVS.
  • ❌ «ClusterIP можна пропінгувати утилітою ping» — віртуальний IP не відповідає на ICMP, тільки TCP/UDP.
  • ❌ «Service балансує HTTP-трафік за URL та заголовками» — Service працює на L4 (TCP/IP). Маршрутизацією на L7 (HTTP-шляхи) займається Ingress.

Пов’язані питання