Побудувати структурно правильні маніфести Kubernetes, використовуючи фундаментальний синтаксис YAML, включаючи скаляри, списки (sequences), словники (mappings) та багаторядкові рядки.
Розібрати чотири обов’язкові поля кожного ресурсу Kubernetes (apiVersion, kind, metadata, spec), щоб пояснити їхні ролі в декларативному керуванні станом.
Діагностувати помилки валідації структури та схеми у файлах YAML, інтерпретуючи вивід команд kubectl apply --dry-run та kubectl explain.
Спроектувати складні конфігурації розгортання з декількома ресурсами, використовуючи просунуті шаблони YAML, такі як роздільники документів, змінні оточення та монтування Volume-ів.
Була 2:15 ночі вівторка, коли основний сервіс оформлення замовлень середньої e-commerce платформи раптово зник із продуктивного кластера. Черговий інженер із заспаними очима та на каві, що вже давно охолола, гарячково перевіряв Deployment pipeline-и. Щойно було випущено hotfix для усунення критичної вразливості у фоновому робочому процесі. Pipeline світився зеленим, але Pod-и оформлення замовлень зникли.
Після 45 хвилин пошуків було виявлено першопричину: одне зайве тире у файлі YAML. Розробник випадково перетворив словник (mapping) міток розгортання на елемент списку, що зробило недійсним selector, який пов’язував Service із Deployment. API-сервер Kubernetes, бездоганно виконуючи те, що йому наказали, побачив Deployment без відповідних Pod-ів, і Service почав спрямовувати трафік у порожнечу. Ця крихітна синтаксична помилка коштувала компанії десятків тисяч доларів втраченого доходу.
Історія з передової: “Згорнутий” сертифікат
В іншому сумнозвісному інциденті у фінансовій технологічній фірмі інженер оновив TLS-сертифікат, що зберігався як Secret у Kubernetes. Замість того, щоб використовувати літеральний блоковий скаляр (|) для збереження суворих переносів рядків у сертифікаті, вони випадково використали складений блоковий скаляр (>). Коли Kubernetes змонтував Secret в Ingress-контролер, весь сертифікат було розпарсено як один величезний рядок, де замість переносів рядків були пробіли. Ingress-контролер постійно падав, оскільки формат сертифіката був недійсним, що призвело до дворічного глобального збою. Різниця в один символ (> проти |) обійшла базові перевірки синтаксису YAML, тому що сам YAML був технічно коректним — він просто зіпсував дані.
YAML (YAML Ain’t Markup Language) — це lingua franca (мова спілкування) у Kubernetes. Це те, як ви повідомляєте бажаний стан керуючій площині (control plane). Хоча Kubernetes технічно може приймати JSON, YAML є стандартом, зручним для читання людиною. Однак його залежність від значущих пробілів та тонких синтаксичних правил робить його “мінним полем” для новачків. Оволодіння YAML — це не просто вивчення мови конфігурації; це вивчення того, як точно та безпечно взаємодіяти з API Kubernetes. Цей модуль перетворить YAML із джерела розчарування на потужний і передбачуваний інструмент для декларативної інфраструктури.
Перш ніж занурюватися у специфічні схеми Kubernetes, ви повинні зрозуміти основні структури даних YAML. YAML — це мова серіалізації даних, розроблена для того, щоб її було легко читати людям, і вона легко відображалася на нативні структури даних у мовах програмування (такі як словники, списки та рядки).
На найнижчому рівні файл YAML будується з трьох примітивних структур:
Скаляри (Scalars): Поодинокі значення (рядки, цілі числа, булеві значення). Це листя дерева даних.
Словники (Mappings): Пари ключ-значення. Вони визначають властивості об’єкта.
Списки (Sequences): Впорядковані колекції елементів.
Ці структури можуть мати нескінченну вкладеність для представлення складних систем:
# Це Словник (Mapping) на кореневому рівні
server: nginx
port: 8080
is_active: true# Булевий скаляр
# Це Список (Sequence) скалярів
allowed_origins:
- https://example.com
- https://api.example.com
# Це Словник, що містить Список Словників
users:
- name: alice
role: admin
permissions:
- read
- write
- name: bob
role: editor
permissions:
- read
Критичне правило: YAML використовує пробіли для відступів, щоб позначити структуру. Табуляція суворо заборонена. Стандартною конвенцією в екосистемі Kubernetes є використання двох пробілів для кожного рівня відступу. Один неправильно вирівняний пробіл змінює всю структуру даних, що часто призводить до помилок валідації схеми.
Зупиніться та подумайте:
Подивіться на блок users вище. Скільки елементів у списку users? Який тип даних містить ключ permissions?
Відповідь
Список `users` має 2 елементи (словники для alice та bob). Ключ `permissions` містить Список (Sequence) рядкових скалярів.
Передаючи файли конфігурації, скрипти або сертифікати в ConfigMap або Secret у Kubernetes, вам часто знадобиться вставляти багаторядкові рядки. YAML надає для цього два індикатори блокових скалярів:
Літеральний блоковий скаляр (|): Зберігає переноси рядків та точне форматування. Це те, що вам потрібно в 99% випадків для скриптів, конфігураційних файлів або TLS-сертифікатів.
Складений блоковий скаляр (>): Перетворює переноси рядків на пробіли, створюючи один довгий рядок, за винятком порожніх рядків.
# Літеральний (|) - ідеально зберігає структуру для скрипту
setup_script: |
#!/bin/bash
echo "Починаємо налаштування..."
apt-get update
apt-get install -y curl
# Складений (>) - добре підходить для довгих описів, які мають бути одним абзацом
description: >
Це дуже довгий опис, який я хочу написати
у кілька рядків у моєму редакторі для зручності читання,
але я хочу, щоб програма бачила його як один
безперервний рядок тексту.
Перш ніж продовжити:
Якщо ви вставляєте ключ сертифіката .pem у Secret Kubernetes, який багаторядковий оператор ви ПОВИННІ використовувати і чому?
Відповідь
Ви ПОВИННІ використовувати літеральний блоковий скаляр (`|`). Сертифікати покладаються на суворі межі нових рядків (наприклад, `-----BEGIN CERTIFICATE-----`, після якого йде перенос рядка). Якщо ви використаєте `>`, він "складе" сертифікат в один недійсний рядок.
Хоча вони рідше зустрічаються у стандартних маніфестах Kubernetes через перевагу Helm або Kustomize для шаблонізації, нативний YAML підтримує принципи DRY (Don’t Repeat Yourself) за допомогою якорів та псевдонімів.
Якір (&) визначає блок YAML, а псевдонім (*) вставляє його в іншому місці.
# Визначаємо якір з назвою 'common_labels'
base_labels: &common_labels
app: web-tier
environment: production
managed-by: platform-team
frontend_pod:
metadata:
# Використовуємо ключ злиття (<<) для вставки псевдоніма
<<: *common_labels
name: react-frontend
backend_pod:
metadata:
<<: *common_labels
name: node-api
Активна вправа:
Подивіться на структуру frontend_pod вище. Якби ви перетворили цей YAML у JSON, як би виглядав отриманий JSON-об’єкт для frontend_pod.metadata?
Відповідь
{
"app": "web-tier",
"environment": "production",
"managed-by": "platform-team",
"name": "react-frontend"
}
Ключ злиття (merge key) розгортає словник безпосередньо в місці виклику.
Кожен окремий ресурс, який ви створюєте в Kubernetes — від простого Pod до складного CustomResourceDefinition — вимагає рівно чотирьох полів на кореневому рівні. Якщо будь-яке з них відсутнє, API-сервер негайно відхилить запит. Розуміння цих чотирьох полів є ключем до опанування декларативного стану.
Це повідомляє API-серверу, яку версію схеми використовувати для валідації. API Kubernetes розвиваються. Ресурс може початися у v1alpha1, перейти до v1beta1 і, нарешті, стати v1. Поле apiVersion вказує, які саме поля дозволені в решті файлу. Сюди включаються назви груп (наприклад, apps/v1, networking.k8s.io/v1). Якщо косої риски немає, ресурс належить до “основної” (core) групи (наприклад, просто v1 для Pod-ів, Service-ів та ConfigMap-ів).
Приклад: Якщо ви спробуєте створити Deployment з apiVersion: v1, API-сервер відхилить його, оскільки Deployment-и керуються схемою apps/v1.
Тип об’єкта, який ви намагаєтеся створити (наприклад, Pod, Service, Deployment, StatefulSet, Ingress). Він завжди пишеться з великої літери (CamelCase).
Дані, які унікально ідентифікують об’єкт і дозволяють кластеру організовувати його.
name: Повинно бути унікальним у межах простору імен (namespace) для цього конкретного типу (kind).
namespace: Віртуальний кластер, до якого належить об’єкт. За замовчуванням використовується default, якщо не вказано інше. Якщо ви забудете це вказати, ви можете випадково розгорнути ресурс не в тому середовищі!
labels: Пари ключ-значення, що використовуються для організації та вибору підмножин об’єктів (наприклад, tier: frontend, env: prod). Вони є функціональними та критично важливими для маршрутизації трафіку.
annotations: Неідентифікаційні метадані, що використовуються зовнішніми інструментами або контролерами (наприклад, build-commit: 4a2b9c, nginx.ingress.kubernetes.io/rewrite-target: /). Вони є описовими і зазвичай не впливають на стандартну маршрутизацію Kubernetes.
Це серце маніфесту. Поле spec декларує ваш бажаний стан. Кожен kind має кардинально різну схему spec. Поле spec у Pod визначає контейнери та Volume-и; spec у Service визначає порти та selector-и. Керуюча площина (control plane) Kubernetes постійно працює над тим, щоб фактичний стан відповідав бажаному стану, визначеному в цьому блоці.
(Примітка: деякі об’єкти, такі як ConfigMap та Secret, використовують поле data замість spec, але принцип залишається тим самим).
Зупиніться та подумайте:
Ви створюєте ConfigMap. Яке з 4 стандартних кореневих полів буде замінено, і як воно називається?
Відповідь
Поле `spec` замінюється на `data` (або `binaryData`). ConfigMap-и та Secret-и не мають "специфікації" бажаного стану; вони просто містять сирі дані.
Ви не можете запам’ятати всю схему API Kubernetes. Існують тисячі полів, а кастомні ресурси додають ще тисячі. Коли вам потрібно дізнатися, як налаштувати readiness probe або змонтувати Volume, вам не потрібно шукати в Інтернеті — у вас є офіційна документація, вбудована прямо у ваш термінал через kubectl explain.
kubectl explain звертається до OpenAPI-схеми вашого кластера.
Якщо ви хочете побачити весь скелет об’єкта одразу без описів, використовуйте прапор --recursive. Це неймовірно корисно для візуального розуміння вкладеної структури складних об’єктів, таких як Deployment.
Terminal window
kubectlexplaindeployment--recursive
Активна вправа:
Використовуйте свій термінал (або уявіть, що використовуєте його). Вам потрібно додати “node selector”, щоб Pod запускався лише на вузлах з SSD. Яку саме команду kubectl explain ви б запустили, щоб знайти документацію для поля node selector всередині Pod?
Відповідь
Terminal window
kubectlexplainpod.spec.nodeSelector
Це покаже вам, що nodeSelector очікує тип <map[string]string>, тобто ви повинні надати пари ключ-значення, що представляють мітки (labels) вузлів.
Давайте подивимося, як фундаментальні структури YAML відображаються на повсякденні конфігурації Kubernetes. Неправильне розуміння цих структур є причиною номер один зламаних розгортань.
Змінні оточення в контейнері визначаються як список (sequence) словників (mappings), де кожен словник повинен мати принаймні ключі name та value. Ви також можете вставляти значення з ConfigMap-ів або Secret-ів, використовуючи valueFrom.
apiVersion: v1
kind: Pod
metadata:
name: env-demo
spec:
containers:
- name: my-app
image: nginx:alpine
env: # Поле 'env' приймає Список (Sequence)
- name: DATABASE_URL# Перший елемент списку, пряме значення
value: "postgres://db:5432"
- name: LOG_LEVEL# Другий елемент списку
value: "debug"
- name: API_KEY# Третій елемент, значення з Secret
Робота з Volume-ами в YAML — це двоетапний процес. Спочатку ви визначаєте Volume на рівні Pod (spec.volumes). Потім ви монтуєте його в конкретні контейнери (spec.containers[].volumeMounts). Обидва поля є списками.
apiVersion: v1
kind: Pod
metadata:
name: volume-demo
spec:
containers:
- name: app-container
image: busybox
command: ["sleep", "3600"]
volumeMounts: # Де контейнер бачить цей volume?
- name: config-store# Повинно точно збігатися з назвою volume нижче!
mountPath: /etc/config
readOnly: true
volumes: # Який саме volume стоїть за цим?
- name: config-store# Ідентифікатор
configMap: # Тип volume (заповнює файли з ConfigMap)
Label-и — це прості пари ключ-значення, що використовуються для ідентифікації. Selector-и використовуються такими ресурсами, як Service та Deployment, щоб знайти інші ресурси на основі цих міток. Для просунутого пошуку Deployment-и використовують matchLabels або matchExpressions.
# Service, що шукає конкретні pod-и
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
spec:
selector: # Service спрямовуватиме трафік на будь-який Pod...
У реальному світі програма складається з декількох компонентів: Deployment, Service, ConfigMap тощо. Замість того, щоб керувати десятком окремих файлів, ви можете об’єднати декілька ресурсів Kubernetes в один файл YAML, використовуючи роздільник документів: --- (три тире).
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
color: "blue"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
# ... деталі розгортання ...
---
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
spec:
# ... деталі сервісу ...
Коли ви запускаєте kubectl apply -f combined.yaml, API-сервер обробляє всі документи.
Перш ніж продовжити:
Чи має значення порядок документів, розділених ---, коли ви запускаєте kubectl apply -f combined.yaml?
Відповідь
Технічно `kubectl apply` обробляє їх у тому порядку, в якому вони з'являються. Проте, оскільки Kubernetes постійно узгоджує стан (reconciliation), якщо Deployment буде створено раніше за ConfigMap, від якого він залежить, Pod-и просто не зможуть запуститися і перейдуть у стан crash-loop, доки ConfigMap не буде створено миттю пізніше. Зрештою все налагодиться, але за правилами хорошого тону варто ставити залежності (ConfigMap-и, Secret-и, PVC) на початок файлу!
Писати YAML легко; налагоджувати його — важко. API-сервер Kubernetes неймовірно суворий. Ви повинні перевіряти свої файли перед тим, як застосовувати їх до працюючого кластера.
Найшвидший спосіб перевірити синтаксис, не впливаючи на кластер, — використати клієнтський “сухий запуск” (dry run). Це перевіряє структуру вашого YAML та базову коректність схеми локально, без зв’язку з контролерами допуску (admission controllers) сервера.
Terminal window
kubectlapply-fmy-pod.yaml--dry-run=client
У разі успіху ви побачите pod/my-pod created (dry run). Якщо сталася помилка, kubectl вкаже на конкретний рядок із помилкою.
Клієнтська валідація бачить не все. Наприклад, вона може не знати, чи існує в кластері конкретне Custom Resource Definition, чи існує простір імен, або чи відхилить вашу зміну admission webhook. Серверний dry-run надсилає дані на API-сервер для повної валідації без збереження об’єкта в etcd.
Перед застосуванням змін до існуючого ресурсу ЗАВЖДИ використовуйте kubectl diff. Вона покаже вам, які саме поля зміняться, використовуючи стандартний формат diff (+ для додавання, - для видалення). Це запобігає випадковим руйнівним оновленням, таким як зміна мітки (label), через що всі ваші Pod-и можуть раптово стати “сиротами”.
Коли валідація не проходить, повідомлення про помилки Kubernetes можуть здаватися загадковими. Давайте розберемо типові з них на конкретних прикладах:
Помилка 1: Пастка відступів
error: error parsing deployment.yaml: error converting YAML to JSON: yaml: line 15: mapping values are not allowed in this context
Діагноз: Це майже завжди означає помилку у відступах, зокрема відсутність тире для елемента списку або неправильні пробіли навколо двокрапки. Перевірте рядок 15 та рядки безпосередньо перед ним.
Помилка 2: Невідповідність типів
The Deployment "my-app" is invalid: spec.replicas: Invalid value: "3": spec.replicas must be an integer
Діагноз: Ви передали рядок "3" замість цілого числа 3. У YAML лапки примусово встановлюють тип рядка. Приберіть лапки.
Помилка 3: Відсутня схема
error: unable to recognize "pod.yaml": no matches for kind "Pod" in version "apps/v1"
Діагноз: Ви використали неправильний apiVersion для цього типу (kind). Pod-и належать до основної групи API v1, а не до apps/v1 (яка призначена для Deployment-ів та StatefulSet-ів).
line 12: mapping key "port" already defined at line 10
Діагноз: Словники (mappings) повинні мати унікальні ключі. Ви не можете визначити port: 80, а потім port: 443 у тому самому блоці словника. Один перезапише інший, або парсер взагалі відхилить файл.
Помилка 5: Валідація невідомого поля
error: error validating "deployment.yaml": error validating data: ValidationError(Deployment.spec.template.spec): unknown field "image" in io.k8s.api.core.v1.PodSpec;
Діагноз: Невідповідність схемі. Ви поставили image безпосередньо під spec, але image має бути всередині списку containers (spec.containers[0].image).
Версії YAML: Kubernetes в основному використовує специфікації YAML версії 1.2, хоча старіші парсери спиралися на 1.1. У YAML 1.1 рядок NO (без лапок) сприймається як булеве значення False. Це спричинило величезні проблеми для Норвегії (код країни NO), вимагаючи суворого використання лапок у маніфестах Kubernetes.
Максимальний розмір маніфесту: Максимальний розмір одного об’єкта, який можна зберігати в etcd (і, відповідно, надіслати через YAML), становить рівно 1.5 Мегабайта. Якщо ваш ConfigMap перевищує цей розмір, вам потрібно переглянути архітектуру або використовувати зовнішнє сховище.
Еквівалентність JSON: Оскільки YAML є надмножиною JSON, будь-який коректний JSON-файл автоматично є коректним YAML-файлом. Ви можете виконати kubectl apply -f manifest.json, і це спрацює ідеально.
Походження ‘spec’: Поділ на metadata та spec був значною мірою натхненний дизайном внутрішнього оркестратора контейнерів Google — Borg. Поле spec представляє “вектор бажаного стану”, що передається в цикл керування.
Проблема 2000 року для YAML: Рядок 22:22 без лапок у YAML 1.1 розпізнається як ціле число у шістдесятковому форматі (як час), що дорівнює 1342. У YAML 1.2 він сприймається як рядок. Щоб уникнути сюрпризів, завжди беріть час або версії в лапки!
1. Ви пишете ConfigMap і вам потрібно включити багаторядковий bash-скрипт. Ви хочете зберегти точні переноси рядків і форматування. Який індикатор блокового скаляра YAML слід використовувати?
Відповідь: Літеральний блоковий скаляр: | (вертикальна риска). Це гарантує, що переноси рядків будуть збережені точно так, як вони написані, що критично для shell-скриптів.
2. Ви виконуєте kubectl apply -f deployment.yaml і отримуєте помилку: yaml: line 22: did not find expected key. Яка найбільш імовірна причина?
Відповідь: Помилка у відступах біля 22-го рядка. Ця конкретна помилка зазвичай означає, що парсер YAML зустрів значення там, де він очікував ключ словника, що часто спричинено неправильними пробілами або відсутнім тире у списку.
3. Сценарій: Вам потрібно з'ясувати, як саме налаштувати volume AWS Elastic Block Store (EBS) безпосередньо у специфікації Pod. У вас немає доступу до Інтернету. Яку саме команду ви запустите, щоб прочитати документацію?
Відповідь:kubectl explain pod.spec.volumes.awsElasticBlockStore. Ця команда проходить крізь OpenAPI-схему, щоб надати опис конкретних полів, необхідних для цього типу volume.
4. Які чотири обов'язкові кореневі поля повинні бути в будь-якому стандартному маніфесті ресурсу Kubernetes?
Відповідь:apiVersion, kind, metadata та spec (або data у випадку ConfigMap/Secret).
5. Сценарій: Ви написали складний файл StatefulSet YAML на 300 рядків. Ви хочете перевірити синтаксис і переконатися, що API-сервер розуміє схему ресурсу, але ви категорично не хочете створювати об'єкт у кластері зараз. Який прапор потрібно додати до kubectl apply?
Відповідь:--dry-run=server. Це надішле маніфест на API-сервер для повної валідації (включаючи перевірки admission controller-ів та CRD) без збереження змін у etcd. --dry-run=client також підходить для базової перевірки синтаксису, але серверна валідація є більш повною.
6. Правда чи неправда: Ви можете застосувати валідний JSON-файл за допомогою kubectl apply -f my-pod.json.
Відповідь: Правда. YAML офіційно є надмножиною JSON, що означає, що всі стандартні парсери YAML у Kubernetes нативно розуміють і приймають дані у форматі JSON.
Створіть файл із назвою dojo-app.yaml. Вставте в нього наступний навмисно зламаний YAML. Спробуйте застосувати його за допомогою kubectl apply -f dojo-app.yaml --dry-run=client.
apiVersion: v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: "2"
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
name: nginx
image: nginx:1.24
Рішення та діагноз 1
Ви повинні побачити помилку на кшталт: no matches for kind "Deployment" in version "v1".
Виправлення: Змініть apiVersion: v1 на apiVersion: apps/v1. Deployment-и не знаходяться в основній (core) групі API.
Знову застосуйте файл (client dry-run). Ви зіткнетеся з новими помилками. Виправляйте їх одну за одною на основі повідомлень про помилки. Використовуйте kubectl explain deployment.spec, якщо заплутаєтеся в структурі.
Рішення та діагноз 2
Помилка:Invalid value: "2": spec.replicas must be an integer.
Виправлення: Змініть replicas: "2" на replicas: 2 (приберіть лапки).
Помилка:error converting YAML to JSON: yaml: line 15: mapping values are not allowed in this context (або подібна залежно від парсера). Подивіться на блок containers.
Виправлення: Поле containers очікує список (sequence) об’єктів, а не прямий словник. Вам не вистачає тире.
Змініть:
Тепер, коли Deployment валідний, додайте Service в кінець того самого файлу dojo-app.yaml. Service повинен відкривати порт 80 і спрямовувати трафік на ваші Pod-и. Переконайтеся, що ви використовуєте правильний роздільник документів.
Рішення 3
Додайте --- у кінці файлу, а потім додайте визначення Service:
Тепер додайте третій ресурс на самий початок файлу (перед Deployment): ConfigMap з назвою app-config, одним ключем welcome-message та значенням "Hello KubeDojo!".
Рішення 4
Додайте це на самий початок dojo-app.yaml і відокремте від Deployment за допомогою ---.
Змініть Deployment із Завдання 2 так, щоб контейнер nginx монтував ConfigMap із Завдання 4 як змінну оточення з назвою GREETING. Потім запустіть серверний dry-run для валідації всього файлу.
Terminal window
kubectlapply-fdojo-app.yaml--dry-run=server
Рішення 5
Ваш фінальний валідний dojo-app.yaml має виглядати саме так:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
welcome-message: "Hello KubeDojo!"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.24
env:
- name: GREETING
valueFrom:
configMapKeyRef:
name: app-config
key: welcome-message
---
apiVersion: v1
kind: Service
metadata:
name: web-app-svc
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
Коли ви запустите kubectl apply -f dojo-app.yaml --dry-run=server, ви повинні побачити вивід, що підтверджує створення всіх трьох ресурсів:
configmap/app-config created (server dry run)
deployment.apps/web-app created (server dry run)
service/web-app-svc created (server dry run)
Якщо ви це бачите, ваш складний файл YAML із декількома ресурсами структурно правильний і відповідає схемі. Ви можете прибрати --dry-run=server, щоб реально розгорнути його!
Ви опанували мову Kubernetes (YAML) і розумієте, як будувати ресурси, що запускають ваші робочі навантаження. Але чому Kubernetes спроектований саме так? Чому варто використовувати декларативний YAML замість імперативних команд?
Продовжуйте до Філософії та дизайну, щоб зрозуміти загальну картину: цикли керування, архітектуру узгодження та чому Kubernetes зрештою переміг у війні оркестраторів контейнерів.