← До програми курсу

Модуль 3 / 19

Namespaces, labels, annotations, selectors та ownership

KUBERNETES

Зміст
01

1. Цілі та результат модуля

Після модуля ви зможете проєктувати зрозумілу metadata-схему, обирати ресурси без розбору імен, розрізняти labels та annotations, знаходити помилки selectors і доводити ownership за uid.

  • Namespace задає scope імені, але сам по собі не є межею безпеки
  • Labels призначені для ідентифікації та вибірки
  • Annotations зберігають неідентифікувальні дані
  • OwnerReferences формують lifecycle graph для garbage collector
  • Finalizers і ownership розв’язують різні задачі
02

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
03

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 не підходять.

04

4. Теорія: annotations

Annotations підходять для runbook URL, власника, change ticket, checksum або налаштувань інтеграції. Вони допускають об’ємніші значення, але не беруть участі в label selectors.

Annotation не є секретним сховищем. Значення, які читає automation, потрібно документувати й версіонувати як контракт.

05

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.

06

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 не можна видаляти без аналізу наслідків.

07

7. Методичні вказівки

  • Визначте scope через kubectl api-resources
  • Зафіксуйте словник labels і відповідальних за нього
  • Застосовуйте власний DNS prefix
  • Не кодуйте всю metadata в name
  • Не змінюйте selector робочого workload для випуску версії
  • За порожньої вибірки перевіряйте namespace, key, value та operator
  • Розрізняйте label selection і ownerReferences
  • Перед видаленням Namespace перелічіть ресурси ширше, ніж kubectl get all
08

8. Лабораторна робота: metadata-контракт

Створіть Namespace і три ConfigMap зі спільною таксономією. Спочатку виконується server-side validation, потім запис.

terminal
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
EOF
terminal
kubectl 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-labels
09

9. Лабораторна робота: selectors і mutation

Перевірте equality, AND, set membership, inequality та відсутність ключа. Потім змініть label і переконайтеся, що об’єкт одразу перейшов до іншої вибірки.

terminal
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-labels
10

10. Лабораторна робота: граф власників

Deployment використовується лише для спостереження ownership; rollout докладно розглядається в модулі 5. Порівняйте uid об’єктів з uid в ownerReferences і доведіть ланцюжок Deployment → ReplicaSet → Pod.

terminal
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

11. Діагностика: порожня вибірка, scope та immutable selector

Перший запит коректний, але обирає нуль об’єктів. Потім відтворюється namespace mismatch. Спроба змінити selector Deployment відхиляється як immutable, а робочий об’єкт залишається незмінним.

terminal
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

12. Контроль, критерії засвоєння та cleanup

  • З чого складається identity namespaced object?
  • Чому Namespace не гарантує isolation?
  • Що зберігати в label, а що в annotation?
  • Як поєднуються selector requirements?
  • Чому нульова вибірка небезпечна для script?
  • Чи створюють однакові labels ownership?
  • Навіщо ownerReference містить uid?
  • Чим finalizer відрізняється від ownerReference?
  • Чому selector Deployment immutable?
terminal
kubectl delete namespace k8s-m03 --wait=true
rm -rf /tmp/k8s-m03
kubectl get namespace k8s-m03 2>&1 || true

Модуль засвоєно, якщо ви проєктуєте taxonomy, використовуєте різні selectors, доводите ownership за uid та безпечно діагностуєте порожню вибірку.