GitOps с ArgoCD: declarative deployments, sync policies, rollback
Вы развернули Node.js приложение в Kubernetes через kubectl apply -f deployment.yaml. Работает. Теперь нужно развернуть то же приложение в staging с другими переменными окружения, другим количеством реплик и другим ingress. Копируете YAML, меняете значения, применяете. Через месяц у вас 15 почти идентичных файлов с дублированным кодом. Helm решает эту проблему — это package manager для Kubernetes, который превращает набор манифестов в переиспользуемый шаблон.
Зачем Helm: package manager для Kubernetes
Helm — это apt/yum/npm для Kubernetes. Он упаковывает манифесты (Deployment, Service, Ingress) в единицу, называемую chart, и отделяет конфигурацию от структуры через values.yaml. Вместо копирования YAML вы пишете шаблон один раз и переопределяете значения для каждого окружения.
Преимущества: переиспользование (один chart для dev/staging/prod), версионирование (можно откатиться), зависимости (chart может зависеть от PostgreSQL, Redis), шаблонизация (условия, циклы, функции), release management (Helm отслеживает установки и историю).
Структура chart
Создание chart:
helm create myapp
Структура:
myapp/
├── Chart.yaml # Метаданные
├── values.yaml # Значения по умолчанию
├── charts/ # Зависимости
├── templates/ # Шаблоны манифестов
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── _helpers.tpl # Вспомогательные функции
│ └── NOTES.txt
└── .helmignore
Chart.yaml
apiVersion: v2
name: myapp
description: Node.js application
type: application
version: 0.1.0 # Версия chart
appVersion: "1.0.0" # Версия приложения
dependencies:
- name: postgresql
version: "15.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
Templating: Go templates
Файлы в templates/ — это YAML с вставками Go template синтаксиса.
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Chart.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.port }}
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- if .Values.resources }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
{{- end }}
Ключевые конструкции:
{{ .Values.key }}— подстановка изvalues.yaml{{ .Chart.Name }}— имя chart{{ .Release.Name }}— имя release{{- ... }}— удаляет пробелы слева{{ if }} ... {{ end }}— условие{{ range }} ... {{ end }}— цикл{{ .value | quote }}— функция{{- toYaml .Values.obj | nindent 10 }}— конвертирует в YAML с отступом
_helpers.tpl: переиспользуемые фрагменты
{{- define "myapp.labels" -}}
app: {{ .Chart.Name }}
version: {{ .Chart.AppVersion }}
release: {{ .Release.Name }}
{{- end }}
Использование:
metadata:
labels:
{{- include "myapp.labels" . | nindent 4 }}
Values и overrides
values.yaml
replicaCount: 2
image:
repository: myregistry/myapp
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 3000
env:
- name: NODE_ENV
value: production
- name: PORT
value: "3000"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
Environment-specific values
values-dev.yaml:
replicaCount: 1
image:
tag: "dev"
env:
- name: NODE_ENV
value: development
values-prod.yaml:
replicaCount: 5
autoscaling:
enabled: true
Установка:
helm install myapp-dev ./myapp -f values-dev.yaml
helm install myapp-prod ./myapp -f values-prod.yaml
# Или через --set
helm install myapp ./myapp --set image.tag=1.2.3
Приоритет: values.yaml → -f values-prod.yaml → --set.
helm upgrade
helm upgrade myapp-prod ./myapp -f values-prod.yaml --set image.tag=1.2.4
# Откат
helm rollback myapp-prod
# История
helm history myapp-prod
Dependencies: subcharts
Добавьте зависимость в Chart.yaml:
dependencies:
- name: postgresql
version: "15.2.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
Скачивание:
helm dependency update ./myapp
Конфигурация subchart через values.yaml:
postgresql:
enabled: true
auth:
username: myapp
password: secret
database: myapp_db
primary:
persistence:
size: 10Gi
Отключение зависимости:
helm install myapp ./myapp --set postgresql.enabled=false
Использование в Deployment:
env:
- name: DATABASE_URL
value: "postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ .Release.Name }}-postgresql:5432/{{ .Values.postgresql.auth.database }}"
Практические советы
Проверка шаблонов:
# Рендер без установки
helm template myapp ./myapp -f values-prod.yaml
# Dry-run
helm install myapp ./myapp --dry-run --debug
# Lint
helm lint ./myapp
Упаковка и публикация:
helm package ./myapp
helm push myapp-0.1.0.tgz oci://myregistry.io/charts
Вывод
Helm превращает набор YAML-файлов в переиспользуемый, версионируемый пакет. Вместо копирования манифестов для каждого окружения вы пишете шаблон один раз и переопределяете значения через values.yaml. Go templates позволяют использовать условия, циклы и функции. Dependencies упрощают управление зависимостями — PostgreSQL, Redis устанавливаются автоматически.
Начните с helm create, адаптируйте шаблоны, создайте values-dev.yaml и values-prod.yaml. Используйте helm template и --dry-run для проверки. Добавляйте зависимости через Chart.yaml. Helm — стандарт де-факто для деплоя в Kubernetes, и в 2026 году управление сырыми YAML через kubectl apply — антипаттерн для любого проекта сложнее hello-world.