Helm и GitOps

Складывать десятки YAML вручную через kubectl apply не масштабируется; учимся шаблонизировать релизы через Helm и доставлять их декларативно через GitOps.

GitOps — модель доставки, где желаемое состояние кластера целиком описано в git-репозитории, а специальный агент непрерывно приводит реальный кластер в соответствие с тем, что записано в git.

Зачем это на практике

В базовом разделе Helm упоминался как «пакетный менеджер»: helm install — и приложение в кластере. Но Helm — это прежде всего шаблонизатор, и именно эта его сторона незаменима на проде. Одно приложение нужно развернуть в dev, staging и prod — манифесты почти одинаковы, отличаются образом, числом реплик, доменом. Копировать три набора YAML и руками их синхронизировать — путь к рассинхрону и ошибкам. Helm параметризует один набор шаблонов, а различия выносит в файлы значений.

GitOps решает смежную задачу: как доставлять эти релизы безопасно и воспроизводимо. Вместо того чтобы инженер с ноутбука делал helm upgrade в прод (а кто и что накатил — попробуй вспомни), всё состояние лежит в git, а агент в кластере сам подтягивает изменения. git становится единственным источником истины.

Анатомия Helm-чарта

Чарт — это каталог определённой структуры:

mychart/
  Chart.yaml          # метаданные: имя, версия чарта, версия приложения
  values.yaml         # значения по умолчанию
  templates/          # шаблоны манифестов
    deployment.yaml
    service.yaml
  charts/             # вложенные чарты-зависимости

Файл values.yaml хранит параметры по умолчанию, а шаблоны в templates/ подставляют их через синтаксис Go-шаблонов {{ ... }}. Вот фрагмент templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-web
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: web
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

Соответствующий values.yaml:

replicaCount: 2
image:
  repository: myapp
  tag: "1.4.0"
resources:
  requests:
    cpu: 250m
    memory: 256Mi

Теперь для прода не нужен отдельный чарт — достаточно файла с переопределениями:

# отдельные значения для прода
helm upgrade --install web ./mychart \
  --namespace prod \
  --values values-prod.yaml \
  --set image.tag=1.4.1

# увидеть итоговый YAML, ничего не применяя
helm template web ./mychart --values values-prod.yaml

helm template — крайне полезная команда: она рендерит шаблоны локально и показывает финальные манифесты, не трогая кластер. Незаменимо, чтобы поймать ошибку в шаблоне до деплоя.

Релизы и откаты

Каждый helm install/upgrade создаёт пронумерованный релиз — снимок применённого состояния. Helm хранит историю релизов (по умолчанию в Secret внутри namespace), и это даёт мгновенный откат:

helm history web
# REVISION  STATUS      CHART        APP VERSION
# 1         superseded  mychart-0.1  1.4.0
# 2         deployed    mychart-0.1  1.4.1

# откат к предыдущей ревизии одной командой
helm rollback web 1

Откат через helm rollback возвращает весь набор объектов релиза к состоянию ревизии 1 — это надёжнее, чем вручную править Deployment. Команда helm history показывает, что и когда катилось.

GitOps: git как источник истины

Классический CD — это push: CI-пайплайн после сборки выполняет kubectl apply или helm upgrade, толкая изменения в кластер. У этого подхода есть слабые места: CI нужны полные права на прод-кластер (большая поверхность атаки), а реальное состояние кластера может разойтись с тем, что в репозитории, и никто этого не заметит.

GitOps переворачивает модель на pull. В кластере живёт агент (ArgoCD или Flux), который сам следит за git-репозиторием с манифестами/чартами. Заметив новый коммит, он применяет изменения; заметив, что кто-то руками поменял ресурс в кластере (дрифт), — возвращает его к тому, что записано в git. Развёртывание превращается в обычный git-коммит и pull request, а откат — в git revert.

АспектPush CD (CI делает apply)GitOps (агент тянет из git)
Источник истинычто последним накатил CIgit-репозиторий
Доступ к кластеруу CI полные права на продагент внутри кластера, CI прод не трогает
Дрифтвозможен и незаметенагент обнаруживает и чинит
Откатперезапуск пайплайнаgit revert

ArgoCD и Flux

Два главных GitOps-агента. ArgoCD — с богатым веб-интерфейсом, наглядно показывает дерево ресурсов и статус синхронизации (Synced/OutOfSync). Приложение для него описывается так:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web
spec:
  source:
    repoURL: https://github.com/org/infra.git
    path: charts/web
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: prod
  syncPolicy:
    automated:
      prune: true       # удалять то, чего больше нет в git
      selfHeal: true    # откатывать ручные правки в кластере

Флаги prune и selfHeal — суть GitOps: prune убирает из кластера объекты, удалённые из git, а selfHeal возвращает ручные изменения к состоянию репозитория. Flux решает ту же задачу, но без обязательного UI, более «кубернетес-нативно» (всё через CRD), и часто компактнее в составе кластера. Выбор между ними — скорее вопрос вкуса и потребности в интерфейсе.

Как это работает под капотом

Helm не хранит состояние в каком-то внешнем сервере: историю релизов он пишет прямо в кластер — в объекты Secret (тип helm.sh/release.v1) в namespace релиза. Поэтому helm rollback работает даже с другой машины: вся история уже лежит в кластере, а не на вашем ноутбуке. Сами шаблоны Helm рендерит на стороне клиента (Helm 3 не требует серверного Tiller): helm install разворачивает {{ ... }} в готовый YAML и отправляет его в API-сервер обычным apply.

GitOps-агент устроен как контроллер с циклом сверки (reconciliation loop): раз в несколько минут он сравнивает желаемое состояние (рендер из git) с фактическим (что в кластере) и применяет разницу. Это тот же принцип, на котором держится весь Kubernetes — контроллеры постоянно приводят реальность к декларации, — только источником декларации становится git, а не один лишь etcd. Поэтому GitOps так органичен для Kubernetes: он просто продлевает родную модель «контроллер сверяет желаемое с текущим» на весь процесс доставки.

Частые ошибки

  • Деплоить без helm template/--dry-run. Ошибка в Go-шаблоне всплывёт уже в кластере; сначала отрендерьте локально.
  • Хранить секреты открытым текстом в git. При GitOps репозиторий — источник истины, но не место для паролей; используйте Sealed Secrets / SOPS / внешнее хранилище.
  • Править ресурсы руками при включённом selfHeal. Агент тут же откатит ваше изменение к git — меняйте через коммит, а не kubectl edit.
  • Откатывать прод через kubectl edit вместо helm rollback. Ручная правка рассинхронит релиз с историей Helm и состоянием git.
  • Давать CI полные права на прод-кластер при наличии GitOps. Это сводит на нет главное преимущество pull-модели — закрытую поверхность доступа.

Итоги

  • Helm — это шаблонизатор: один набор шаблонов templates/ + разные values на окружение вместо копий YAML.
  • helm template рендерит манифесты локально; релизы пронумерованы, helm rollback возвращает весь набор объектов.
  • GitOps делает git единственным источником истины; агент в кластере (ArgoCD/Flux) тянет изменения по pull-модели.
  • prune и selfHeal устраняют дрифт: удаляют лишнее и откатывают ручные правки к состоянию репозитория.
  • Деплой превращается в коммит, откат — в git revert; CI больше не нужны прямые права на прод-кластер.
Проверьте себя
1. В чём основная ценность Helm для развёртывания приложения в нескольких окружениях?
AОн шифрует все манифесты перед отправкой в кластер
BОн шаблонизирует один набор манифестов, а различия окружений выносит в файлы values
CОн полностью заменяет kubectl и API-сервер
DОн хранит образы контейнеров вместо реестра
2. Чем GitOps-доставка (pull) отличается от классического push-CD?
AВ GitOps CI напрямую делает kubectl apply в прод-кластер
BВ GitOps агент внутри кластера сам тянет изменения из git и держит кластер в соответствии с репозиторием, а git становится источником истины
CGitOps работает только без Helm
DGitOps хранит состояние кластера на ноутбуке инженера
3. Что делает опция selfHeal у ArgoCD-приложения?
AПерезапускает упавшие поды приложения
BВозвращает ручные изменения ресурсов в кластере к состоянию, описанному в git
CАвтоматически повышает версию Helm-чарта
DШифрует секреты в репозитории