Kube/Pythonproduction course
Курс · RU10-configuration-management.md

10. Управление конфигурацией: исходный YAML, Kustomize, Helm, GitOps

Незнакомый термин? Откройте словарь Kubernetes и терминов курса.

Назначение, предварительные знания и результаты обучения

Нужны главы 00–09 и базовые манифесты. После главы вы сможете:

  • выбрать исходные манифесты, наложения Kustomize или chart Helm;
  • объяснить наложение patches в сравнении с шаблонизацией;
  • отделить успешный рендеринг от проверки API и среды исполнения;
  • получить детерминированное сравнение dev и prod;
  • обнаружить и устранить расхождение;
  • описать риски согласования, prune и секретов в GitOps;
  • не смешивать конфигурацию окружения со скопированным устаревшим YAML.

Ментальная модель: исходник → рендеринг → проверка → согласование

исходные файлы + входные данные среды
        ↓ рендеринг
полные объекты Kubernetes
        ↓ синтаксис/схема клиента
        ↓ схема/значения по умолчанию/admission/аутентификация сервера
сохранённые желаемые объекты
        ↓ контроллеры/среда исполнения
наблюдаемая нагрузка

Каждый этап отвечает на другой вопрос. helm template/kubectl kustomize могут успешно вывести синтаксически корректный YAML с несуществующим API, неправильным полем, отсутствующим Secret или невыполнимым планированием. Шаблонизация не равна проверке API; серверный dry-run не доказывает здоровую среду исполнения.

Три подхода

Исходные манифесты

Плюсы: Kubernetes API виден, меньше инструментов, удобно для обучения и небольшого сервиса. Минусы: дублирование окружений, ручные хеши rollout, распространение и версионирование. Исходный YAML уместен, пока повторение и API для потребителя не стали сложнее самого ресурса.

Kustomize

Kustomize выполняет структурные преобразования и patches без универсального языка шаблонов. Базовый набор остаётся корректным Kubernetes YAML, наложения выражают дельту. Встроен в kubectl -k.

Базовый набор:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - namespace.yaml
  - configmap.yaml
  - serviceaccount.yaml
  - deployment.yaml
  - service.yaml
  - ingress.yaml
  - networkpolicy.yaml
  - pdb.yaml
  - hpa.yaml
  - rbac.yaml

Наложение:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
patches:
  - path: deployment-patch.yaml
  - path: hpa-patch.yaml
  - path: configmap-patch.yaml

Это конфигурация для рендерера, а не объект Kubernetes API; у Kustomization нет metadata/spec. Его результат содержит полные объекты. Например, patch:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-api
  namespace: kube-course
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: api
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
              ephemeral-storage: 32Mi
            limits:
              cpu: 250m
              memory: 192Mi
              ephemeral-storage: 128Mi

Стратегическое слияние сопоставляет ресурс по GVK, имени и namespace, а элементы списка — по ключу слияния, например имени контейнера. Проверяйте отрендеренный результат: неверный patch списка может заменить весь список.

Helm

Chart Helm — упакованный параметризованный шаблон с Chart.yaml, values.yaml, шаблонами и состоянием выпуска. Он подходит для распространения ПО среди многих потребителей, условных ресурсов и пакетов экосистемы. Цена — сложность шаблонов Go, совместимость API значений, ошибки пробелов и типов и скрытый итоговый YAML.

helm template рендерит локально; helm install/upgrade управляет выпуском. Обработчики имеют отдельный жизненный цикл и могут сделать обновление неатомарным. CRDs из crds/ требуют отдельного плана обновления. --atomic полезен, но не откатывает внешние побочные эффекты и миграции БД. Всегда проверяйте:

helm lint ./chart
helm template python-api ./chart -f values-prod.yaml > /tmp/rendered.yaml
kubectl apply --dry-run=server -f /tmp/rendered.yaml

Курс не добавляет chart Helm ради дублирования работающих ресурсов Kustomize: задача — понять интерфейс и компромиссы. Для внутреннего приложения наложения проще. Для распространяемого компонента платформы chart может быть лучше.

Практическое упражнение: детерминированный рендеринг и сравнение

Рендеринг

kubectl kustomize manifests/overlays/dev > /tmp/python-api-dev.yaml
kubectl kustomize manifests/overlays/prod > /tmp/python-api-prod.yaml
kubectl kustomize manifests/overlays/dev > /tmp/python-api-dev-second.yaml
cmp /tmp/python-api-dev.yaml /tmp/python-api-dev-second.yaml

Ожидается код 0 от cmp: одинаковый исходник даёт побайтово идентичный результат рендеринга.

Проверяйте без ненадёжного визуального просмотра:

kubectl kustomize manifests/overlays/dev |
  kubectl apply --dry-run=client -f -
kubectl apply --dry-run=server -f /tmp/python-api-dev.yaml
kubectl diff -f /tmp/python-api-dev.yaml

Код 0 от kubectl diff означает отсутствие различий, 1 — наличие различий, а код больше 1 — ошибку. Не оборачивайте команду так, чтобы любой ненулевой код игнорировался.

Сравните значимые поля:

diff -u /tmp/python-api-dev.yaml /tmp/python-api-prod.yaml
rg -n 'replicas:|cpu:|memory:|APP_MESSAGE|FEATURE_COLOR' \
  /tmp/python-api-dev.yaml /tmp/python-api-prod.yaml

Ожидается: в dev меньше реплик, ресурсов и границ HPA, а в prod больше ресурсов. Сгенерированные порядок и детали могут зависеть от версии kubectl/Kustomize; фиксируйте версию инструмента в CI.

Применить наложение dev и проверить владение

test "$(kubectl config current-context)" = "kind-kube-course"
kubectl apply -k manifests/overlays/dev
kubectl rollout status deployment/python-api -n kube-course --timeout=180s
kubectl get deployment python-api -n kube-course \
  -o jsonpath='{.spec.replicas}{" replicas\n"}'

HPA может сразу изменить число реплик в разрешённом диапазоне, поэтому исходное replicas: 1 не гарантирует фактическое значение после согласования HPA. Это пример нескольких владельцев поля.

Упражнение по расхождениям

kubectl annotate service python-api -n kube-course \
  course.example.com/manual-drift=true --overwrite
kubectl diff -k manifests/overlays/dev
kubectl apply -k manifests/overlays/dev
kubectl get service python-api -n kube-course \
  -o jsonpath='{.metadata.annotations.course\.example\.com/manual-drift}{"\n"}'

Если аннотация не принадлежит менеджеру поля или источнику применения, применение может её не удалить. Определение расхождения зависит от владения. Удалите явно:

kubectl annotate service python-api -n kube-course \
  course.example.com/manual-drift-

Используйте metadata.managedFields для расследования владения полями, но не сохраняйте его в Git.

Секреты и сгенерированные имена

secret.example.yaml содержит только заглушку для лаборатории и схемы и намеренно не включён в Kustomization. Эксплуатационный конвейер обязан отдельно доставить Secret одобренным способом до rollout. Не помещайте настоящий секрет в значения Helm, отрендеренный артефакт или журнал CI.

Kustomize configMapGenerator/secretGenerator добавляет хеш содержимого в имя, а ссылки преобразуются; изменение меняет шаблон Pod и вызывает rollout. Плюсы — неизменяемая версия и автоматический rollout, минусы — сборка мусора и открытый текст Secret в локальном исходнике Kustomize, если генератор читает литеральное значение. Используйте процесс с зашифрованным или внешним секретом.

Обзор GitOps

Контроллер GitOps, например дополнения экосистемы Flux или Argo CD, получает и согласует объявленный источник с кластером. Он даёт непрерывное исправление расхождений, инвентаризацию и след проверки и аудита. Плохой манифест от этого не становится хорошим.

Проектные решения:

  • неизменяемые коммит, артефакт и digest как продвигаемая единица;
  • один авторитетный владелец каждого поля;
  • проверки здоровья и процедура rollback или возврата изменения;
  • prune удаляет отсутствующие объекты — это мощная разрушающая операция;
  • волны синхронизации, обработчики, CRDs и порядок зависимостей с возможной задержкой;
  • граница расшифрования секретов и ключи;
  • экстренное изменение с последующим согласованием исходника.

Не включайте автоматический prune во всём кластере до проверки инвентаризации, области действия и резервной копии.

Самостоятельная задача

Добавьте наложение staging только как разницу: две реплики, HPA 2–4, FEATURE_COLOR: amber, ресурсы и Pod Security, похожие на prod. Выполните рендеринг дважды и серверный dry-run, покажите сравнение с dev и prod. Критерий приёмки: базовый набор не скопирован, настоящих Secrets нет, все целевые patches нашли ровно один ресурс.

Сломанное наложение

apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-ap1
  namespace: kube-course
spec:
  replicas: 9

Добавьте patch с неверным именем во временное наложение. Современный Kustomize должен завершиться ненулевым кодом с no matches for Id ...; failed to find unique target. Если преобразователь создаёт неожиданный вывод, проверьте число и имена ресурсов:

kubectl kustomize /tmp/broken-overlay

Исправьте точные GVK, имя и namespace. Не добавляйте второй Deployment, чтобы «patch нашёлся»: это меняет архитектуру.

Дерево диагностических решений

Доставленное состояние не совпадает с ожидаемым
├─ рендеринг завершается ошибкой → путь, цель patch, функция/тип YAML/шаблона
├─ рендеринг успешен, клиент завершается ошибкой → локальная схема/синтаксис/версия инструмента
├─ клиент успешен, сервер отклоняет → версия API/admission/RBAC/неизменяемое поле
├─ сервер принимает, rollout завершается ошибкой → ссылка на конфигурацию/образ/назначение/проверка
└─ живое состояние отличается
   ├─ полем владеет HPA/контроллер → ожидаемое совместное владение
   ├─ полем владеет ручной менеджер → managedFields/политика расхождений
   ├─ неверный источник/revision GitOps → status контроллера/артефакт
   └─ мутация вебхуком/значения по умолчанию → сравнить объект сервера и результат рендеринга

Типичные ошибки: копирование каталогов окружений; шаблон заключает числа и логические значения в кавычки; helm template считается проверкой; плавающая зависимость chart; Secret из эксплуатационной среды попадает в значения или результат рендеринга; patch Kustomize незаметно заменяет список контейнеров или окружения; HPA и GitOps спорят; prune применяется без инвентаризации.

Эксплуатационные компромиссы, безопасность и стоимость

Более высокий уровень абстракции снижает повторение, но усложняет проверку и диагностику. Рендерер, дополнение и chart — код цепочки поставки; фиксируйте версии и digests, проверяйте происхождение. Отрендеренные манифесты могут содержать секреты и требуют политики срока хранения и доступа. Контроллер GitOps имеет широкие разрешения на запись и удаление: ограничивайте область namespace, где возможно, аудитируйте и защищайте ветку исходников. Сканирование расхождений и опрос API имеют стоимость для плоскости управления; выбирайте интервал по риску, а не секунды по умолчанию.

Самопроверка

  1. Проверяет ли helm template admission?
  2. Чем patch отличается от шаблона?
  3. Почему результат рендеринга нужно хранить и проверять в CI?
  4. Что означает код завершения 1 у kubectl diff?
  5. Почему фактическое число реплик может отличаться от исходника?
  6. Чем опасен GitOps prune?
  7. Должны ли настоящие секреты находиться в значениях Helm?

Ответы: 1) нет; 2) структурная разница для корректных ресурсов в сравнении с генерацией текста; 3) это фактическая полезная нагрузка API; 4) есть различия, это не ошибка выполнения; 5) HPA или другой владелец поля; 6) удаляет объекты, отсутствующие в инвентаризации; 7) нет.

Резюме и источники

Сохраняйте авторитетный источник, детерминированный рендеринг и явную цепочку проверок. Далее: промышленная эксплуатация.