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) |
| Источник истины | что последним накатил CI | git-репозиторий |
| Доступ к кластеру | у 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 больше не нужны прямые права на прод-кластер.