Модуль 3 / 19
Namespaces, labels, annotations, selectors та ownership
Зміст
1. Цілі та результат модуля
Після модуля ви зможете проєктувати зрозумілу metadata-схему, обирати ресурси без розбору імен, розрізняти labels та annotations, знаходити помилки selectors і доводити ownership за uid.
- Namespace задає scope імені, але сам по собі не є межею безпеки
- Labels призначені для ідентифікації та вибірки
- Annotations зберігають неідентифікувальні дані
- OwnerReferences формують lifecycle graph для garbage collector
- Finalizers і ownership розв’язують різні задачі
2. Теорія: namespaces, scope та імена
Identity namespaced object утворюють API resource, namespace та name. Cluster-scoped об’єкти, наприклад Node, Namespace і StorageClass, namespace не мають. Неправильний namespace зазвичай дає NotFound, навіть якщо однойменний об’єкт існує в іншому scope.
Namespace організовує команди, середовища та policy scope, але ізоляція з’являється лише разом із RBAC, NetworkPolicy, quotas і admission. Namespace default — звичайний namespace, а не місце для всіх застосунків.
- metadata.name після створення не змінюється
- generateName дозволяє API Server додати унікальний суфікс
- імена багатьох ресурсів підпорядковуються DNS-обмеженням
- видалення Namespace каскадно видаляє namespaced content
3. Теорія: labels і таксономія
Label — індексована пара key/value для стабільних ознак, за якими controllers, Services, policies та оператори обирають об’єкти. Зміна label може негайно змінити поведінку системи, тому це частина API-контракту.
Використовуйте app.kubernetes.io/name, instance, version, component, part-of і managed-by, а організаційні ключі розміщуйте під власним DNS prefix. Secrets, довгі тексти та значення високої cardinality для labels не підходять.
4. Теорія: annotations
Annotations підходять для runbook URL, власника, change ticket, checksum або налаштувань інтеграції. Вони допускають об’ємніші значення, але не беруть участі в label selectors.
Annotation не є секретним сховищем. Значення, які читає automation, потрібно документувати й версіонувати як контракт.
5. Теорія: equality та set-based selectors
Equality selectors використовують =, == та !=. Set-based selectors підтримують in, notin, наявність key і відсутність !key. Умови через кому поєднуються AND; універсального OR між різними ключами та substring search немає.
Оператори != і notin також збігаються з об’єктами, у яких зазначений key відсутній; це відрізняється від вимоги existence. Порожній результат kubectl get зазвичай повертає exit code 0: запит коректний, але збігів немає. Script має перевіряти кількість та identity, оператор — namespace і --show-labels.
6. Теорія: ownership, garbage collection і finalizers
OwnerReference містить kind, name та uid власника. Deployment володіє ReplicaSet, ReplicaSet — Pods. Однакові labels допомагають знайти об’єкти, але ownership не створюють.
За background deletion власник зникає раніше dependents, foreground очікує їх видалення, orphan залишає їх. Cross-namespace ownership обмежена.
Finalizer затримує остаточне видалення після deletionTimestamp, доки controller виконує cleanup. Це зобов’язання controller, а не механізм володіння; невідомий finalizer не можна видаляти без аналізу наслідків.
7. Методичні вказівки
- Визначте scope через kubectl api-resources
- Зафіксуйте словник labels і відповідальних за нього
- Застосовуйте власний DNS prefix
- Не кодуйте всю metadata в name
- Не змінюйте selector робочого workload для випуску версії
- За порожньої вибірки перевіряйте namespace, key, value та operator
- Розрізняйте label selection і ownerReferences
- Перед видаленням Namespace перелічіть ресурси ширше, ніж kubectl get all
8. Лабораторна робота: metadata-контракт
Створіть Namespace і три ConfigMap зі спільною таксономією. Спочатку виконується server-side validation, потім запис.
mkdir -p /tmp/k8s-m03
cat > /tmp/k8s-m03/namespace.yaml <<'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: k8s-m03
labels:
course.goretskiy.pro/module: "03"
environment: training
annotations:
course.goretskiy.pro/purpose: "metadata and selectors laboratory"
EOF
cat > /tmp/k8s-m03/resources.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: storefront-api-config
namespace: k8s-m03
labels:
app.kubernetes.io/name: storefront
app.kubernetes.io/component: api
app.kubernetes.io/part-of: course-platform
app.kubernetes.io/managed-by: kubectl
course.goretskiy.pro/environment: dev
annotations:
course.goretskiy.pro/owner: platform-team
course.goretskiy.pro/runbook: "https://example.invalid/runbooks/storefront"
data:
LOG_LEVEL: info
---
apiVersion: v1
kind: ConfigMap
metadata:
name: storefront-worker-config
namespace: k8s-m03
labels:
app.kubernetes.io/name: storefront
app.kubernetes.io/component: worker
app.kubernetes.io/part-of: course-platform
app.kubernetes.io/managed-by: kubectl
course.goretskiy.pro/environment: dev
course.goretskiy.pro/temporary: "true"
annotations:
course.goretskiy.pro/owner: platform-team
data:
QUEUE: orders
---
apiVersion: v1
kind: ConfigMap
metadata:
name: billing-api-config
namespace: k8s-m03
labels:
app.kubernetes.io/name: billing
app.kubernetes.io/component: api
app.kubernetes.io/part-of: course-platform
app.kubernetes.io/managed-by: kubectl
course.goretskiy.pro/environment: stage
annotations:
course.goretskiy.pro/owner: finance-team
data:
LOG_LEVEL: warn
EOFkubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m03/namespace.yaml
kubectl wait --for=jsonpath='{.status.phase}'=Active namespace/k8s-m03 --timeout=30s
kubectl apply --dry-run=server -f /tmp/k8s-m03/resources.yaml
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m03/resources.yaml
kubectl get namespace k8s-m03 --show-labels
kubectl -n k8s-m03 get configmaps --show-labels9. Лабораторна робота: selectors і mutation
Перевірте equality, AND, set membership, inequality та відсутність ключа. Потім змініть label і переконайтеся, що об’єкт одразу перейшов до іншої вибірки.
kubectl -n k8s-m03 get configmaps -l app.kubernetes.io/name=storefront
kubectl -n k8s-m03 get configmaps -l app.kubernetes.io/name=storefront,app.kubernetes.io/component=api
kubectl -n k8s-m03 get configmaps -l 'course.goretskiy.pro/environment in (dev,stage)'
kubectl -n k8s-m03 get configmaps -l 'app.kubernetes.io/component!=worker'
kubectl -n k8s-m03 get configmaps -l '!course.goretskiy.pro/temporary'
kubectl -n k8s-m03 label configmap/storefront-worker-config course.goretskiy.pro/environment=stage --overwrite
kubectl -n k8s-m03 annotate configmap/storefront-worker-config course.goretskiy.pro/change-ticket=CHG-1042
kubectl -n k8s-m03 get configmaps -l course.goretskiy.pro/environment=dev --show-labels10. Лабораторна робота: граф власників
Deployment використовується лише для спостереження ownership; rollout докладно розглядається в модулі 5. Порівняйте uid об’єктів з uid в ownerReferences і доведіть ланцюжок Deployment → ReplicaSet → Pod.
cat > /tmp/k8s-m03/deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: k8s-m03
labels:
app.kubernetes.io/name: storefront
app.kubernetes.io/component: web
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: storefront
app.kubernetes.io/component: web
template:
metadata:
labels:
app.kubernetes.io/name: storefront
app.kubernetes.io/component: web
spec:
containers:
- name: nginx
image: nginx:1.29-alpine
EOF
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m03/deployment.yaml
kubectl -n k8s-m03 rollout status deployment/web --timeout=120s
rs=$(kubectl -n k8s-m03 get replicasets -l app.kubernetes.io/component=web -o jsonpath='{.items[0].metadata.name}')
pod=$(kubectl -n k8s-m03 get pods -l app.kubernetes.io/component=web -o jsonpath='{.items[0].metadata.name}')
kubectl -n k8s-m03 get replicaset "$rs" -o jsonpath='{range .metadata.ownerReferences[*]}owner={.kind}/{.name} uid={.uid}{"\n"}{end}'
kubectl -n k8s-m03 get pod "$pod" -o jsonpath='{range .metadata.ownerReferences[*]}owner={.kind}/{.name} uid={.uid}{"\n"}{end}'
kubectl -n k8s-m03 get deployment web -o jsonpath='deployment-uid={.metadata.uid}{"\n"}'
kubectl -n k8s-m03 get replicaset "$rs" -o jsonpath='replicaset-uid={.metadata.uid}{"\n"}'11. Діагностика: порожня вибірка, scope та immutable selector
Перший запит коректний, але обирає нуль об’єктів. Потім відтворюється namespace mismatch. Спроба змінити selector Deployment відхиляється як immutable, а робочий об’єкт залишається незмінним.
kubectl -n k8s-m03 get configmaps -l course.goretskiy.pro/environment=qa
kubectl -n k8s-m03 get configmaps --show-labels
kubectl -n default get configmap storefront-api-config 2>&1 || true
kubectl -n k8s-m03 get configmap storefront-api-config
kubectl -n k8s-m03 patch deployment web --type=merge \
-p '{"spec":{"selector":{"matchLabels":{"course.goretskiy.pro/track":"canary"}},"template":{"metadata":{"labels":{"course.goretskiy.pro/track":"canary"}}}}}' || true
kubectl -n k8s-m03 get deployment web -o jsonpath='selector={.spec.selector.matchLabels}{"\n"}template={.spec.template.metadata.labels}{"\n"}'Для нової identity workload створюють новий об’єкт і планують міграцію, а не обходять validation наявного Deployment.
12. Контроль, критерії засвоєння та cleanup
- З чого складається identity namespaced object?
- Чому Namespace не гарантує isolation?
- Що зберігати в label, а що в annotation?
- Як поєднуються selector requirements?
- Чому нульова вибірка небезпечна для script?
- Чи створюють однакові labels ownership?
- Навіщо ownerReference містить uid?
- Чим finalizer відрізняється від ownerReference?
- Чому selector Deployment immutable?
kubectl delete namespace k8s-m03 --wait=true
rm -rf /tmp/k8s-m03
kubectl get namespace k8s-m03 2>&1 || trueМодуль засвоєно, якщо ви проєктуєте taxonomy, використовуєте різні selectors, доводите ownership за uid та безпечно діагностуєте порожню вибірку.