← К программе курса

Модуль 15 / 19

Helm, Kustomize и воспроизводимая доставка

KUBERNETES

Содержание
01

1. Цели и результат модуля

Вы научитесь отделять исходные manifests от rendered output, выбирать Kustomize или Helm и проводить изменение через render, validation, diff, apply/upgrade и rollback.

  • Kustomize преобразует Kubernetes resources без шаблонного языка
  • Helm упаковывает templates, values и release lifecycle
  • rendered YAML нужно проверять до mutation
  • version chart не равна appVersion
  • секреты нельзя безопасно хранить в обычном values.yaml
02

2. Теория: воспроизводимая доставка

Воспроизводимость требует зафиксированных версий tool/chart/image, reviewable inputs и одинакового render. Команда, зависящая от latest, локальных файлов или ручного --set без записи, плохо воспроизводится.

Package manager не заменяет CI, policy, observability и backup. Он отвечает за формирование и lifecycle набора Kubernetes objects.

03

3. Kustomize: base и overlays

Kustomize работает со структурированными Kubernetes resources. Base содержит общее, overlay добавляет environment-specific namespace, patches, images, replicas и generators. Он встроен в kubectl как apply -k и kustomize.

configMapGenerator добавляет content hash к имени и обновляет references, поэтому изменение конфигурации меняет Pod template и может вызвать rollout.

04

4. Helm: chart, values и release

Chart содержит Chart.yaml, defaults values.yaml, templates и optional schema. Helm render использует release context и values; install/upgrade сохраняют release revisions в кластере. appVersion — метаданные приложения, version — версия package chart.

Values — публичный configuration interface chart. Слишком гибкие templates становятся непроверяемой программой; values.schema.json делает контракт явным.

05

5. Install, upgrade и rollback

upgrade --install идемпотентно выбирает создание или изменение release. --wait ждёт готовности поддерживаемых resources до timeout. history показывает revisions, rollback создаёт новую revision на основе старой — не перематывает историю clock.

Rollback manifests не откатывает внешние side effects, database migration или содержимое PV. Совместимость данных планируется отдельно.

06

6. CRD, hooks, secrets и ownership

CRDs в chart имеют особый lifecycle и обычно не удаляются автоматически. Hooks — Jobs/resources вне обычного порядка, их side effects нужно делать идемпотентными. Перед установкой чужого chart изучайте rendered RBAC, securityContext, images и cluster-scoped objects.

Helm values и rendered Secret — не secret storage. Используйте внешнее управление секретами и не передавайте чувствительные значения в command history.

07

7. Методические указания

  • Фиксируйте tool, chart и image versions
  • Сначала lint/render, затем server dry-run и diff
  • Сохраняйте environment inputs в Git
  • Не редактируйте rendered YAML вручную
  • Не смешивайте ownership Helm и kubectl одного поля
  • Проверяйте cluster-scoped resources и hooks
  • Тестируйте rollback вместе с данными
  • Не используйте --set для недокументированных production changes
08

8. Лабораторная работа: Kustomize overlay

Base задаёт один Deployment, overlay — namespace, prefix, две replicas и APP_MODE=lab. Проверьте hash ConfigMap и автоматически переписанную reference.

terminal
mkdir -p /tmp/k8s-m15/kustomize/base /tmp/k8s-m15/kustomize/overlays/lab
cat > /tmp/k8s-m15/kustomize/base/deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata: {name: app}
spec:
  replicas: 1
  selector: {matchLabels: {app: kustomize-web}}
  template:
    metadata: {labels: {app: kustomize-web}}
    spec:
      containers:
      - name: nginx
        image: nginx:1.29-alpine
        envFrom: [{configMapRef: {name: app-config}}]
EOF
cat > /tmp/k8s-m15/kustomize/base/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources: [deployment.yaml]
configMapGenerator:
- name: app-config
  literals: [APP_MODE=base]
EOF
cat > /tmp/k8s-m15/kustomize/overlays/lab/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: k8s-m15-kustomize
namePrefix: lab-
resources: [../../base]
replicas:
- {name: app, count: 2}
configMapGenerator:
- name: app-config
  behavior: merge
  literals: [APP_MODE=lab]
EOF
terminal
kubectl create namespace k8s-m15-kustomize
kubectl kustomize /tmp/k8s-m15/kustomize/overlays/lab > /tmp/k8s-m15/rendered.yaml
sed -n '1,160p' /tmp/k8s-m15/rendered.yaml
kubectl apply --dry-run=server -f /tmp/k8s-m15/rendered.yaml
kubectl diff -k /tmp/k8s-m15/kustomize/overlays/lab || [ "$?" -eq 1 ]
kubectl apply --server-side --field-manager=kustomize-lab -k /tmp/k8s-m15/kustomize/overlays/lab
kubectl -n k8s-m15-kustomize rollout status deployment/lab-app --timeout=120s
kubectl -n k8s-m15-kustomize get deployment,configmap,pod
kubectl -n k8s-m15-kustomize get deployment lab-app -o jsonpath='{.spec.template.spec.containers[0].envFrom[0].configMapRef.name}{"\n"}'
09

9. Лабораторная работа: собственный Helm chart

Минимальный chart имеет явный values contract. Выполните lint, сохраните render, проведите server dry-run и только затем создайте release.

terminal
mkdir -p /tmp/k8s-m15/chart/templates
cat > /tmp/k8s-m15/chart/Chart.yaml <<'EOF'
apiVersion: v2
name: course-web
description: Minimal chart for Kubernetes Fundamentals module 15
type: application
version: 0.1.0
appVersion: "1.29"
EOF
cat > /tmp/k8s-m15/chart/values.yaml <<'EOF'
replicaCount: 2
image: {repository: nginx, tag: 1.29-alpine}
service: {port: 80}
EOF
cat > /tmp/k8s-m15/chart/values.schema.json <<'EOF'
{"$schema":"https://json-schema.org/draft-07/schema#","type":"object","required":["replicaCount","image","service"],"properties":{"replicaCount":{"type":"integer","minimum":1,"maximum":5},"image":{"type":"object","required":["repository","tag"]},"service":{"type":"object","required":["port"]}}}
EOF
cat > /tmp/k8s-m15/chart/templates/_helpers.tpl <<'EOF'
{{- define "course-web.name" -}}course-web{{- end -}}
EOF
cat > /tmp/k8s-m15/chart/templates/deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "course-web.name" . }}
  labels: {app.kubernetes.io/name: {{ include "course-web.name" . }}, app.kubernetes.io/managed-by: {{ .Release.Service }}}
spec:
  replicas: {{ .Values.replicaCount }}
  selector: {matchLabels: {app.kubernetes.io/name: {{ include "course-web.name" . }}}}
  template:
    metadata: {labels: {app.kubernetes.io/name: {{ include "course-web.name" . }}}}
    spec:
      containers:
      - name: nginx
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        ports: [{name: http, containerPort: {{ .Values.service.port }}}]
EOF
cat > /tmp/k8s-m15/chart/templates/service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata: {name: {{ include "course-web.name" . }}}
spec:
  selector: {app.kubernetes.io/name: {{ include "course-web.name" . }}}
  ports: [{name: http, port: {{ .Values.service.port }}, targetPort: http}]
EOF
terminal
helm version --short
helm lint /tmp/k8s-m15/chart
helm template course-web /tmp/k8s-m15/chart --namespace k8s-m15-helm > /tmp/k8s-m15/helm-rendered.yaml
kubectl apply --dry-run=server -f /tmp/k8s-m15/helm-rendered.yaml
helm upgrade --install course-web /tmp/k8s-m15/chart \
  --namespace k8s-m15-helm --create-namespace --wait --timeout 3m
helm status course-web -n k8s-m15-helm
kubectl -n k8s-m15-helm get deployment,service,pod
10

10. Лабораторная работа: upgrade и rollback

Измените replicas через values, исследуйте effective values/history, затем откатитесь к revision 1. После rollback история получает новую revision.

terminal
helm upgrade course-web /tmp/k8s-m15/chart -n k8s-m15-helm \
  --set replicaCount=3 --wait --timeout 3m
helm get values course-web -n k8s-m15-helm --all
helm history course-web -n k8s-m15-helm
kubectl -n k8s-m15-helm get deployment course-web

helm rollback course-web 1 -n k8s-m15-helm --wait --timeout 3m
helm history course-web -n k8s-m15-helm
kubectl -n k8s-m15-helm get deployment course-web
11

11. Диагностика до mutation

Строка zero нарушает JSON Schema integer и upgrade не создаёт revision. Сломанный Kustomize path также прекращает render; уже применённый Deployment не меняется.

terminal
helm upgrade course-web /tmp/k8s-m15/chart -n k8s-m15-helm \
  --set-string replicaCount=zero || true
helm history course-web -n k8s-m15-helm
kubectl -n k8s-m15-helm get deployment course-web -o jsonpath='replicas={.spec.replicas}{"\n"}'

cp -a /tmp/k8s-m15/kustomize/overlays/lab /tmp/k8s-m15/kustomize/overlays/broken
sed -i 's#../../base#../../missing-base#' /tmp/k8s-m15/kustomize/overlays/broken/kustomization.yaml
kubectl kustomize /tmp/k8s-m15/kustomize/overlays/broken || true
kubectl -n k8s-m15-kustomize get deployment lab-app

Порядок: versions/dependencies → input values/overlay → lint/build → rendered YAML → schema/server dry-run → diff → release events/status → workload diagnostics.

12

12. Контроль, критерии освоения и cleanup

  • Чем base отличается от overlay?
  • Зачем hash ConfigMap?
  • version против appVersion?
  • Что хранит Helm revision?
  • Rollback удаляет database migration?
  • Зачем values schema?
  • Почему rendered Secret небезопасен?
  • Чем hook опаснее обычного resource?
  • Можно ли смешивать Helm и kubectl ownership?
  • Почему latest разрушает воспроизводимость?
terminal
helm uninstall course-web -n k8s-m15-helm
kubectl delete namespace k8s-m15-helm k8s-m15-kustomize --ignore-not-found --wait=true
rm -rf /tmp/k8s-m15
helm list -A
kubectl get namespace k8s-m15-helm k8s-m15-kustomize 2>&1 || true

Модуль освоен, если вы получаете одинаковый render из зафиксированных inputs, проводите Helm upgrade/rollback и блокируете неверное значение до mutation.