Модуль 5.1: Сервіси
Складність:
[MEDIUM]— Основна концепція мережі, кілька типів для розумінняЧас на виконання: 45–55 хвилин
Передумови: Модуль 1.1 (Поди), Модуль 2.1 (Деплойменти), розуміння основ мережі
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Створити Сервіси ClusterIP, NodePort та LoadBalancer для відкриття доступу до застосунків
- Діагностувати проблеми з’єднання Сервісів за допомогою перевірки точок доступу, роздільної здатності DNS та перевірки портів
- Пояснити як Сервіси використовують селектори міток для маршрутизації трафіку до правильних Підів
- Порівняти типи Сервісів та обрати відповідний для внутрішніх та зовнішніх паттернів доступу
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Сервіси забезпечують стабільну мережу для подів. Оскільки поди ефемерні й отримують нові IP при перестворенні, вам потрібні Сервіси для стабільного доступу до ваших застосунків. Сервіси — це основа того, як застосунки спілкуються в Kubernetes.
Іспит CKAD перевіряє:
- Створення Сервісів (ClusterIP, NodePort, LoadBalancer)
- Розуміння виявлення Сервісів
- Зневадження зʼєднання Сервісів
- Роботу з точками доступу (endpoints)
Аналогія з телефонним довідником
Сервіси — це як корпоративний телефонний довідник. Працівники (поди) приходять і йдуть, змінюють робочі місця (IP), але внутрішній номер відділу (Сервіс) залишається незмінним. Коли ви телефонуєте у «Відділ продажів» (назва Сервісу), система направляє виклик тому, хто зараз працює. Довідник (DNS) перетворює імена на номери, а комутатор (kube-proxy) маршрутизує виклик.
Типи Сервісів
Розділ «Типи Сервісів»ClusterIP (типовий)
Розділ «ClusterIP (типовий)»Доступ лише зсередини кластера:
apiVersion: v1kind: Servicemetadata: name: my-servicespec: type: ClusterIP # Типовий, можна не вказувати selector: app: my-app ports: - port: 80 # Порт Сервісу targetPort: 8080 # Порт контейнера# Створити імперативноk expose deployment my-app --port=80 --target-port=8080
# Доступ зсередини кластераcurl http://my-service:80curl http://my-service.default.svc.cluster.local:80NodePort
Розділ «NodePort»Відкриває на IP кожного вузла на статичному порті:
apiVersion: v1kind: Servicemetadata: name: my-nodeportspec: type: NodePort selector: app: my-app ports: - port: 80 # Порт Сервісу (ClusterIP) targetPort: 8080 # Порт контейнера nodePort: 30080 # Порт вузла (30000-32767)# Створити імперативноk expose deployment my-app --type=NodePort --port=80 --target-port=8080
# Доступ ззовні кластераcurl http://<node-ip>:30080LoadBalancer
Розділ «LoadBalancer»Створює зовнішній балансувальник навантаження (хмарні середовища):
apiVersion: v1kind: Servicemetadata: name: my-loadbalancerspec: type: LoadBalancer selector: app: my-app ports: - port: 80 targetPort: 8080# Створити імперативноk expose deployment my-app --type=LoadBalancer --port=80 --target-port=8080
# Отримати зовнішню IPk get svc my-loadbalancer# Стовпець EXTERNAL-IP показує IP балансувальника навантаженняExternalName
Розділ «ExternalName»Відображає на зовнішнє DNS-імʼя (без проксіювання):
apiVersion: v1kind: Servicemetadata: name: external-dbspec: type: ExternalName externalName: database.example.comВиявлення Сервісів
Розділ «Виявлення Сервісів»DNS-імена
Розділ «DNS-імена»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 |
Змінні середовища
Розділ «Змінні середовища»Поди отримують змінні середовища для Сервісів, що існували на момент запуску пода:
# Всередині пода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)»Точки доступу створюються та оновлюються автоматично:
# Переглянути точки доступу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Немає відповідних подів?
Розділ «Немає відповідних подів?»Якщо селектор не збігається з жодним подом:
k get endpoints my-service# NAME ENDPOINTS AGE# my-service <none> 5mБезголові Сервіси (Headless)
Розділ «Безголові Сервіси (Headless)»Для прямого виявлення подів без балансування навантаження:
apiVersion: v1kind: Servicemetadata: name: headless-svcspec: clusterIP: None # Робить безголовим selector: app: my-app ports: - port: 80DNS повертає всі IP подів замість IP Сервісу:
# Повертає кілька A-записів (один на кожен під)nslookup headless-svc.default.svc.cluster.localВипадки використання: StatefulSets, бази даних, виявлення пірів.
Багатопортові Сервіси
Розділ «Багатопортові Сервіси»apiVersion: v1kind: Servicemetadata: name: multi-portspec: selector: app: my-app ports: - name: http # Назва обовʼязкова для багатопортових port: 80 targetPort: 8080 - name: https port: 443 targetPort: 8443Спорідненість сесій
Розділ «Спорідненість сесій»Маршрутизувати того самого клієнта до того самого пода:
apiVersion: v1kind: Servicemetadata: name: sticky-servicespec: selector: app: my-app sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 ports: - port: 80Швидка довідка
Розділ «Швидка довідка»# Створити Сервісk expose deployment NAME --port=80 --target-port=8080k expose deployment NAME --type=NodePort --port=80k expose deployment NAME --type=LoadBalancer --port=80
# Переглянути Сервісиk get svck describe svc NAME
# Переглянути точки доступуk get endpoints NAMEk get ep NAME
# Зневадити DNSk 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-rangekube-apiserver.
Типові помилки
Розділ «Типові помилки»| Помилка | Чим це шкодить | Рішення |
|---|---|---|
| Селектор не збігається з мітками подів | Сервіс не має точок доступу | k get ep для перевірки, виправити мітки |
| Неправильний targetPort | Зʼєднання відхилено | Має збігатися з портом, на якому слухає контейнер |
| Використання IP пода замість Сервісу | Ламається при перезапуску пода | Завжди використовуйте імʼя/IP Сервісу |
| Забули простір імен у DNS | Не можна дістатися до Сервісу | Використовуйте svc.namespace або повний FQDN |
| NodePort без правила файрволу | Немає доступу ззовні | Відкрити порт вузла у хмарному файрволі |
Тест
Розділ «Тест»-
Яка різниця між
portіtargetPortу Сервісі?Відповідь
`port` — це порт Сервісу (до якого підключаються клієнти). `targetPort` — це порт контейнера (куди перенаправляється трафік). Приклад: Сервіс слухає на 80, перенаправляє на порт 8080 контейнера. -
Як дізнатися, до яких подів маршрутизує Сервіс?
Відповідь
`kubectl get endpoints <назва-сервісу>` показує IP та порти подів, до яких маршрутизує Сервіс. -
Що станеться, якщо селектор Сервісу не збігається з жодним подом?
Відповідь
Сервіс існує, але не має точок доступу. Зʼєднання не працюватимуть. `kubectl get endpoints` покаже ``. -
Як поди у просторі імен A можуть звернутися до Сервісу у просторі імен B?
Відповідь
Використовуйте DNS-імʼя з простором імен: `service-name.namespace-b` або повний FQDN `service-name.namespace-b.svc.cluster.local`.
Практична вправа
Розділ «Практична вправа»Завдання: Створити та протестувати різні типи Сервісів.
Підготовка:
# Створити деплойментk create deployment web --image=nginx --replicas=3
# Дочекатися подівk wait --for=condition=Ready pod -l app=web --timeout=60sЧастина 1: Сервіс ClusterIP
# Створити 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
# Перевірити DNSk run test --image=busybox --rm -it --restart=Never -- nslookup web.default.svc.cluster.localЧастина 2: Сервіс NodePort
# Видалити ClusterIP сервісk delete svc web
# Створити NodePort сервісk expose deployment web --type=NodePort --port=80 --target-port=80
# Отримати призначений NodePortk get svc web -o jsonpath='{.spec.ports[0].nodePort}'echo
# Тест (якщо є доступ до вузла)# curl http://<node-ip>:<nodeport>Частина 3: Зневадження відсутніх точок доступу
# Створити сервіс з неправильним селекторомcat << 'EOF' | k apply -f -apiVersion: v1kind: Servicemetadata: name: broken-svcspec: selector: app: wrong-label ports: - port: 80EOF
# Перевірити точки доступу (мають бути порожніми)k get endpoints broken-svc
# Виправити патчем селектораk patch svc broken-svc -p '{"spec":{"selector":{"app":"web"}}}'
# Перевірити, що точки доступу тепер єk get endpoints broken-svcПрибирання:
k delete deployment webk delete svc web broken-svcПрактичні вправи
Розділ «Практичні вправи»Вправа 1: Створити ClusterIP Сервіс (Ціль: 1 хвилина)
Розділ «Вправа 1: Створити ClusterIP Сервіс (Ціль: 1 хвилина)»k create deployment drill1 --image=nginxk expose deployment drill1 --port=80k get svc drill1k get ep drill1k delete deploy drill1 svc drill1Вправа 2: Створити NodePort Сервіс (Ціль: 2 хвилини)
Розділ «Вправа 2: Створити NodePort Сервіс (Ціль: 2 хвилини)»k create deployment drill2 --image=nginxk expose deployment drill2 --type=NodePort --port=80 --target-port=80
# Отримати NodePortk get svc drill2 -o jsonpath='{.spec.ports[0].nodePort}'echo
k delete deploy drill2 svc drill2Вправа 3: Тестування DNS-резолюції (Ціль: 2 хвилини)
Розділ «Вправа 3: Тестування DNS-резолюції (Ціль: 2 хвилини)»k create deployment drill3 --image=nginxk expose deployment drill3 --port=80
# Тестування DNSk run dns-test --image=busybox --rm -it --restart=Never -- nslookup drill3
k delete deploy drill3 svc drill3Вправа 4: Сервіс з іменованим портом (Ціль: 2 хвилини)
Розділ «Вправа 4: Сервіс з іменованим портом (Ціль: 2 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Servicemetadata: name: drill4spec: selector: app: drill4 ports: - name: http port: 80 targetPort: 80 - name: metrics port: 9090 targetPort: 9090---apiVersion: apps/v1kind: Deploymentmetadata: name: drill4spec: replicas: 2 selector: matchLabels: app: drill4 template: metadata: labels: app: drill4 spec: containers: - name: nginx image: nginxEOF
k get svc drill4k get ep drill4k delete deploy drill4 svc drill4Вправа 5: Зневадження зʼєднання Сервісу (Ціль: 3 хвилини)
Розділ «Вправа 5: Зневадження зʼєднання Сервісу (Ціль: 3 хвилини)»# Створити деплоймент і зламаний сервісk create deployment drill5 --image=nginxcat << 'EOF' | k apply -f -apiVersion: v1kind: Servicemetadata: name: drill5spec: selector: app: wrong ports: - port: 80EOF
# Зневадження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 хвилини)»# Створити простір імен і сервісk create ns drill6k create deployment drill6-app --image=nginx -n drill6k expose deployment drill6-app --port=80 -n drill6
# Доступ з простору імен defaultk run test --image=busybox --rm -it --restart=Never -- wget -qO- drill6-app.drill6:80
k delete ns drill6Наступний модуль
Розділ «Наступний модуль»Модуль 5.2: Інгрес — HTTP-маршрутизація та завершення TLS.