← К программе курса

Модуль 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: запрос корректен, но совпадений нет. Скрипт должен проверять количество и 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 и безопасно диагностируете пустую выборку.