Розбираємо Kubernetes зрозумілою мовою
Якщо ви чули слова «Kubernetes», «кластер», «pod», але далі цих слів справа не заходила — ця стаття саме для вас. Тут я спробую пояснити простими словами, що таке Kubernetes, навіщо він взагалі потрібен, і як з ним працювати на базовому рівні.
Без складних слів, тільки прості приклади й зрозумілі коментарі.
Що таке Kubernetes
Kubernetes (або просто K8s) — це система, яка дозволяє запускати і керувати контейнерами на великій кількості машин.
Контейнер — це спосіб запакувати ваш застосунок разом з усіма його залежностями, щоб він працював однаково в будь-якому середовищі. Найпопулярніший це Docker.
Але не хвилюйтесь: спершу це виглядає складно, але по факту все зводиться до кількох YAML-файлів, у яких ви описуєте, що хочете отримати — а Kubernetes вже сам вирішує, як це виконати.
Основні поняття
Pod — це те, що Kubernetes реально запускає. Це «обгортка» навколо одного або кількох контейнерів (Docker).
apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: my-app image: nginx:latest ports: - containerPort: 80
Що тут відбувається:
apiVersion: v1— вказує, що ми використовуємо першу версію API Kubernetes для опису ресурсу.kind: Pod— ми створюємо Pod (найменшу одиницю в Kubernetes, яка може запускатися). Pod може містити один або кілька контейнерів, які працюють разом.metadata.name: my-pod— задаємо ім’я Pod-у. Потім це ім’я можна буде використати для пошуку чи управління цим Pod-ом.spec— описує, що саме всередині Pod-а.containers— список контейнерів, які будуть запущені в Pod.name: my-appім’я контейнера.image: nginx:latestобраз контейнера, який буде запущено.nginxпопулярний веб-сервер.latestостання доступна версія.- Образ буде завантажено з Docker Hub.
containerPort: 80порт, на якому контейнер слухає вхідні запити (всередині контейнера).
Service — доступ до вашого pod’а
У Kubernetes Pod-и мають тимчасові IP-адреси. Якщо Pod перезапуститься, його IP зміниться. Це проблема, бо інші контейнери з додатками або юзери не зможуть знайти його за старою адресою.
Service вирішує цю проблему: він дає постійну адресу (ім’я в DNS або IP), яка завжди вказує на потрібні Pod-и, навіть якщо ті перезапускаються.
Тому нам потрібно створити Service:
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 80
Що тут важливо:
selector — вказуємо, що Service повинен знаходити й обслуговувати тільки ті Pod-и, у яких є тег my-app.
ports — Service слухає звернення зовні.
targetPort — порт усередині Pod-а, на який Service переадресує запит.
У нашому випадку це 80, бо Nginx працює саме на ньому.
Deployment — автоматичне створення pod’ів
Якщо створювати Pod-и вручну, то у разі збою чи оновлення вам доведеться самому їх видаляти, перезапускати та стежити, щоб вони були в потрібній кількості.
Deployment це такий собі «менеджер» для Pod-ів. Ви описуєте, скільки і яких Pod-ів вам потрібно, а Kubernetes сам їх створює, стежить, щоб їх кількість відповідала тому, що ми прописали в конфігах, та оновлює їх без простоїв (по одному чи партіями, щоб сервіс не зупинявся).
apiVersion: apps/v1 kind: Deployment metadata: name: my-deployment spec: replicas: 2 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: nginx:latest ports: - containerPort: 80
Що тут головне:
replicas: 2— означає, що ми хочемо два однакових Pod-и.
Якщо один зламається, Kubernetes автоматично створить новий, щоб знову було два.selectorтаtemplate:selectorкаже: «цей Deployment керує лише Pod-ами з лейбломmy-app».template— опис шаблону Pod-а, який Kubernetes створюватиме.
Тут ми вказали контейнер Nginx, який слухає порт 80.
ConfigMap — змінні конфігурації
Іноді в застосунку треба вказати налаштування — наприклад, у якому режимі він працює (production, development) або до якого API підключається. Якщо ці значення захардкодити, то для зміни конфігурації доведеться збирати новий контейнер.
ConfigMap дозволяє винести ці налаштування назовні — у спеціальний об’єкт Kubernetes.
apiVersion: v1 kind: ConfigMap metadata: name: my-config data: APP_MODE: "production" API_URL: "https://api.example.com"
APP_MODE— приклад змінної, яка може визначати режим роботи застосунку.API_URL— адреса API, до якого звертається застосунок.
А потім додати ці значення в контейнер:
envFrom: - configMapRef: name: my-config
Це скаже Kubernetes: «Візьми всі ключі з ConfigMap my-config і зроби їх змінними в контейнері». Тобто всередині контейнера можна буде звернутися до них, як до звичайних environment variables ($APP_MODE, $API_URL).
Secret — зберігаємо паролі
Чому не можна використовувати ConfigMap для паролів? ConfigMap зберігає дані у відкритому вигляді (plain text). Будь-хто, хто має доступ до кластеру, зможе побачити ці значення. Якщо нам треба зберігати конфіденційну інформацію, ті ж логіни, паролі або API-ключі, то для цього використовують зазвичай Secret.
Secret — це спеціальний тип об’єкта Kubernetes для зберігання більш чутливих даних.
Він не шифрує їх «по-справжньому», але зберігає у base64. Для повноцінного захисту треба ще додатково налаштовувати шифрування в Kubernetes (Encryption at Rest) або інтегруватися з одним із секрет-менеджерів (HashiCorp Vault, AWS Secrets Manager тощо).
apiVersion: v1 kind: Secret metadata: name: my-secret type: Opaque data: DB_USER: dXNlcg== # user (base64) DB_PASS: cGFzc3dvcmQ= # password (base64)
DB_USERіDB_PASS— це ключі, які збережені у форматі base64.
Як можна передати Secret у контейнер?
envFrom: - secretRef: name: my-secret
Після цього $DB_USER та $DB_PASS будуть доступні як звичайні environment variables в середині контейнера.
Volume — збереження даних
Контейнери — тимчасові: якщо контейнер впаде або перезапуститься, усі дані всередині зникнуть.
Щоб цього уникнути (наприклад, для збереження логів або файлів), використовують volume — він зберігає дані незалежно від стану контейнера.
volumes:
- name: my-volume
emptyDir: {} # тимчасове сховище
Якщо треба зберігати дані навіть після видалення Pod-а, використовують PersistentVolume (PV) це постійне сховище, яке може виступати або локальним диском, або хмарним (AWS EBS, GCP Persistent Disk, Azure Disk).
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
storage: 1Gi— просимо 1 гігабайт пам’яті.ReadWriteOnce— цей том можна підключити для читання/запису лише до одного вузла одночасно.
Як прикріпити volume до контейнера:
spec: containers: - name: my-app image: nginx volumeMounts: - mountPath: /data name: my-volume volumes: - name: my-volume persistentVolumeClaim: claimName: my-pvc
mountPathце шлях у контейнері, де будуть доступні файли з volume.persistentVolumeClaim.claimName— назва PVC, який ми створили.
Ingress — доступ із зовнішнього світу
У Kubernetes кожен Pod має внутрішню IP-адресу, але вони недоступні напряму ззовні. Навіть якщо ми створили Service, він за замовчуванням доступний лише всередині кластера.
Але якщо ми хочемо, щоб користувач зміг зайти на наш сайт — наприклад, myapp.com, нам потрібно щось більше. Тут вже з’являється Ingress.
Це просто набір правил, який описує:
- як маршрутизувати HTTP(S)-запити на конкретні сервіси;
- які хости обробляти (myapp.com, admin.myapp.com);
- які шляхи куди направляти (/login → один сервіс, /api → інший);
- і навіть HTTPS/TLS, редіректи, обробку помилок тощо.
Але Ingress сам нічого не робить 😅
Ingress це лише конфіг. Щоб він реально працював, у кластері має бути встановлений Ingress Controller — це спеціальний сервіс, який «слухає» ці правила і обробляє трафік.
Як це виглядає у схемі?
[Клієнт] --> [Ingress Controller] --> [Service] --> [Pod] --> [Контейнери]
Простий приклад Ingress:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress spec: rules: - host: myapp.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-service port: number: 80
Це означає:
- Якщо користувач заходить на myapp.com (і DNS цього домену вказує на IP Ingress Controller), то:
- Запит потрапляє на Service
my-app-service. - Service перенаправляє його в потрібний Pod на порт 80.
Ingress Controller — без нього нічого не працює
Kubernetes не має вбудованого Ingress Controller — його треба встановити окремо.
Найпопулярніші варіанти:
- NGINX Ingress Controller
- Traefik
- HAProxy
Як встановити NGINX Ingress Controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm install my-app-ingress ingress-nginx/ingress-nginx
Після цього у вас зʼявиться:
- Deployment з Ingress Controller-ом.
- Сервіс, через який він доступний ззовні — типу LoadBalancer або NodePort (залежить від середовища).
У Kubernetes можна дати зовнішній доступ до сервісу кількома способами.
NodePort — відкриває порт на одному або кількох серверах (їх у Kubernetes називають нодами). До сервісу можна звернутися через IP-адресу такого сервера та номер порту.
LoadBalancer — працює лише в хмарі. Kubernetes просить у хмарного провайдера зовнішню IP-адресу та підключає її до сервісу автоматично.
У локальних середовищах, таких як Minikube, LoadBalancer зазвичай недоступний, тому використовується NodePort.
Багато хто плутає і думає, що достатньо просто створити Ingress, але Ingress не працює без контролера. Це як правила без поліції.
Helm — як npm для Kubernetes
YAML-файли — це круто, але коли їх багато, важко щось змінити. Тут приходить на допомогу Helm — пакетний менеджер для Kubernetes. Він дозволяє:
- встановлювати готові «чарти» (наприклад, Redis, PostgreSQL, Nginx);
- створювати свої шаблони з параметрами;
- автоматизувати деплой.
Як встановити Helm
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null sudo apt-get install apt-transport-https --yes echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list sudo apt-get update sudo apt-get install helm
Пошук готових чартів
helm repo add bitnami https://charts.bitnami.com/bitnami helm search repo nginx
Встановити nginx за одну команду
helm install my-nginx bitnami/nginx
Kubernetes сам створить pod-и, service-и, secrets і volume-и для nginx.
Як побачити, що він працює?
kubectl get pods -n my-nginx
А як створити свій Helm chart?
helm create my-app
І отримаємо структуру:
my-app/ ├── charts/ # залежності ├── templates/ # YAML-шаблони ├── values.yaml # параметри за замовчуванням
Приклад шаблону
У Helm Chart’ах шаблони пишуться з використанням {{ ... }} — це схоже на змінні, які підставляються з values.yaml.
Наприклад, у файлі templates/deployment.yaml може бути так:
# templates/deployment.yaml
spec:
replicas: {{ .Values.replicaCount }}
А в values.yaml:
replicaCount: 2
Тут .Values.replicaCount означає: «підстав значення replicaCount з values.yaml».
Коли ми запускаємо:
helm install my-app ./my-app
Helm:
- читає всі файли з templates/;
- підставляє всі значення з values.yaml;
- генерує готові Kubernetes-ресурси (Deployment, Service, тощо);
- і відправляє їх у кластер.
У результаті Kubernetes отримає Deployment з replicas: 2.
Підсумок
Kubernetes — це не складно, якщо підходити поступово. YAML-файли — це просто опис того, що ти хочеш, а Kubernetes сам вирішить, як це зробити.
Ще раз:
- Pod — це контейнер.
- Service — шлях до pod’ів.
- Deployment — автоматизація запуску і оновлень.
- ConfigMap/Secret - конфігурації і паролі.
- Volumes - збереження даних.
P.S. Я фронтендщік, тому не сильно кидайте в мене помідорами :D
P.S.S. Також я ще пишу у своєму Medium, тому буду радий, якщо підпишетесь.

16 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів