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

Модуль 1.2: Декларативність проти імперативності — Філософія

Складність: [ШВИДКО] — Концептуальне розуміння

Час на проходження: 25-30 хвилин

Попередні вимоги: Модуль 1 (Чому Kubernetes переміг)


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

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

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

  • Пояснити різницю між декларативним та імперативним підходами на реальних прикладах Kubernetes
  • Передбачити, що зробить Kubernetes, якщо ви вручну зміните щось, чим він керує (і чому)
  • Писати декларативні маніфести замість імперативних команд для продуктових навантажень
  • Діагностувати проблеми, спричинені імперативним мисленням у декларативній системі

О другій годині ночі чергова інженерка SaaS-компанії отримала сповіщення — критичний Pod перебував у циклі перезавантаження (crash-looping). Напівсонна, вона зайшла через SSH на вузол і вручну перезапустила контейнер. Проблему вирішено, можна спати далі. Проте Kubernetes миттєво видалив її запущений вручну контейнер і замінив його новим — який також пішов у crash-loop. Вона перезапустила його знову. Kubernetes знову його видалив. Протягом 45 хвилин вона боролася із системою, не усвідомлюючи, що Kubernetes не був зламаний — вона думала імперативно в декларативному світі.

Щойно вона оновила Deployment YAML, щоб виправити справжню причину конфігураційної помилки, і виконала kubectl apply, система відновилася сама за 12 секунд. Це і є різниця між імперативним та декларативним мисленням — і це найважливіша концепція в Kubernetes.

Якщо ви розумієте декларативне мислення, у роботі Kubernetes з’являється сенс. Якщо ні — ви будете боротися із системою замість того, щоб використовувати її.


Імперативний: кажіть системі, що робити

Розділ «Імперативний: кажіть системі, що робити»
Terminal window
# Імперативний підхід
ssh server1
docker run -d nginx
docker run -d nginx
docker run -d nginx
# Перевірка, чи вони запущені
docker ps
# Якщо один впаде, запустити інший
# Якщо трафік зросте, запустити більше
# Якщо сервер вийде з ладу, зайти через SSH кудись ще і повторити

Ви і є циклом керування. Ви спостерігаєте, приймаєте рішення та дієте.

Декларативний: кажіть системі, чого ви хочете

Розділ «Декларативний: кажіть системі, чого ви хочете»
# Декларативний підхід
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
Terminal window
kubectl apply -f nginx-deployment.yaml
# Готово. Kubernetes подбає про решту.

Kubernetes є циклом керування. Він спостерігає, приймає рішення та діє — безперервно.


Цикл узгодження (Reconciliation Loop)

Розділ «Цикл узгодження (Reconciliation Loop)»

Це основний механізм Kubernetes:

┌─────────────────────────────────────────────────────────────┐
│ ЦИКЛ УЗГОДЖЕННЯ │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ │ │
│ │ БАЖАНИЙ │◄───── Ви визначаєте це (YAML) │
│ │ СТАН │ │
│ │ │ │
│ └──────┬───────┘ │
│ │ │
│ │ Порівняння │
│ ▼ │
│ ┌──────────────┐ │
│ │ │ │
│ │ ПОТОЧНИЙ │◄───── K8s спостерігає за цим │
│ │ СТАН │ │
│ │ │ │
│ └──────┬───────┘ │
│ │ │
│ │ Якщо відрізняється... │
│ ▼ │
│ ┌──────────────┐ │
│ │ │ │
│ │ ВИКОНАТИ │◄───── K8s діє для узгодження │
│ │ ДІЮ │ │
│ │ │ │
│ └──────┬───────┘ │
│ │ │
│ └─────────────────────────────────────────┐ │
│ │ │
│ Цикл назавжди ◄───────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

Цей цикл працює постійно. Не один раз при запуску kubectl apply, а вічно.


Аналогія з реального світу: Термостат

Розділ «Аналогія з реального світу: Термостат»

Імперативний (без термостата)

Розділ «Імперативний (без термостата)»
Ви: "Холодно. Увімкни обігрівач."
[Минає час]
Ви: "Тепер спекотно. Вимкни обігрівач."
[Минає час]
Ви: "Знову холодно. Увімкни обігрівач."
[Повторювати вічно або здатися і страждати]

Ви — цикл керування.

Декларативний (з термостатом)

Розділ «Декларативний (з термостатом)»
Ви: "Я хочу, щоб було 22°C."
Термостат: [Постійно моніторить і регулює]

Термостат — це цикл керування. Ви оголосили свій бажаний стан (22°C), і система його підтримує.


Чому декларативність перемагає

Розділ «Чому декларативність перемагає»

1. Самовідновлення (Self-Healing)

Розділ «1. Самовідновлення (Self-Healing)»
# Ви кажете: "Я хочу 3 репліки"
spec:
replicas: 3

Зупиніться та подумайте: Що станеться, якщо ви вручну видалите Pod, яким керує Deployment?

Що відбувається, коли:

  • Контейнер падає? → K8s запускає інший
  • Вузол виходить з ладу? → K8s переносить Pod на інші вузли
  • Хтось вручну видаляє Pod? → K8s створює його знову

Ви не писали жодної логіки для цього. Ви просто задекларували те, що хочете отримати.

Terminal window
# Запустіть це 100 разів
kubectl apply -f deployment.yaml
# Результат: однаковий стан щоразу
# Жодних дублікатів, конфліктів чи помилок "вже існує"

Декларативні операції є ідемпотентними — багаторазове застосування однієї і тієї ж конфігурації дає однаковий результат.

3. Контроль версій та GitOps

Розділ «3. Контроль версій та GitOps»
Terminal window
# Ваша інфраструктура — це код
git log --oneline
a1b2c3d feat: scale web to 5 replicas
d4e5f6g fix: increase memory limit
g7h8i9j feat: add health checks
# Відкат інфраструктури за допомогою git
git revert a1b2c3d
kubectl apply -f .

Декларативні конфігурації можна версіонувати, рецензувати та перевіряти.

4. Виявлення відхилень (Drift Detection)

Розділ «4. Виявлення відхилень (Drift Detection)»
Імперативний світ:
- Хтось зайшов через SSH і вніс зміни
- Документація не відповідає реальності
- "На моїй машині працює"
- Страх торкатися продуктового середовища
Декларативний світ:
- Git — єдине джерело істини
- K8s безперервно підтримує цю істину
- Зміни потребують перевірки через PR
- Впевненість у розгортанні

Імперативна пастка

Розділ «Імперативна пастка»

Зупиніться та подумайте: Якщо Kubernetes постійно узгоджує стан, що це означає для ручних виправлень, зроблених безпосередньо на сервері?

Kubernetes має імперативні команди:

Terminal window
# Вони працюють, проте...
kubectl run nginx --image=nginx
kubectl scale deployment nginx --replicas=5
kubectl set image deployment/nginx nginx=nginx:1.19

Чому вони небезпечні:

  1. Відсутність аудиту: Хто запустив команду? Коли?
  2. Відсутність перевірки: Зміни оминають процес PR/рецензування.
  3. Дрейф (Drift): Стан системи не відповідає жодному файлу.
  4. Неможливість відтворення: “Які саме команди ми запускали для налаштування?”

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

  • Навчання та експерименти.
  • Налагодження (тимчасові зміни).
  • Надзвичайні ситуації (з негайним оновленням декларативної конфігурації після цього).

Найкраща практика:

Terminal window
# Генеруйте YAML, не застосовуйте напряму
kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > deployment.yaml
# Перевірте, зафіксуйте в git (commit), потім застосуйте
kubectl apply -f deployment.yaml

Візуалізація: Стан у часі

Розділ «Візуалізація: Стан у часі»
┌─────────────────────────────────────────────────────────────┐
│ ІМПЕРАТИВНІ ОПЕРАЦІЇ │
├─────────────────────────────────────────────────────────────┤
│ │
│ Стан │
│ ▲ │
│ │ Потрібне ручне втручання │
│ │ │ │ │ │
│ │ ┌─────────┼─────────┼─────────┼─────────┐ │
│ │ │ ▼ ▼ ▼ │ │
│ │ ───┴─────────X─────────X─────────X─────────┴─── │
│ │ Збої / Дрейф │
│ │ │
│ └────────────────────────────────────────────────► Час │
│ │
│ Результат: постійне гасіння пожеж, непередбачуваний стан │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ ДЕКЛАРАТИВНІ ОПЕРАЦІЇ │
├─────────────────────────────────────────────────────────────┤
│ │
│ Стан │
│ ▲ │
│ │ │
│ │ ┌─────────────────────────────────────────┐ │
│ │ │ Бажаний стан (визначений у YAML) │ │
│ │ ───┴─────────────────────────────────────────┴─── │
│ │ ▲ ▲ ▲ ▲ ▲ │
│ │ │ │ │ │ │ │
│ │ Самовідновлення через цикл узгодження │
│ │ │
│ └────────────────────────────────────────────────► Час │
│ │
│ Результат: самовідновлення, передбачуваність, спокійний сон│
│ │
└─────────────────────────────────────────────────────────────┘

  • Цикл узгодження запускається кожні 10 секунд за замовчуванням. Контролери постійно перевіряють, чи відповідає реальність бажаному стану.

  • Кожен ресурс Kubernetes є декларативним. Pods, Services, ConfigMaps — усе це лише декларації бажаного стану, які узгоджують контролери.

  • GitOps народився з цієї моделі. ArgoCD та Flux розширюють декларативне мислення: Git стає джерелом істини, а K8s узгоджує стан відповідно до Git.

  • Terraform використовує ту ж модель. Декларативна революція виходить за межі K8s. Сучасні інструменти інфраструктури майже повсюдно є декларативними.


ПомилкаЧому це шкодитьРішення
Використання імперативних команд у продакшеніНемає історії змін, ризик дрейфу конфігураціїЗавжди використовуйте kubectl apply -f
Редагування ресурсів “наживо” через kubectl editЗміни не зафіксовані в GitОновіть YAML-файли, застосуйте з Git
Нерозуміння того, що зміни є безперервнимиСюрприз, коли K8s “скасовує” ручні зміниПрийміть модель — змінюйте декларацію
Боротьба з циклом узгодженняРозчарування, складні обхідні шляхиПрацюйте із системою, а не проти неї
Змішування декларативних інструментів (напр., Helm + kubectl apply)Інструменти перезаписують конфігурації один одногоОберіть один метод розгортання для ресурсу
Забування про видалення застарілих ресурсівСтарі ресурси залишаються в кластеріВикористовуйте GitOps або kubectl apply --prune
Зберігання секретів у YAML без шифруванняВідкриває конфіденційні дані в системі контролю версійВикористовуйте Sealed Secrets, SOPS або External Secrets Operator
Очікування повної атомарності від kubectl applyЧасткові збої можуть залишити застосунок у зламаному станіПокладайтеся на перевірки стану (health checks) та readiness probes

Старе мислення (імперативне)

Розділ «Старе мислення (імперативне)»
"Мені потрібно розгорнути застосунок"
→ Зайти на сервер через SSH
→ Стягнути код
→ Зібрати
→ Запустити процес
→ Налаштувати nginx
→ Оновити фаєрвол
→ Протестувати
→ Задокументувати те, що я зробив

Нове мислення (декларативне)

Розділ «Нове мислення (декларативне)»
"Мені потрібно розгорнути застосунок"
→ Визначити бажаний стан у YAML
→ Зафіксувати в Git
→ kubectl apply (або дозволити GitOps зробити це)
→ K8s подбає про решту
→ Git І Є документацією

Контрольні запитання

Розділ «Контрольні запитання»
  1. Ви розгортаєте вебзастосунок із 3 репліками. Раптово на вузлі, де розміщено два з цих Pods, зникає живлення. Без втручання людини кількість реплік застосунку повертається до 3 протягом декількох хвилин. Який саме механізм Kubernetes відповідає за це і як він працює?

    Відповідь Основним механізмом, відповідальним за таку поведінку самовідновлення, є цикл узгодження (reconciliation loop). Kubernetes постійно порівнює бажаний стан (вашу декларацію про 3 репліки) із поточним станом (лише 1 активний Pod після збою вузла). Коли він виявляє цю невідповідність, контролер негайно вживає заходів для запуску 2 нових Pods на справних вузлах. Цей цикл працює у фоновому режимі безперервно.
  2. Під час великого напливу трафіку інженер швидко запускає kubectl scale deployment web --replicas=10. Через два дні інший розробник вносить зміни в образ застосунку через PR, і розгортання раптово повертається до 3 реплік, що спричиняє збій. Що стало причиною цього і чому імперативна команда стала пасткою?

    Відповідь Імперативна команда `kubectl scale` змінила стан кластера, але не оновила джерело істини в Git, створивши дрейф конфігурації. Коли розробник застосував оновлений YAML з Git, Kubernetes побачив, що задекларовано 3 репліки, і "виправив" кластер відповідно до цього, не знаючи про ручне масштабування. Імперативні команди небезпечні, бо не залишають слідів у системі контролю версій та створюють хибну реальність, яка буде перезаписана при наступному декларативному оновленні.
  3. Ваш CI/CD пайплайн налаштований на запуск kubectl apply -f deployment.yaml щоразу, коли зміни вливаються в гілку main. Розробник випадково запускає пайплайн 5 разів поспіль, не змінюючи файл розгортання. Що станеться в кластері та яка властивість декларативних систем робить це безпечним?

    Відповідь Стан кластера залишиться незмінним, без створення дублікатів ресурсів, завдяки властивості ідемпотентності. Операція є ідемпотентною, якщо її багаторазове застосування дає той самий результат, що й одноразове. У декларативній системі `kubectl apply` лише вказує кінцевий стан; якщо кластер уже йому відповідає, Kubernetes не виконує жодних дій. Це робить автоматизацію безпечною та передбачуваною.
  4. Скрипт випадково запустив kubectl delete pod для Pod бази даних, яким керує StatefulSet. До того як черговий інженер встиг відкрити ноутбук, Pod уже перезапускався. Чому система не чекала втручання людини?

    Відповідь Kubernetes негайно створив новий Pod, оскільки він працює за принципом безперервного декларативного циклу керування. Контролер, що стежить за StatefulSet, побачив, що поточний стан (0 Pods) відхиляється від бажаного (1 Pod), вказаного в маніфесті. Йому байдуже, чому Pod зник; його єдина робота — узгодити реальність із задекларованим наміром. Це демонструє, як декларативне самовідновлення замінює ручне гасіння пожеж автоматизованим виправленням.
  5. Ваша команда використовує ArgoCD (інструмент GitOps) для керування продуктовим кластером. Адміністратор заходить на вузол через SSH і вручну редагує конфігураційний файл Nginx всередині запущеного Pod, щоб виправити баг. Через десять хвилин баг з’являється знову. Чому це сталося і як GitOps обробляє таку ситуацію?

    Відповідь Баг з'явився знову, тому що GitOps забезпечує виконання декларативного стану, збереженого в Git, як абсолютного джерела істини. Ручна зміна всередині Pod створила дрейф конфігурації між поточним станом і репозиторієм Git. Контролер GitOps (або сам Kubernetes) зрештою узгодив стан, або перезапустивши Pod, або перезаписавши ручні зміни. Щоб виправити баг назавжди, інженер повинен оновити конфігурацію в Git.

Вправа для роздумів

Розділ «Вправа для роздумів»

Цей модуль є концептуальним — подумайте над цими питаннями:

1. Аналогія з термостатом:

  • Які ще системи у вашому житті працюють декларативно? (Круїз-контроль? Автоматична яскравість?)
  • Що робить їх простішими у використанні, ніж ручні аналоги?

2. Ваш поточний робочий процес:

  • Як ви розгортаєте програмне забезпечення сьогодні?
  • Це більше імперативний чи декларативний підхід?
  • Що б змінилося, якби ви перейшли на іншу модель?

3. Наслідки самовідновлення:

  • Якщо Kubernetes “скасовує” ручні зміни, це перевага чи недолік?
  • Як це змінює ваш підхід до пошуку та усунення несправностей?

4. Git та інфраструктура:

  • Чому зберігання інфраструктури як YAML у Git є потужним інструментом?
  • На які питання ви можете відповісти за допомогою git log, на які не могли відповісти раніше?

5. Зміна ментальної моделі:

  • Оператор запитує: “які команди мені запустити?”. Декларативний мислитель запитує: “який стан мені потрібен?”.
  • Яке питання веде до створення більш стабільних систем? Чому?

Цей перехід у мисленні — від “як” до “що” — є найважливішою концепцією в усьому курсі.


Декларативна модель — це фундамент Kubernetes:

  • Бажаний стан: ви визначаєте, що ви хочете (YAML).
  • Узгодження: K8s постійно приводить реальність у відповідність до бажання.
  • Самовідновлення: збої виправляються автоматично.
  • Ідемпотентність: застосовуйте ту саму конфігурацію багаторазово, отримуйте той самий результат.
  • Контроль версій: ваша інфраструктура — це код у Git.

Це не просто технічний вибір — це філософія, яка дозволяє будувати надійну та масштабовану інфраструктуру.


Модуль 1.3: Що ми не розглядаємо (і чому) — розуміння обсягу KubeDojo та куди звертатися за темами, які ми пропускаємо.