Перейти до вмісту

Модуль 5.1: Сервіси

Hands-On Lab Available
K8s Cluster intermediate 30 min
Launch Lab ↗

Opens in Killercoda in a new tab

Складність: [MEDIUM] — Основна концепція мережі, кілька типів для розуміння

Час на виконання: 45–55 хвилин

Передумови: Модуль 1.1 (Поди), Модуль 2.1 (Деплойменти), розуміння основ мережі


Що ви зможете робити

Розділ «Що ви зможете робити»

Після завершення цього модуля ви зможете:

  • Створити Сервіси ClusterIP, NodePort та LoadBalancer для відкриття доступу до застосунків
  • Діагностувати проблеми з’єднання Сервісів за допомогою перевірки точок доступу, роздільної здатності DNS та перевірки портів
  • Пояснити як Сервіси використовують селектори міток для маршрутизації трафіку до правильних Підів
  • Порівняти типи Сервісів та обрати відповідний для внутрішніх та зовнішніх паттернів доступу

Чому цей модуль важливий

Розділ «Чому цей модуль важливий»

Сервіси забезпечують стабільну мережу для подів. Оскільки поди ефемерні й отримують нові IP при перестворенні, вам потрібні Сервіси для стабільного доступу до ваших застосунків. Сервіси — це основа того, як застосунки спілкуються в Kubernetes.

Іспит CKAD перевіряє:

  • Створення Сервісів (ClusterIP, NodePort, LoadBalancer)
  • Розуміння виявлення Сервісів
  • Зневадження зʼєднання Сервісів
  • Роботу з точками доступу (endpoints)

Аналогія з телефонним довідником

Сервіси — це як корпоративний телефонний довідник. Працівники (поди) приходять і йдуть, змінюють робочі місця (IP), але внутрішній номер відділу (Сервіс) залишається незмінним. Коли ви телефонуєте у «Відділ продажів» (назва Сервісу), система направляє виклик тому, хто зараз працює. Довідник (DNS) перетворює імена на номери, а комутатор (kube-proxy) маршрутизує виклик.


Доступ лише зсередини кластера:

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP # Типовий, можна не вказувати
selector:
app: my-app
ports:
- port: 80 # Порт Сервісу
targetPort: 8080 # Порт контейнера
Terminal window
# Створити імперативно
k expose deployment my-app --port=80 --target-port=8080
# Доступ зсередини кластера
curl http://my-service:80
curl http://my-service.default.svc.cluster.local:80

Відкриває на IP кожного вузла на статичному порті:

apiVersion: v1
kind: Service
metadata:
name: my-nodeport
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80 # Порт Сервісу (ClusterIP)
targetPort: 8080 # Порт контейнера
nodePort: 30080 # Порт вузла (30000-32767)
Terminal window
# Створити імперативно
k expose deployment my-app --type=NodePort --port=80 --target-port=8080
# Доступ ззовні кластера
curl http://<node-ip>:30080

Створює зовнішній балансувальник навантаження (хмарні середовища):

apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
Terminal window
# Створити імперативно
k expose deployment my-app --type=LoadBalancer --port=80 --target-port=8080
# Отримати зовнішню IP
k get svc my-loadbalancer
# Стовпець EXTERNAL-IP показує IP балансувальника навантаження

Відображає на зовнішнє DNS-імʼя (без проксіювання):

apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: database.example.com

Виявлення Сервісів

Розділ «Виявлення Сервісів»

Kubernetes створює DNS-записи для Сервісів:

<назва-сервісу>.<простір-імен>.svc.cluster.local
DNS-імʼяЩо резолвить
my-serviceТой самий простір імен
my-service.defaultПростір імен default
my-service.default.svcПростір імен default, svc
my-service.default.svc.cluster.localПовний FQDN

Поди отримують змінні середовища для Сервісів, що існували на момент запуску пода:

Terminal window
# Всередині пода
env | grep MY_SERVICE
# MY_SERVICE_SERVICE_HOST=10.96.0.1
# MY_SERVICE_SERVICE_PORT=80

┌─────────────────────────────────────────────────────────────┐
│ Типи Сервісів │
├─────────────────────────────────────────────────────────────┤
│ │
│ ClusterIP (лише внутрішній) │
│ ┌─────────────────────────────────────┐ │
│ │ cluster.local:80 ──► Під:8080 │ │
│ │ ──► Під:8080 │ │
│ │ ──► Під:8080 │ │
│ └─────────────────────────────────────┘ │
│ │
│ NodePort (ClusterIP + доступ через вузол) │
│ ┌─────────────────────────────────────┐ │
│ │ <IP-вузла>:30080 ──► ClusterIP:80 ──► Поди │
│ └─────────────────────────────────────┘ │
│ │
│ LoadBalancer (NodePort + зовнішній БН) │
│ ┌─────────────────────────────────────┐ │
│ │ <Зовн-IP>:80 ──► NodePort ──► ClusterIP ──► Поди │
│ └─────────────────────────────────────┘ │
│ │
│ Потік портів Сервісу: │
│ ┌──────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Зовнішній ──► nodePort ──► port ──► targetPort │ │
│ │ :80 :30080 :80 :8080 │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

Селектори та точки доступу

Розділ «Селектори та точки доступу»

Як Сервіси знаходять Поди

Розділ «Як Сервіси знаходять Поди»

Сервіси використовують селектори міток для пошуку подів:

# Сервіс
spec:
selector:
app: my-app
tier: frontend
# Під (має відповідати ВСІМ міткам)
metadata:
labels:
app: my-app
tier: frontend

Точки доступу (Endpoints)

Розділ «Точки доступу (Endpoints)»

Точки доступу створюються та оновлюються автоматично:

Terminal window
# Переглянути точки доступу
k get endpoints my-service
# NAME ENDPOINTS AGE
# my-service 10.244.0.5:8080,10.244.0.6:8080 5m
# Describe показує IP подів
k describe endpoints my-service

Немає відповідних подів?

Розділ «Немає відповідних подів?»

Якщо селектор не збігається з жодним подом:

Terminal window
k get endpoints my-service
# NAME ENDPOINTS AGE
# my-service <none> 5m

Безголові Сервіси (Headless)

Розділ «Безголові Сервіси (Headless)»

Для прямого виявлення подів без балансування навантаження:

apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None # Робить безголовим
selector:
app: my-app
ports:
- port: 80

DNS повертає всі IP подів замість IP Сервісу:

Terminal window
# Повертає кілька A-записів (один на кожен під)
nslookup headless-svc.default.svc.cluster.local

Випадки використання: StatefulSets, бази даних, виявлення пірів.


Багатопортові Сервіси

Розділ «Багатопортові Сервіси»
apiVersion: v1
kind: Service
metadata:
name: multi-port
spec:
selector:
app: my-app
ports:
- name: http # Назва обовʼязкова для багатопортових
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443

Спорідненість сесій

Розділ «Спорідненість сесій»

Маршрутизувати того самого клієнта до того самого пода:

apiVersion: v1
kind: Service
metadata:
name: sticky-service
spec:
selector:
app: my-app
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
ports:
- port: 80

Terminal window
# Створити Сервіс
k expose deployment NAME --port=80 --target-port=8080
k expose deployment NAME --type=NodePort --port=80
k expose deployment NAME --type=LoadBalancer --port=80
# Переглянути Сервіси
k get svc
k describe svc NAME
# Переглянути точки доступу
k get endpoints NAME
k get ep NAME
# Зневадити DNS
k run tmp --image=busybox --rm -it --restart=Never -- nslookup my-service
# Перевірити зʼєднання
k run tmp --image=busybox --rm -it --restart=Never -- wget -qO- my-service:80

  • kube-proxy насправді не проксіює трафік. Попри свою назву, він налаштовує правила iptables/IPVS. Трафік йде напряму від джерела до цільового пода.

  • Сервіси існують на рівні всього кластера, хоча вони належать простору імен. DNS-імʼя включає простір імен, але підлеглий ClusterIP працює між просторами імен.

  • NodePort використовує ВСІ вузли. Навіть вузли без цільових подів перенаправлять трафік до правильного пода.

  • Діапазон портів 30000–32767 можна налаштувати через прапорець --service-node-port-range kube-apiserver.


ПомилкаЧим це шкодитьРішення
Селектор не збігається з мітками подівСервіс не має точок доступуk get ep для перевірки, виправити мітки
Неправильний targetPortЗʼєднання відхиленоМає збігатися з портом, на якому слухає контейнер
Використання IP пода замість СервісуЛамається при перезапуску подаЗавжди використовуйте імʼя/IP Сервісу
Забули простір імен у DNSНе можна дістатися до СервісуВикористовуйте svc.namespace або повний FQDN
NodePort без правила файрволуНемає доступу ззовніВідкрити порт вузла у хмарному файрволі

  1. Яка різниця між port і targetPort у Сервісі?

    Відповідь `port` — це порт Сервісу (до якого підключаються клієнти). `targetPort` — це порт контейнера (куди перенаправляється трафік). Приклад: Сервіс слухає на 80, перенаправляє на порт 8080 контейнера.
  2. Як дізнатися, до яких подів маршрутизує Сервіс?

    Відповідь `kubectl get endpoints <назва-сервісу>` показує IP та порти подів, до яких маршрутизує Сервіс.
  3. Що станеться, якщо селектор Сервісу не збігається з жодним подом?

    Відповідь Сервіс існує, але не має точок доступу. Зʼєднання не працюватимуть. `kubectl get endpoints` покаже ``.
  4. Як поди у просторі імен A можуть звернутися до Сервісу у просторі імен B?

    Відповідь Використовуйте DNS-імʼя з простором імен: `service-name.namespace-b` або повний FQDN `service-name.namespace-b.svc.cluster.local`.

Завдання: Створити та протестувати різні типи Сервісів.

Підготовка:

Terminal window
# Створити деплоймент
k create deployment web --image=nginx --replicas=3
# Дочекатися подів
k wait --for=condition=Ready pod -l app=web --timeout=60s

Частина 1: Сервіс ClusterIP

Terminal window
# Створити ClusterIP сервіс
k expose deployment web --port=80 --target-port=80
# Перевірити точки доступу
k get endpoints web
# Тест зсередини кластера
k run test --image=busybox --rm -it --restart=Never -- wget -qO- web:80
# Перевірити DNS
k run test --image=busybox --rm -it --restart=Never -- nslookup web.default.svc.cluster.local

Частина 2: Сервіс NodePort

Terminal window
# Видалити ClusterIP сервіс
k delete svc web
# Створити NodePort сервіс
k expose deployment web --type=NodePort --port=80 --target-port=80
# Отримати призначений NodePort
k get svc web -o jsonpath='{.spec.ports[0].nodePort}'
echo
# Тест (якщо є доступ до вузла)
# curl http://<node-ip>:<nodeport>

Частина 3: Зневадження відсутніх точок доступу

Terminal window
# Створити сервіс з неправильним селектором
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Service
metadata:
name: broken-svc
spec:
selector:
app: wrong-label
ports:
- port: 80
EOF
# Перевірити точки доступу (мають бути порожніми)
k get endpoints broken-svc
# Виправити патчем селектора
k patch svc broken-svc -p '{"spec":{"selector":{"app":"web"}}}'
# Перевірити, що точки доступу тепер є
k get endpoints broken-svc

Прибирання:

Terminal window
k delete deployment web
k delete svc web broken-svc

Вправа 1: Створити ClusterIP Сервіс (Ціль: 1 хвилина)

Розділ «Вправа 1: Створити ClusterIP Сервіс (Ціль: 1 хвилина)»
Terminal window
k create deployment drill1 --image=nginx
k expose deployment drill1 --port=80
k get svc drill1
k get ep drill1
k delete deploy drill1 svc drill1

Вправа 2: Створити NodePort Сервіс (Ціль: 2 хвилини)

Розділ «Вправа 2: Створити NodePort Сервіс (Ціль: 2 хвилини)»
Terminal window
k create deployment drill2 --image=nginx
k expose deployment drill2 --type=NodePort --port=80 --target-port=80
# Отримати NodePort
k get svc drill2 -o jsonpath='{.spec.ports[0].nodePort}'
echo
k delete deploy drill2 svc drill2

Вправа 3: Тестування DNS-резолюції (Ціль: 2 хвилини)

Розділ «Вправа 3: Тестування DNS-резолюції (Ціль: 2 хвилини)»
Terminal window
k create deployment drill3 --image=nginx
k expose deployment drill3 --port=80
# Тестування DNS
k run dns-test --image=busybox --rm -it --restart=Never -- nslookup drill3
k delete deploy drill3 svc drill3

Вправа 4: Сервіс з іменованим портом (Ціль: 2 хвилини)

Розділ «Вправа 4: Сервіс з іменованим портом (Ціль: 2 хвилини)»
Terminal window
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Service
metadata:
name: drill4
spec:
selector:
app: drill4
ports:
- name: http
port: 80
targetPort: 80
- name: metrics
port: 9090
targetPort: 9090
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: drill4
spec:
replicas: 2
selector:
matchLabels:
app: drill4
template:
metadata:
labels:
app: drill4
spec:
containers:
- name: nginx
image: nginx
EOF
k get svc drill4
k get ep drill4
k delete deploy drill4 svc drill4

Вправа 5: Зневадження зʼєднання Сервісу (Ціль: 3 хвилини)

Розділ «Вправа 5: Зневадження зʼєднання Сервісу (Ціль: 3 хвилини)»
Terminal window
# Створити деплоймент і зламаний сервіс
k create deployment drill5 --image=nginx
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Service
metadata:
name: drill5
spec:
selector:
app: wrong
ports:
- port: 80
EOF
# Зневадження
k get ep drill5 # Немає точок доступу
k get pods --show-labels # Перевірити мітки подів
k describe svc drill5 | grep Selector # Перевірити селектор сервісу
# Виправлення
k patch svc drill5 -p '{"spec":{"selector":{"app":"drill5"}}}'
k get ep drill5 # Тепер мають бути точки доступу
k delete deploy drill5 svc drill5

Вправа 6: Доступ до Сервісу між просторами імен (Ціль: 3 хвилини)

Розділ «Вправа 6: Доступ до Сервісу між просторами імен (Ціль: 3 хвилини)»
Terminal window
# Створити простір імен і сервіс
k create ns drill6
k create deployment drill6-app --image=nginx -n drill6
k expose deployment drill6-app --port=80 -n drill6
# Доступ з простору імен default
k run test --image=busybox --rm -it --restart=Never -- wget -qO- drill6-app.drill6:80
k delete ns drill6

Модуль 5.2: Інгрес — HTTP-маршрутизація та завершення TLS.