Модуль 2 / 19
Kubernetes API, kubectl, маніфести та життєвий цикл об’єктів
Зміст
1. Цілі та результат модуля
Після модуля ви зможете досліджувати API поточного кластера, читати Kubernetes manifest як API-запит, безпечно перевіряти зміну до запису, пояснювати metadata життєвого циклу та розбирати конфлікти конкурентних клієнтів.
- GVK ідентифікує тип об’єкта в manifest, GVR — REST resource endpoint
- kubeconfig обирає cluster, identity і context, але не надає права
- dry-run client і server відповідають на різні питання
- resourceVersion захищає optimistic concurrency
- generation змінюється разом із desired state, observedGeneration показує оброблену controller версію
- Server-Side Apply зберігає володіння полями в managedFields
2. Теорія: GVK, GVR і discovery
apiVersion і kind утворюють Group-Version-Kind. Наприклад apps/v1 Deployment описує тип серіалізованого об’єкта. API endpoint використовує Group-Version-Resource: deployments — множинне ім’я REST resource. Один kind може обслуговуватися кількома API versions, а preferred version повідомляє discovery API Server.
kubectl api-resources отримує доступні ресурси з discovery, а kubectl explain читає опубліковану OpenAPI schema. Не переносьте поля між подібними kind із пам’яті: перевіряйте точний apiVersion, scope і schema кластера.
- core group записується як apiVersion: v1
- іменовані групи використовують форму group/version, наприклад apps/v1
- namespaced resource потребує namespace, cluster-scoped resource — ні
- short name зручне людині, але automation має віддавати перевагу повному імені resource
3. Теорія: обробка API-запиту
Спочатку клієнт установлює TLS і надає identity. API Server виконує authentication, потім authorization, mutating admission, schema/defaulting, validating admission і запис до storage. Точний порядок admission залежить від налаштованих plugins та webhooks.
Authentication відповідає «хто», authorization — «чи може identity виконати verb над resource», admission — «чи допустимий конкретний об’єкт і як його доповнити». Успішний RBAC check не гарантує проходження schema або policy. Помилки 401, 403, 404, 409 і 422 потребують різних гіпотез.
4. Теорія: identity, версії та стан об’єкта
name унікальне в межах scope, namespace відокремлює namespaced objects, uid розрізняє різні втілення одного імені. resourceVersion відображає версію запису для concurrency і watch, але не є порядковим номером для арифметики.
generation збільшується за релевантних змін desired state. Controller записує status.observedGeneration, коли обробив відповідну generation. Рівність цих значень ще не доводить готовність, тому її читають разом із Conditions.
creationTimestamp, deletionTimestamp, finalizers, ownerReferences і managedFields описують lifecycle та відносини. Status зазвичай оновлюється через окремий subresource, щоб controller не володів користувацьким spec.
5. Теорія: kubeconfig, команди та представлення
Context пов’язує cluster, user і необов’язковий default namespace. Перемикання context змінює ціль наступних команд, тому identity та endpoint підтверджують до будь-якої зміни. kubeconfig містить чутливі credentials і не має потрапляти до Git або спільного архіву.
get дає огляд, describe поєднує стан і Events, -o yaml/json зберігає машинне представлення, JSONPath дістає відомі поля, --watch продовжує потік змін. Табличний human-readable output зручний оператору, але нестабільний як інтерфейс automation.
kubectl config current-context
kubectl config view --minify
kubectl auth whoami
kubectl api-resources --api-group=apps
kubectl api-versions
kubectl explain deployment.spec.strategy
kubectl get --raw='/apis/apps/v1' | head -c 8006. Теорія: declarative apply і field ownership
create створює відсутній об’єкт, replace надсилає повне актуальне представлення з resourceVersion, patch змінює частину об’єкта, apply узгоджує заявлену конфігурацію з live state. Imperative команди корисні для дослідження та обмежених операцій, але відтворюваний desired state зберігається в manifests.
Server-Side Apply призначає field manager і фіксує володіння полями в managedFields. Два manager можуть володіти різними полями одного об’єкта. Конфлікт виникає, коли один намагається змінити поле іншого. --force-conflicts означає свідомо забрати володіння; застосовувати його без аналізу не можна.
Client dry-run перевіряє локальну генерацію та частину schema, server dry-run проходить актуальні server-side defaulting, admission і validation без збереження. kubectl diff показує прогноз live → desired і повертає exit code 1, коли знайдено відмінності; це не помилка перевірки.
7. Теорія: create, update, watch і delete
API mutation створює нову resourceVersion, а watch доставляє послідовність подій відносно обраної версії. Для надто старої версії сервер може повернути 410 Gone, після чого клієнт повторює list і продовжує watch з актуального стану.
Delete спочатку може виставити deletionTimestamp. Finalizers утримують об’єкт, доки відповідальний controller не завершить cleanup. OwnerReferences дозволяють garbage collector видаляти dependents відповідно до propagation policy. Force delete обходить частину гарантій і не є звичайним способом усунення завислого ресурсу.
8. Методичні вказівки
- Підтвердьте context, API endpoint, identity і namespace
- Дослідіть resource через api-resources та explain до написання manifest
- Застосовуйте Namespace окремо до namespaced resources: набір YAML-документів не є транзакцією
- Перед apply використовуйте server dry-run і diff
- Для scripts обирайте JSON/YAML/JSONPath і перевіряйте exit codes
- Не видаляйте resourceVersion зі сценарію replace і не обходьте Conflict нескінченним retry
- Перед --force-conflicts визначте поточного field manager і власника процесу
- Не редагуйте status як користувацький desired state
9. Лабораторна робота: discovery і server-side apply
Створіть два окремі manifests. Спочатку застосуйте Namespace і дочекайтеся Active, потім перевірте Deployment через server dry-run та виконайте Server-Side Apply з явним field manager.
mkdir -p /tmp/k8s-m02
cat > /tmp/k8s-m02/namespace.yaml <<'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: k8s-m02
labels:
course.goretskiy.pro/module: "02"
EOF
cat > /tmp/k8s-m02/app.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: k8s-m02
labels:
app.kubernetes.io/name: web
app.kubernetes.io/managed-by: kubectl
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: web
template:
metadata:
labels:
app.kubernetes.io/name: web
spec:
containers:
- name: nginx
image: nginx:1.29-alpine
ports:
- name: http
containerPort: 80
EOFkubectl apply --dry-run=server -f /tmp/k8s-m02/namespace.yaml
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m02/namespace.yaml
kubectl wait --for=jsonpath='{.status.phase}'=Active namespace/k8s-m02 --timeout=30s
kubectl apply --dry-run=server -f /tmp/k8s-m02/app.yaml
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m02/app.yaml
kubectl -n k8s-m02 rollout status deployment/web --timeout=120sРозділення файлів принципове: multi-document apply не є транзакцією й не має використовуватися як гарантія доступності Namespace до наступного namespaced object.
10. Лабораторна робота: metadata, diff і watch
Зіставте uid, resourceVersion, generation, observedGeneration і managers. Команда watch блокує термінал; виконуйте її окремо та завершіть Ctrl+C після спостереження змін.
kubectl -n k8s-m02 get deployment web -o jsonpath='uid={.metadata.uid}{"\n"}resourceVersion={.metadata.resourceVersion}{"\n"}generation={.metadata.generation}{"\n"}observedGeneration={.status.observedGeneration}{"\n"}'
kubectl -n k8s-m02 get deployment web -o jsonpath='{range .metadata.managedFields[*]}manager={.manager} operation={.operation} subresource={.subresource}{"\n"}{end}'
kubectl -n k8s-m02 get deployment web -o yaml
kubectl -n k8s-m02 get pods --watchЗмініть replicas у початковому manifest. Спочатку дослідіть diff, потім застосуйте desired state та дочекайтеся controller.
sed -i 's/replicas: 2/replicas: 3/' /tmp/k8s-m02/app.yaml
kubectl diff -f /tmp/k8s-m02/app.yaml || [ "$?" -eq 1 ]
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m02/app.yaml
kubectl -n k8s-m02 rollout status deployment/web --timeout=120s
kubectl -n k8s-m02 get deployment web -o jsonpath='generation={.metadata.generation}{"\n"}observedGeneration={.status.observedGeneration}{"\n"}replicas={.status.readyReplicas}{"\n"}'Очікується нова resourceVersion, збільшення generation і подальше вирівнювання observedGeneration. uid лишається попереднім, бо Deployment оновлено, а не перестворено.
11. Діагностика: schema, stale version і SSA conflict
Збережіть стару версію об’єкта, змініть replicas через scale і спробуйте replace старим YAML. API поверне Conflict: stale resourceVersion не має перезаписувати новіший стан.
kubectl -n k8s-m02 get deployment web -o yaml > /tmp/k8s-m02/web-stale.yaml
kubectl -n k8s-m02 scale deployment/web --replicas=4
kubectl replace -f /tmp/k8s-m02/web-stale.yaml || true
kubectl -n k8s-m02 get deployment web -o jsonpath='{range .metadata.managedFields[*]}manager={.manager} operation={.operation} subresource={.subresource}{"\n"}{end}'
kubectl -n k8s-m02 scale deployment/web --replicas=3
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m02/app.yaml
kubectl -n k8s-m02 rollout status deployment/web --timeout=120sПісля scale replicas належить manager kubectl із subresource scale. Повернення до значення manifest узгоджує наміри, після чого course-lab знову може застосувати конфігурацію без захоплення чужого поля.
Тепер надішліть об’єкт, де рядок two використано замість integer. Server dry-run поверне BadRequest до створення ресурсу.
cat > /tmp/k8s-m02/invalid.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: invalid
namespace: k8s-m02
spec:
replicas: two
selector:
matchLabels:
app: invalid
template:
metadata:
labels:
app: invalid
spec:
containers:
- name: nginx
image: nginx:1.29-alpine
EOF
kubectl apply --dry-run=server -f /tmp/k8s-m02/invalid.yaml || true
kubectl get deployment invalid -n k8s-m02 2>&1 || true- 401 Unauthorized — credentials відсутні або не прийняті
- 403 Forbidden — identity встановлено, але authorization заборонила дію
- 404 NotFound — неправильні resource, name, namespace або API endpoint
- 409 Conflict — застаріла resourceVersion або SSA field conflict
- 422 Invalid — об’єкт зрозумілий API, але порушує validation rules
- 429 Too Many Requests — спрацювало обмеження потоку API
12. Контроль, критерії засвоєння та cleanup
- Чим GVK відрізняється від GVR?
- Що обирає context і чому він не надає RBAC?
- Чим client dry-run відрізняється від server dry-run?
- Чому kubectl diff з exit code 1 може бути успішною перевіркою?
- Для чого потрібні uid, resourceVersion і generation?
- Як observedGeneration допомагає читати controller status?
- Чому набір YAML-документів не є транзакцією?
- Коли SSA зупиняється на field conflict?
- Чому force delete і --force-conflicts потребують окремого обґрунтування?
kubectl delete namespace k8s-m02 --wait=true
rm -rf /tmp/k8s-m02
kubectl get namespace k8s-m02 2>&1 || trueМодуль засвоєно, якщо ви досліджуєте schema живого API, прогнозуєте mutation через dry-run і diff, пояснюєте metadata lifecycle та розв’язуєте Conflict без втрати чужої зміни.