Модуль 1.5: Services — Стабільна мережа
Складність:
[СЕРЕДНЯ]— Базова концепція мережЧас на проходження: 35-40 хвилин
Попередні вимоги: Модуль 4 (Deployments)
Що ви зможете зробити
Розділ «Що ви зможете зробити»Після завершення цього модуля ви зможете:
- Створити Services для надання доступу до Pod-ів та пояснити, чому Pod-ам потрібні Services (IP-адреси Pod-ів є ефемерними)
- Обрати між ClusterIP, NodePort та LoadBalancer та пояснити, коли використовувати кожен з них
- Перевірити зв’язок із Service зсередини кластера за допомогою
curlта DNS-імен - Діагностувати Service, який не може підключитися до своїх Pod-ів, перевіряючи мітки (labels), селектори (selectors) та кінцеві точки (endpoints)
Чому це важливо
Розділ «Чому це важливо»Це була Чорна п’ятниця, і платформа електронної комерції мала серйозні проблеми. Інженерна команда помітила, що їхні Pod-и обробки платежів постійно аварійно завершували роботу та перезапускалися через витоки пам’яті. Хоча Kubernetes успішно створював нові Pod-и для підтримки потужності, фронтенд-додаток мав жорстко прописані IP-адреси старих Pod-ів. Кожного разу, коли платіжний Pod перезапускався, транзакції переривалися, поки інженер вручну не оновлював конфігурацію фронтенду новою IP-адресою. Вони втрачали тисячі доларів щохвилини, тому що їхня внутрішня мережа не могла адаптуватися до ефемерної інфраструктури.
Pod-и ефемерні — вони з’являються та зникають, кожен з новою IP-адресою. Services забезпечують стабільну мережеву взаємодію: фіксовану IP-адресу та DNS-ім’я, що спрямовують трафік на ваші Pod-и, незалежно від їхньої кількості чи частоти змін.
Проблема, яку вирішують Services
Розділ «Проблема, яку вирішують Services»┌─────────────────────────────────────────────────────────────┐│ БЕЗ SERVICES │├─────────────────────────────────────────────────────────────┤│ ││ IP-адреси Pod-ів постійно змінюються: ││ ││ Час 0: [Pod: 10.1.0.5] ││ Час 1: Pod впав, перестворений ││ Час 2: [Pod: 10.1.0.9] ← Інша IP! ││ ││ Проблема: Як іншим додаткам знайти ваш Pod? ││ │├─────────────────────────────────────────────────────────────┤│ З SERVICES │├─────────────────────────────────────────────────────────────┤│ ││ Service: my-app.default.svc.cluster.local ││ ClusterIP: 10.96.0.100 (стабільна!) ││ │ ││ ┌────────┴────────┐ ││ ▼ ▼ ││ [Pod: 10.1.0.5] [Pod: 10.1.0.9] ││ ││ Service спрямовує трафік на здорові Pod-и, IP не важливі ││ │└─────────────────────────────────────────────────────────────┘Зупиніться та подумайте: Якщо Deployment масштабується до 10 Pod-ів, скільки IP-адрес матиме пов’язаний з ним Service? (Відповідь: Тільки одну. Service підтримує єдину стабільну IP-адресу, розподіляючи трафік між усіма 10 фоновими Pod-ами.)
Створення Services
Розділ «Створення Services»Спробуйте самі: Імперативний спосіб (швидко)
Розділ «Спробуйте самі: Імперативний спосіб (швидко)»# Відкрити доступ до deploymentkubectl expose deployment nginx --port=80
# Зі специфічним типомkubectl expose deployment nginx --port=80 --type=NodePort
# Перевірити сервісkubectl get serviceskubectl get svc # Коротка формаСпробуйте самі: Декларативний спосіб (для продакшену)
Розділ «Спробуйте самі: Декларативний спосіб (для продакшену)»apiVersion: v1kind: Servicemetadata: name: nginxspec: selector: app: nginx # Має збігатися з мітками pod ports: - port: 80 # Порт Service targetPort: 80 # Порт контейнера type: ClusterIP # Тип за замовчуваннямkubectl apply -f service.yamlПорівняння типів Service
Розділ «Порівняння типів Service»Вибір правильного типу Service є критично важливим для безпеки та архітектури.
| Тип | Доступність | Найкраще для | Компроміс |
|---|---|---|---|
| ClusterIP | Тільки внутрішня | Бекенд бази даних, внутрішні API | Неможливо отримати доступ ззовні кластера. |
| NodePort | Зовнішня (Високий порт) | Швидка відладка, bare-metal кластери | Відкриває високі порти (30000+), незручно для зовнішніх клієнтів. |
| LoadBalancer | Зовнішня (Стандартний порт) | Публічні веб-додатки у хмарі | Коштує грошей за кожен Service, залежить від хмарного провайдера. |
ClusterIP (За замовчуванням)
Розділ «ClusterIP (За замовчуванням)»Тільки внутрішній доступ у межах кластера:
apiVersion: v1kind: Servicemetadata: name: internal-apispec: type: ClusterIP # За замовчуванням, можна пропустити selector: app: api ports: - port: 80 targetPort: 8080# Доступ тільки зсередини кластераcurl http://internal-api:80NodePort
Розділ «NodePort»Відкриває доступ на IP-адресі кожного вузла (node) на статичному порту:
apiVersion: v1kind: Servicemetadata: name: web-nodeportspec: type: NodePort selector: app: web ports: - port: 80 targetPort: 80 nodePort: 30080 # Опціонально: 30000-32767# Доступ ззовні кластераcurl http://<node-ip>:30080LoadBalancer
Розділ «LoadBalancer»Створює зовнішній балансувальник навантаження (у хмарних середовищах):
apiVersion: v1kind: Servicemetadata: name: web-lbspec: type: LoadBalancer selector: app: web ports: - port: 80 targetPort: 80# Отримати зовнішню IP (тільки в хмарі)kubectl get svc web-lb# Колонка EXTERNAL-IP покаже IP балансувальникаДіаграма Service
Розділ «Діаграма Service»┌─────────────────────────────────────────────────────────────┐│ ТИПИ SERVICE │├─────────────────────────────────────────────────────────────┤│ ││ ClusterIP (Тільки внутрішній) ││ ┌─────────────────────────────────────┐ ││ │ ClusterIP:80 ──► Pod:8080 │ ││ │ ──► Pod:8080 │ ││ │ (Доступно тільки в межах кластера) │ ││ └─────────────────────────────────────┘ ││ ││ NodePort (Зовнішній через вузол) ││ ┌─────────────────────────────────────┐ ││ │ <NodeIP>:30080 ──► ClusterIP:80 ──► Pods ││ │ (Доступно ззовні) │ ││ └─────────────────────────────────────┘ ││ ││ LoadBalancer (Хмарний зовнішній) ││ ┌─────────────────────────────────────┐ ││ │ <ExternalIP>:80 ──► NodePort ──► ClusterIP ──► Pods ││ │ (Хмарний провайдер керує LB) │ ││ └─────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────┘Виявлення сервісів (DNS)
Розділ «Виявлення сервісів (DNS)»Kubernetes створює записи DNS для Services:
<service-name>.<namespace>.svc.cluster.local# З будь-якого Pod-а ви можете звернутися до:curl nginx # Той самий простір іменcurl nginx.default # Явний простір іменcurl nginx.default.svc # Більш детальноcurl nginx.default.svc.cluster.local # Повне ім'я FQDNПриклад
Розділ «Приклад»# Створити deployment та servicekubectl create deployment nginx --image=nginxkubectl expose deployment nginx --port=80
# Перевірити DNS з іншого Pod-аkubectl run test --image=busybox --rm -it -- wget -qO- nginx# Повертає HTML-код nginx!
# Перевірити за допомогою повного DNS-іменіkubectl run test --image=busybox --rm -it -- nslookup nginx.default.svc.cluster.localСелектори: Як Services знаходять Pod-и
Розділ «Селектори: Як Services знаходять Pod-и»Services використовують селектори міток (label selectors):
# Servicespec: selector: app: nginx tier: frontend
# Pod (має мати ВСІ мітки)metadata: labels: app: nginx tier: frontendЗупиніться та подумайте: Що станеться з вашим Service, якщо ви вручну відредагуєте запущений Pod і видалите мітку
tier: frontend? (Відповідь: Service негайно видалить цей Pod зі свого списку кінцевих точок (endpoints), оскільки він більше не відповідає селектору, і трафік на нього більше не спрямовуватиметься.)
# Перевірити, на які Pod-и спрямований Servicekubectl get endpoints nginx# Показує IP:Port відповідних Pod-івМапінг портів
Розділ «Мапінг портів»spec: ports: - port: 80 # Порт Service (використовується клієнтами) targetPort: 8080 # Порт контейнера (де слухає додаток) protocol: TCP # TCP (за замовчуванням) або UDP┌─────────────────────────────────────────────────────────────┐│ Клієнт ──► Service:80 ──► Pod:8080 ││ │ │ ││ "port" "targetPort" │└─────────────────────────────────────────────────────────────┘Історії з передової: Фантомний збій
Розділ «Історії з передової: Фантомний збій»Велика стрімінгова компанія зіткнулася з дивним збоєм, коли рівно 10% запитів користувачів до їхнього каталогу відео завершувалися помилкою тайм-ауту з’єднання. Усі Pod-и виглядали здоровими, а Service був активним. Після кількох годин діагностики старший інженер виконав команду kubectl get endpoints catalog-service.
Вони виявили 10 кінцевих точок (endpoints), але одна з IP-адрес належала Pod-у, який був вручну видалений безпосередньо через середовище виконання контейнерів (Docker), оминаючи Kubernetes. Базові правила iptables для Service все ще спрямовували трафік на мертву IP-адресу! Рішення? Перезавантаження компонента kube-proxy на ураженому вузлі для оновлення застарілих правил маршрутизації. Урок: завжди дозволяйте Kubernetes керувати життєвим циклом ваших Pod-ів і завжди перевіряйте свої endpoints, коли трафік зникає!
Чи знали ви?
Розділ «Чи знали ви?»- Services використовують iptables або IPVS.
kube-proxyналаштовує правила, які спрямовують IP-адреси Service на IP-адреси Pod-ів. Жоден реальний проксі-процес не обробляє кожне з’єднання. - ClusterIP є віртуальним. Жоден мережевий інтерфейс не має цієї IP-адреси. Вона існує лише в правилах iptables.
- NodePort використовує УСІ вузли. Навіть ті вузли, на яких немає цільових Pod-ів, правильно спрямують трафік на потрібний вузол.
- Services балансують навантаження випадковим чином за замовчуванням. Кожне нове з’єднання може потрапити на інший Pod.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це погано | Рішення |
|---|---|---|
| Селектор не збігається з мітками Pod-а | Service не має endpoints, і трафік зникає у «чорній дірі». | Використовуйте kubectl get endpoints <service-name>, щоб перевірити збіг. |
Неправильний targetPort | Помилки “connection refused”, бо сервіс шле трафік туди, де ніхто не слухає. | Переконайтеся, що targetPort збігається з портом, на якому реально працює додаток. |
| Використання IP Pod-а замість імені Service | Додаток ламається в той самий момент, коли Pod перезапускається і отримує нову IP. | Завжди налаштовуйте додатки на використання DNS-імені Service. |
Забули встановити protocol: UDP | DNS або кастомні UDP-сервіси не працюють, бо за замовчуванням Service використовує TCP. | Явно вкажіть protocol: UDP у налаштуваннях порту. |
Відкриття кожного мікросервісу як LoadBalancer | Величезні рахунки за хмару, бо кожен LoadBalancer коштує грошей. | Використовуйте ClusterIP для внутрішніх сервісів та Ingress для маршрутизації HTTP. |
| Помилка в іменованих портах | Трафік не проходить, якщо рядок targetPort не збігається з ім’ям порту контейнера. | Ретельно перевіряйте написання та регістр між targetPort у Service та ports.name у Pod. |
Використання NodePort для публічного трафіку | Складно керувати, потребує нестандартних портів (30000+), бракує гнучкої маршрутизації. | Використовуйте LoadBalancer або Ingress для зовнішнього доступу у продакшені. |
Контрольні запитання
Розділ «Контрольні запитання»-
Сценарій: Молодший розробник жорстко прописав IP-адресу Pod-а бази даних у конфігурацію фронтенду. Наступного дня фронтенд не може з’єднатися з базою, хоча Pod бази даних працює ідеально. Чому це сталося, і яке рішення є нативним для Kubernetes?
Відповідь
Pod-и ефемерні, тобто вони часто знищуються та створюються контролерами, такими як Deployments. Коли Pod бази даних був перестворений (через оновлення вузла або збій), він отримав нову IP-адресу, що зламало конфігурацію фронтенду. Рішенням є створення Kubernetes Service для бази даних, який забезпечує стабільну, незмінну IP-адресу та DNS-ім'я. -
Сценарій: Ви розгортаєте кеш Redis, до якого мають звертатися виключно ваші Pod-и бекенд API у тому самому кластері. Вимоги безпеки забороняють доступ до цього кешу з інтернету. Який тип Service ви оберете і чому?
Відповідь
Слід обрати `ClusterIP`, який є типом за замовчуванням. Service типу ClusterIP призначає внутрішню IP-адресу, яка доступна лише зсередини самого кластера. Це ідеально відповідає вимогам безпеки, запобігаючи зовнішньому трафіку та дозволяючи бекенд API безперешкодно спілкуватися з Redis. -
Сценарій: Ви розгорнули новий веб-додаток і створили для нього Service. Однак при спробі доступу ви отримуєте помилку “connection refused”. Команда
kubectl get pods --show-labelsпоказує, що ваші Pod-и мають міткиapp=frontend,env=prod. Ваш Service має селекторapp=frontend,tier=web. Чому трафік не проходить?Відповідь
Services використовують селектори міток для визначення цільових Pod-ів. Щоб Service спрямував трафік на Pod, цей Pod повинен мати *всі* мітки, вказані в селекторі Service. У цьому сценарії Service шукає Pod-и з міткою `tier=web`, якої у Pod-ів немає. Як результат, Service не має кінцевих точок (endpoints) і скидає трафік. Потрібно оновити мітки Pod-ів або селектор Service. -
Сценарій: Розробник проводить діагностику всередині тестового Pod-а busybox у просторі імен
default. Їм потрібно перевірити зв’язок із Service платіжного API, який знаходиться у просторі іменfinance. Яке саме DNS-ім’я вони мають використати у командіcurl?Відповідь
Розробник має використати `payment-api.finance` або повне доменне ім'я (FQDN) `payment-api.finance.svc.cluster.local`. Оскільки тестовий Pod і цільовий Service знаходяться у різних просторах імен, звичайний `curl payment-api` не спрацює, бо за замовчуванням DNS Kubernetes шукає сервіси лише у поточному просторі імен Pod-а. -
Сценарій: Ваша команда переносить застарілий додаток у Kubernetes на AWS. Додаток має бути доступним зовнішнім клієнтам через інтернет на стандартному 80-му порту. Ви спробували
NodePort, але команда безпеки заборонила відкривати порти в діапазоні 30000+. Який тип Service буде правильним архітектурним вибором?Відповідь
Слід використовувати тип `LoadBalancer`. При створенні Service типу LoadBalancer у хмарному середовищі (як-от AWS, GCP або Azure), Kubernetes автоматично створює нативний хмарний балансувальник навантаження. Він спрямовує трафік зі стандартних портів (80 або 443) на публічній IP-адресі прямо у ваш кластер, задовольняючи вимоги безпеки. -
Сценарій: Ви створили Service під назвою
auth-svcта Deployment з Pod-ами авторизації. Ви хочете переконатися, що Kubernetes успішно зв’язав Service із Pod-ами, перш ніж тестувати додаток з іншого мікросервісу. Яку командуkubectlслід виконати, щоб довести, що Service знайшов IP-адреси Pod-ів?Відповідь
Потрібно виконати команду `kubectl get endpoints auth-svc` (або `kubectl describe svc auth-svc`). Об'єкт Endpoints автоматично створюється та оновлюється Kubernetes для ведення списку реальних IP-адрес Pod-ів, які відповідають селектору міток Service. Якщо список порожній — значить, є невідповідність міток або Pod-и не працюють. -
Сценарій: Ваш додаток на Node.js працює на порту 3000 всередині контейнера. Ви хочете, щоб інші Pod-и в кластері могли звертатися до нього за адресою
http://node-backend:80. Як налаштуватиportтаtargetPortу визначенні Service?Відповідь
Ви маєте встановити у Service `port: 80` та `targetPort: 3000`. Поле `port` визначає порт, який сам Service відкриває для клієнтів (віртуальний порт). Поле `targetPort` визначає реальний порт, на якому слухає додаток у контейнері. Service виступає як внутрішній проксі, транслюючи трафік з 80-го порту на 3000-й порт Pod-а.
Практична вправа
Розділ «Практична вправа»Завдання: Створити Deployment та надати доступу до нього через Service.
# 1. Створити deploymentkubectl create deployment web --image=nginx --replicas=3
# 2. Відкрити доступ як ClusterIPkubectl expose deployment web --port=80
# 3. Перевірити сервісkubectl get svc webkubectl get endpoints web
# 4. Тест зсередини кластераkubectl run test --image=busybox --rm -it -- wget -qO- web
# 5. Створити NodePort сервісkubectl expose deployment web --port=80 --type=NodePort --name=web-external
# 6. Отримати NodePortkubectl get svc web-external# Зверніть увагу на порт у діапазоні 30000-32767
# 7. Очищенняkubectl delete deployment webkubectl delete svc web web-externalКритерії успіху:
- Внутрішній Service
webстворено і йому призначено ClusterIP. -
kubectl get endpoints webпоказує три різні IP-адреси Pod-ів. - Команда
wgetіз тимчасового Pod-а успішно повертає вітальну HTML-сторінку Nginx. - Service
web-externalстворено з типомNodePortта портом у діапазоні 30000-32767.
Підсумок
Розділ «Підсумок»Services забезпечують стабільну роботу мережі:
Типи:
- ClusterIP — тільки внутрішній (за замовчуванням)
- NodePort — зовнішній через порт вузла (node port)
- LoadBalancer — зовнішній через хмарний LB
Ключові концепції:
- Селектори збігаються з мітками Pod-ів
- DNS-імена для виявлення (discovery)
- Мапінг портів (port → targetPort)
- Endpoints показують відповідні Pod-и
Команди:
kubectl expose deployment NAME --port=PORTkubectl get svckubectl get endpoints
Наступний модуль
Розділ «Наступний модуль»Модуль 1.6: ConfigMaps та Secrets — Керування конфігурацією.