← До програми курсу

Модуль 15 / 19

Helm, Kustomize та відтворювана доставка

KUBERNETES

Зміст
01

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

Ви навчитеся відокремлювати source 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 на основі старої.

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.