lenec ru

← все посты

Helm charts: шаблонизация Kubernetes-манифестов на практике

15K

Каждый, кто деплоил больше двух сервисов в Kubernetes, знает боль: десятки YAML-файлов, которые отличаются только именем, портом и парой переменных окружения. Helm решает эту проблему — превращает манифесты в параметризованные шаблоны с управлением зависимостями и жизненным циклом релизов.

Зачем Helm: проблема копипасты YAML

Без Helm типичный деплой микросервиса — это 5–7 файлов: Deployment, Service, Ingress, HPA, ConfigMap, Secret, ServiceAccount. Для 10 сервисов получаем 50–70 файлов, где 80% содержимого идентично. Kustomize частично решает проблему через overlays, но не даёт полноценной логики (циклы, условия) и не управляет релизами.

Helm даёт:

  • Шаблонизацию — Go templates с функциями Sprig.
  • Пакетирование — чарт = единица деплоя с версионированием.
  • Управление релизами — install, upgrade, rollback с историей.
  • Зависимости — подключение subcharts (PostgreSQL, Redis) одной строкой.

Структура чарта

my-service/
├── Chart.yaml          # метаданные: имя, версия, зависимости
├── values.yaml         # дефолтные значения параметров
├── templates/
│   ├── _helpers.tpl    # переиспользуемые шаблоны
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── hpa.yaml
│   └── secrets.yaml
└── charts/             # зависимости (subcharts)

Chart.yaml — паспорт чарта:

apiVersion: v2
name: my-service
version: 1.3.0
appVersion: "2.1.0"
description: Backend API service
dependencies:
  - name: postgresql
    version: "15.x"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled

values.yaml — параметры по умолчанию, которые переопределяются при деплое через -f или --set.

Шаблонизация: Values, range, if, include

Шаблоны Helm используют Go template syntax. Основные конструкции:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-service.fullname" . }}
  labels:
    {{- include "my-service.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "my-service.selectorLabels" . | nindent 6 }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}
          env:
            {{- range $key, $val := .Values.env }}
            - name: {{ $key }}
              value: {{ $val | quote }}
            {{- end }}
          {{- if .Values.resources }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          {{- end }}

Ключевые приёмы:

  • {{ .Values.x }} — подстановка значения из values.yaml.
  • {{- range }} — цикл по map или list (дефис убирает лишние пробелы).
  • {{- if }} — условный рендеринг блока.
  • {{ include "name" . | nindent N }} — вставка именованного шаблона с отступом.
  • {{ toYaml .Values.x | nindent N }} — вставка произвольного YAML-блока.

Хелперы в _helpers.tpl:

{{- define "my-service.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

Хуки: pre-install, post-upgrade — миграции БД

Helm hooks позволяют запускать Job-ы в определённые моменты жизненного цикла релиза. Классический кейс — миграции базы данных перед обновлением приложения:

# templates/migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-service.fullname" . }}-migrate
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          command: ["npm", "run", "migrate"]
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: {{ include "my-service.fullname" . }}-secrets
                  key: database-url
  backoffLimit: 3

Доступные хуки: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete, pre-rollback, post-rollback. Вес (hook-weight) определяет порядок выполнения при нескольких хуках одного типа.

Зависимости: subchart и condition

Helm позволяет подключать другие чарты как зависимости. Это удобно для dev-окружений, где нужна локальная БД:

# Chart.yaml
dependencies:
  - name: postgresql
    version: "15.5.x"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
  - name: redis
    version: "19.x"
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled
# values.yaml
postgresql:
  enabled: true          # включено для dev
  auth:
    postgresPassword: devpass
    database: myapp

redis:
  enabled: false         # выключено, используем managed

В production переопределяем через values-prod.yaml:

# values-prod.yaml
postgresql:
  enabled: false   # используем RDS

redis:
  enabled: false   # используем ElastiCache

Команда деплоя: helm upgrade --install my-service ./my-service -f values-prod.yaml -n production.

Практика: деплоим микросервис с Ingress, HPA, Secrets

Полный values.yaml для production-ready сервиса:

replicaCount: 3

image:
  repository: ghcr.io/myorg/api-service
  tag: "2.1.0"
  pullPolicy: IfNotPresent

service:
  port: 8080
  type: ClusterIP

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: api.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: api-tls
      hosts:
        - api.example.com

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20
  targetCPUUtilization: 70

resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: "1"
    memory: 512Mi

env:
  NODE_ENV: production
  LOG_LEVEL: info

secrets:
  DATABASE_URL: "postgresql://user:pass@rds.amazonaws.com:5432/myapp"
  JWT_SECRET: "super-secret-key"

Шаблон HPA с условным рендерингом:

# templates/hpa.yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ include "my-service.fullname" . }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "my-service.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilization }}
{{- end }}

Деплой одной командой:

helm upgrade --install api-service ./my-service \
  -f values-prod.yaml \
  --set image.tag=$CI_COMMIT_SHA \
  --namespace production \
  --wait --timeout 5m

Флаг --wait заставляет Helm дождаться, пока все поды станут Ready. Если деплой не завершится за 5 минут — автоматический rollback.

Вывод

Helm превращает хаос из десятков YAML-файлов в управляемые, версионированные пакеты. Шаблонизация через Go templates покрывает 95% потребностей, хуки решают проблему миграций, а subcharts позволяют собирать окружение из готовых компонентов. Начните с простого чарта для одного сервиса, отладьте через helm template (рендерит без деплоя), и постепенно добавляйте HPA, Ingress и зависимости по мере роста проекта.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.