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

Які типи Service існують (ClusterIP, NodePort, LoadBalancer)

У Kubernetes існує 4 основні типи Service, які визначають рівень мережевої доступності застосунку (чи буде він видимий лише всередині кластера, чи доступний користувачам з інтер...


🟢 Junior Level

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

У Kubernetes існує 4 основні типи Service, які визначають рівень мережевої доступності застосунку (чи буде він видимий лише всередині кластера, чи доступний користувачам з інтернету):

  1. ClusterIP (за замовчуванням): сервіс отримує стабільну віртуальну IP-адресу, доступну виключно всередині кластера. Зовнішній трафік з інтернету сюди потрапити не може. Використовується для 95% мікросервісів (взаємодія бекенду з базою даних, кешем, брокерами повідомлень).
  2. NodePort: відкриває виділений статичний порт (з діапазону 30000–32767) на кожному фізичному або віртуальному сервері (Node) кластера. Дозволяє звертатися до застосунку ззовні за адресою http://<IP-будь-якої-ноди>:NodePort.
  3. LoadBalancer: автоматично замовляє у зовнішнього хмарного провайдера (AWS, GCP, Azure) повноцінний мережевий балансувальник навантаження з публічною IP-адресою.
  4. ExternalName: спеціальний тип без селектора та без проксіювання трафіку. Повертає DNS CNAME-запис, перенаправляючи клієнтські запити на зовнішній домен (наприклад, db.external-provider.com).

Наочна аналогія «Мотрійка»

Архітектурно типи сервісів вкладені один в одного немов мотрійка:

  • ClusterIP — найменша внутрішня мотрійка (базовий маршрут до подів).
  • NodePort — середня мотрійка: він огортає ClusterIP, прокидаючи порт на хости.
  • LoadBalancer — найбільша зовнішня мотрійка: він замовляє зовнішній балансувальник, спрямовує трафік на NodePort, який передає його на ClusterIP, а той балансує навантаження на Pod!

Зведена таблиця для швидкого розуміння

| Тип сервісу | Зовнішній IP | Доступний з інтернету | Основне призначення | | :— | :—: | :—: | :— | | ClusterIP | ❌ Ні | ❌ Ні | Міжсервісна взаємодія (Back $\to$ DB) | | NodePort | ❌ Ні (порт ноди) | 🟡 Так (за IP нод) | Тестові стенди, пряме підключення для налагодження | | LoadBalancer | 🟢 Так (Public IP) | 🟢 Так | Продакшен-доступ для зовнішніх клієнтів | | ExternalName | ❌ Ні (DNS CNAME) | ❌ Ні | Псевдонім для зовнішніх баз даних чи API |


🟡 Middle Level

Детальна конфігурація та специфікація

1. ClusterIP (Внутрішньокластерний стандарт)

apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  type: ClusterIP # Можна опустити, призначається за замовчуванням
  selector:
    app: order-service
  ports:
  - port: 80         # Порт сервісу
    targetPort: 8080 # Порт контейнера Java Spring Boot

2. NodePort (Доступ через порти хостів)

apiVersion: v1
kind: Service
metadata:
  name: debug-service
spec:
  type: NodePort
  selector:
    app: order-service
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 31234 # Діапазон: 30000–32767 (якщо опустити, K8s обере випадковий)

Запит на адресу 192.168.1.10:31234 перенаправляється на сервіс, навіть якщо на цій конкретній ноді взагалі не запущено жодного пода order-service.

3. LoadBalancer (Інтеграція з хмарною інфраструктурою)

apiVersion: v1
kind: Service
metadata:
  name: public-gateway
spec:
  type: LoadBalancer
  selector:
    app: api-gateway
  ports:
  - port: 443
    targetPort: 8443

Cloud Controller Manager перехоплює подію створення такого сервісу та через хмарний API розгортає AWS NLB або Google Cloud Network Load Balancer.

4. ExternalName (Зовнішній CNAME редирект)

apiVersion: v1
kind: Service
metadata:
  name: postgres-db
spec:
  type: ExternalName
  externalName: mydb-cluster.rds.amazonaws.com # CNAME запис у CoreDNS

Java-застосунок підключається до jdbc:postgresql://postgres-db:5432/mydb, а CoreDNS повертає CNAME на хмарну інстанцію AWS RDS.


Архітектурний вибір: LoadBalancer vs Ingress

У продакшені кожен хмарний LoadBalancer коштує грошей (від 15 до 30 доларів щомісяця за кожен екземпляр в AWS/GCP):

  • Антипатерн: створювати 20 мікросервісів з типом LoadBalancer ($20 \times $25 = $500$ щомісяця лише за мережеві балансувальники).
  • Best Practice (Патерн Ingress):
    1. Створюється рівно один сервіс типу LoadBalancer, спрямований в інтернет.
    2. За ним працює Ingress Controller (Nginx, Traefik, Envoy).
    3. Усі інші мікросервіси оголошуються як дешеві та безпечні ClusterIP.
    4. Ingress розподіляє вхідний HTTP/HTTPS трафік за шляхами (/api/orders, /api/users) на внутрішні ClusterIP сервіси.

🔴 Senior Level

Політика маршрутизації: externalTrafficPolicy: Cluster vs Local

При використанні сервісів типів NodePort та LoadBalancer критично важливо розуміти поведінку параметра spec.externalTrafficPolicy:

[ externalTrafficPolicy: Cluster ]                 [ externalTrafficPolicy: Local ]
          Клієнт                                             Клієнт
             │                                                  │
             ▼                                                  ▼
     [ Node 1 (без пода) ]                             [ Node 1 (без пода) ]
             │ (SNAT + Extra Hop)                               ❌ Трафік скинуто!
             ▼
     [ Node 2 (з подом) ]                              [ Node 2 (з подом) ]
                                                                🟢 Обробка без SNAT!

1. externalTrafficPolicy: Cluster (за замовчуванням)

  • Трафік, який надійшов на будь-яку ноду кластера, може бути пересланий через внутрішню оверлейну мережу на зовсім іншу ноду, де реально запущений под.
  • Переваги: ідеальне рівномірне балансування навантаження між подами.
  • Недоліки:
    1. Зайвий мережевий перехід (Extra Network Hop): збільшує мережеву затримку (latency).
    2. Втрата Client IP (SNAT): ядро виконує підміну вихідної адреси (Source NAT). Java-застосунок бачить IP-адресу внутрішньої ноди замість реального публічного IP клієнта, що ламає аудит безпеки та rate-limiting.

2. externalTrafficPolicy: Local

  • Трафік спрямовується лише на поди, які працюють на цій же самій ноді. Якщо подів на ноді немає, трафік відкидається.
  • Переваги:
    1. Зберігається реальний вихідний IP клієнта (Source IP Preservation) без SNAT.
    2. Відсутній другий міжсерверний hop (мінімальні затримки).
  • Недоліки: ризик нерівномірного завантаження подів при перекосі трафіку між нодами. Хмарний балансувальник зобов’язаний надсилати health-перевірки на службовий порт healthCheckNodePort.

Приватні хмарні мережі (Internal Load Balancer)

За замовчуванням сервіс type: LoadBalancer замовляє публічний IP. Для створення балансувальника, доступного лише всередині корпоративної VPC, використовують анотації хмарного провайдера:

metadata:
  name: internal-service
  annotations:
    # Для AWS:
    service.beta.kubernetes.io/aws-load-balancer-internal: "true"
    # Для GCP:
    networking.gke.io/load-balancer-type: "Internal"

On-Premise балансування: MetalLB

У локальних дата-центрах (Bare-Metal K8s) немає API хмарного провайдера, і сервіс LoadBalancer назавжди зависає у статусі <pending>. Для розв’язання цієї проблеми розгортають MetalLB:

  1. Layer 2 Mode (ARP/NDP): один із серверів кластера відповідає на ARP-запити для виділеного віртуального IP з пулу. При падінні сервера інший вузол перехоплює IP через Gratuitous ARP.
  2. BGP Mode: вузли кластера встановлюють BGP-сесії з фізичними дата-центровими маршрутизаторами (Cisco, Juniper) та анонсують маршрути до сервісів із підтримкою Equal-Cost Multi-Path (ECMP) балансування.

4 Tricky Questions

1. Чому при створенні сервісу type: LoadBalancer Kubernetes все одно виділяє для нього NodePort і ClusterIP? Чи можна вимкнути виділення NodePort?

Відповідь: Архітектурно хмарний балансувальник (наприклад, класичний AWS ELB) не має прямої маршрутизації в оверлейну мережу подів (10.244.0.0/16). Він приймає зовнішній трафік і пересилає його на фізичні IP серверів кластера на виділений NodePort. Далі kube-proxy через правила ClusterIP маршрутизує пакет до пода. Вимкнення NodePort: починаючи з Kubernetes 1.20, доступна властивість spec.allocateLoadBalancerNodePorts: false. Якщо хмарний контролер підтримує пряму маршрутизацію пакетів у поди (наприклад, AWS VPC CNI з TargetGroupBinding у режимі IP mode), виділення портів NodePort можна вимкнути, заощаджуючи пул портів кластера.


2. Чому звернення до зовнішнього HTTPS-сервісу через type: ExternalName часто падає з помилкою валідації SSL/TLS сертифіката в Java (SSLHandshakeException)?

Відповідь: Тип ExternalName працює виключно на рівні DNS: він не проксіює TCP-з’єднання, а лише повертає клієнту CNAME-запис (наприклад, mydb.internal $\to$ custom-service.eu-west-1.rds.amazonaws.com). Коли Java-клієнт встановлює захищене TLS-з’єднання, він відправляє заголовок SNI (Server Name Indication) з початковим ім’ям хоста, до якого підключався (mydb.internal). Віддалений сервер повертає SSL-сертифікат, виписаний на домен *.rds.amazonaws.com. JVM порівнює запитане ім’я mydb.internal з маскою сертифіката, фіксує невідповідність імен і викидає фатальний виняток javax.net.ssl.SSLPeerUnverifiedException: Certificate for <mydb.internal> doesn't match any of the subject alternative names.


3. Як директива externalTrafficPolicy: Local захищає від втрати Client IP і чому вона вимагає налаштування healthCheckNodePort?

Відповідь: У стандартному режимі Cluster мережевий пакет, що надійшов на Node 1, пересилається на Node 2. Щоб Node 2 знала, куди надіслати пакет-відповідь, ядро на Node 1 виконує Source NAT (SNAT), замінюючи реальний IP клієнта на IP-адресу Node 1 (клієнтський IP втрачається). У режимі Local ядро забороняє пересилання між нодами: пакет передається поду лише локально, завдяки чому SNAT не потрібен, і справжній Client IP зберігається. Але якщо на Node 1 немає подів цього сервісу, пакет буде скинуто. Щоб хмарний балансувальник знав, на які саме ноди можна надсилати трафік, Kubernetes автоматично відкриває службовий порт healthCheckNodePort. Балансувальник безперервно опитує цей порт: ноди з подами відповідають HTTP 200, а ноди без подів — HTTP 503.


4. Чи можна всередині кластера звернутися до сервісу NodePort за його внутрішнім ClusterIP, ігноруючи виділений порт 30000–32767?

Відповідь: Так, безумовно. Кожен сервіс типу NodePort одночасно є повноцінним сервісом типу ClusterIP. Всередині кластера інші мікросервіси можуть звертатися до нього за його DNS-іменем та стандартним внутрішнім портом spec.ports.port (наприклад, http://my-service:80), оминаючи громіздкі зовнішні порти nodePort (31234). Виділений порт nodePort відкривається лише для вхідного трафіку із зовнішніх фізичних інтерфейсів нод.


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

Головне про типи Service

  • ClusterIP: дефолтний віртуальний IP, доступний лише всередині кластера.
  • NodePort: відкриває порт 30000–32767 на всіх серверах; трафік прямує через NodePort $\to$ ClusterIP $\to$ Pod.
  • LoadBalancer: замовляє зовнішній IP у хмарі (AWS/GCP); в on-premise вимагає MetalLB.
  • ExternalName: чистий DNS CNAME аліас на зовнішню систему (без віртуального IP і без селектора).
  • Ingress vs LoadBalancer: 1 Ingress (1 LoadBalancer) обслуговує десятки сервісів ClusterIP, заощаджуючи бюджет.
  • externalTrafficPolicy: Local: зберігає реальний Client IP і прибирає зайвий hop, але вимикає міжсерверне балансування.

Порівняння властивостей мережевого доступу

| Тип | Виділяє ClusterIP? | Виділяє NodePort? | Вимагає інтеграції з хмарою? | | :— | :—: | :—: | :—: | | ClusterIP | 🟢 Так | ❌ Ні | ❌ Ні | | NodePort | 🟢 Так | 🟢 Так | ❌ Ні | | LoadBalancer | 🟢 Так | 🟢 Так (за замовчуванням)| 🟢 Так (Cloud Provider / MetalLB) | | ExternalName | ❌ Ні | ❌ Ні | ❌ Ні (лише CoreDNS) |

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

  • ❌ «У продакшені ми для кожного бекенд-сервісу створюємо LoadBalancer з публічним IP» — колосальні зайві витрати та діра в безпеці; потрібен Ingress + ClusterIP.
  • ❌ «NodePort відкриває порт лише на тому сервері, де фізично запущений под» — NodePort відкривається на ВСІХ серверах кластера без винятку.
  • ❌ «ExternalName вміє балансувати TCP-пакети на зовнішню БД» — ExternalName це лише DNS-аліас CNAME, трафік прямує безпосередньо від клієнта до зовнішнього хоста.

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