Helm-Charts: Paketverwaltung für Kubernetes

Kubernetes-Deployments mit Helm-Paketverwaltung

Helm hat die Bereitstellung von Kubernetes-Anwendungen revolutioniert, indem es Paketverwaltungs Konzepte einführen, die von traditionellen Betriebssystemen bekannt sind.

Mit der wachsenden Adoption von Kubernetes wird die Verwaltung komplexer Anwendungen mit Dutzenden von YAML-Dateien immer schwieriger. Helm-Charts lösen dieses Problem, indem sie alle Ressourcen in versionierte, konfigurierbare Pakete bündeln.

Softwareentwickler in der Natur Dieses schöne Bild wurde von dem KI-Modell Flux 1 dev erzeugt.

Helm verstehen: Der Kubernetes-Paketmanager

Helm ist für Kubernetes, was apt für Debian, yum für RedHat oder Homebrew für macOS ist. Es verpackt Kubernetes-Anwendungen in Charts – Sammlungen von Dateien, die verwandte Kubernetes-Ressourcen beschreiben. Ein einzelner Chart könnte ein komplettes Anwendungs-Stack bereitstellen: Webserver, Datenbanken, Caching-Schichten, Ingress-Regeln und Überwachungskomponenten. Für Kubernetes-Neulinge bietet ein Kubernetes Cheatsheet wesentliche Befehle und Konzepte zum Einstieg.

Warum Helm in der modernen DevOps-Welt wichtig ist

Komplexitätsreduktion: Statt 20+ YAML-Dateien zu verwalten, verwalten Sie einen Chart mit anpassbaren Werten.

Wiederholbarkeit: Bereitstellen identischer Konfigurationen in Entwicklung, Staging und Produktion mit umgebungsspezifischen Wert-Überschreibungen. Dies ist besonders wertvoll beim Bereitstellen komplexer Mikroservice-Architekturen, bei denen Konsistenz entscheidend ist.

Versionskontrolle: Charts sind versioniert, was einfache Rollbacks und Upgrade-Verfolgung ermöglicht.

Community-Ökosystem: Tausende vorab erstellte Charts stehen über Artifact Hub (ehemals Helm Hub) für beliebte Anwendungen wie PostgreSQL, Redis, NGINX, Prometheus und mehr zur Verfügung.

Templating-Macht: Go-Templates ermöglichen die dynamische Ressourcenerstellung basierend auf Eingabewerten und reduzieren Duplikate.

Helm-Architektur und Kernkonzepte

Helm 3 Architektur

Helm 3 vereinfachte die Architektur, indem es Tiller, die problematische serverseitige Komponente aus Helm 2, entfernte:

  • Helm-Client: CLI-Tool, das direkt mit der Kubernetes-API interagiert
  • Chart: Paketformat, das Vorlagen und Metadaten enthält
  • Release: Eine Instanz eines Charts, die in einem Kubernetes-Cluster läuft
  • Repository: Speicherort für Charts (HTTP-Server oder OCI-Registry)

Wichtige Komponenten eines Helm-Charts

my-app-chart/
├── Chart.yaml          # Chart-Metadaten und Version
├── values.yaml         # Standardkonfigurationswerte
├── charts/             # Abhängige Charts
├── templates/          # Kubernetes-Ressourcen-Vorlagen
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl    # Vorlagen-Helper
│   └── NOTES.txt       # Nachinstallationshinweise
├── .helmignore         # Zu ignorierende Dateien beim Paketieren
├── README.md
└── LICENSE

Erstellen Ihres ersten Helm-Charts

Initialisieren eines neuen Charts

helm create my-application
cd my-application

Dies erzeugt einen Starter-Chart mit Beispielvorlagen für eine Bereitstellung, einen Dienst und einen Ingress.

Chart.yaml: Definieren von Metadaten

apiVersion: v2
name: my-application
description: Ein produktionsbereiter Anwendungs-Chart
type: application
version: 1.0.0        # Chart-Version
appVersion: "2.4.1"   # Version der Anwendung

maintainers:
  - name: Ihr Team
    email: team@company.com

dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

Values.yaml: Konfigurationsmanagement

Die values.yaml-Datei definiert Standardkonfigurationen, die Benutzer überschreiben können. Dieser Ansatz trennt Konfiguration von Vorlagen und erleichtert die Verwaltung verschiedener Umgebungen (Entwicklung, Staging, Produktion) und die Anpassung von Bereitstellungen ohne Änderung der Vorlagendateien.

replicaCount: 3

image:
  repository: myapp/backend
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

Vorlagen: Dynamische Kubernetes-Manifests

Vorlagen verwenden Go-Templating-Syntax, um Kubernetes-Ressourcen dynamisch zu generieren. Diese Vorlagen können jeden Kubernetes-Ressourcentyp generieren, von einfachen Bereitstellungen bis zu komplexen StatefulSets für zustandsbehaftete Anwendungen, die dauerhafte Speicherung benötigen. Für Anwendungen, die stabile Identitäten und dauerhafte Volumes benötigen, sollten Sie StatefulSets anstelle von Bereitstellungen verwenden, wie in unserer Anleitung zu StatefulSets und dauerhafter Speicherung in Kubernetes beschrieben.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-application.fullname" . }}
  labels:
    {{- include "my-application.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "my-application.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-application.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          {{- toYaml .Values.resources | nindent 10 }}

Vorlagen-Helper (_helpers.tpl)

Erstellen Sie wiederverwendbare Vorlagenfunktionen, um Wiederholungen zu vermeiden:

{{/*
Erweitern Sie den Namen des Charts.
*/}}
{{- define "my-application.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Erstellen Sie einen Standard-vollqualifizierten Anwendungsnamen.
*/}}
{{- define "my-application.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

{{/*
Gemeinsame Labels
*/}}
{{- define "my-application.labels" -}}
helm.sh/chart: {{ include "my-application.chart" . }}
{{ include "my-application.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

Verwaltung von Helm-Charts: Installation und Betrieb

Installation eines Charts

# Installation aus einem Repository
helm install my-release bitnami/postgresql

# Installation aus lokalem Verzeichnis
helm install my-app ./my-application

# Installation mit benutzerdefinierten Werten
helm install my-app ./my-application -f values-production.yaml

# Installation mit inline-Wert-Überschreibungen
helm install my-app ./my-application \
  --set replicaCount=5 \
  --set image.tag=2.0.0

Aktualisieren von Releases

# Aktualisieren mit neuen Werten
helm upgrade my-app ./my-application -f values-production.yaml

# Aktualisieren mit atomarem Rollback bei Fehlschlag
helm upgrade my-app ./my-application --atomic --timeout 5m

# Erzwingen von Ressourcenaktualisierungen
helm upgrade my-app ./my-application --force

Rollback und Historie

# Anzeigen der Release-Historie
helm history my-app

# Rollback zur vorherigen Version
helm rollback my-app

# Rollback zu einer bestimmten Revision
helm rollback my-app 3

Testen und Debuggen

# Trockenlauf, um generierte Manifests anzuzeigen
helm install my-app ./my-application --dry-run --debug

# Vorlagen-Rendering ohne Installation
helm template my-app ./my-application

# Chart auf Probleme prüfen
helm lint ./my-application

# Testen des Releases mit Test-Hooks
helm test my-app

Fortgeschrittene Helm-Funktionen

Chart-Abhängigkeiten

Helm-Charts können von anderen Charts abhängen, was es ermöglicht, komplexe Anwendungen aus wiederverwendbaren Komponenten zusammenzusetzen. Dies ist besonders nützlich beim Bereitstellen von Mikroservices, die Datenbanken, Nachrichtenwarteschlangen oder andere unterstützende Dienste benötigen. Definieren Sie Abhängigkeiten in Chart.yaml:

dependencies:
  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

Aktualisieren Sie Abhängigkeiten:

helm dependency update ./my-application

Helm-Hooks für Lebenszyklus-Management

Hooks werden zu bestimmten Punkten im Release-Lebenszyklus ausgeführt:

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-application.fullname" . }}-db-migration
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "5"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      containers:
      - name: db-migrate
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        command: ["python", "manage.py", "migrate"]
      restartPolicy: Never

Hook-Typen umfassen:

  • pre-install: Vor der Ressourceninstallation
  • post-install: Nach der Installation aller Ressourcen
  • pre-upgrade: Vor dem Upgrade
  • post-upgrade: Nach dem Upgrade
  • pre-delete: Vor der Löschung
  • post-delete: Nach der Löschung
  • pre-rollback: Vor dem Rollback
  • post-rollback: Nach dem Rollback

Bedingte Logik und Flusssteuerung

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "my-application.fullname" . }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if .Values.ingress.className }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  rules:
  {{- range .Values.ingress.hosts }}
  - host: {{ .host | quote }}
    http:
      paths:
      {{- range .paths }}
      - path: {{ .path }}
        pathType: {{ .pathType }}
        backend:
          service:
            name: {{ include "my-application.fullname" $ }}
            port:
              number: {{ $.Values.service.port }}
      {{- end }}
  {{- end }}
{{- end }}

OCI-Registry-Unterstützung: Moderne Chart-Verteilung

Seit Helm 3.8 ist die OCI (Open Container Initiative)-Registry-Unterstützung stabil, was es ermöglicht, Charts zusammen mit Container-Images zu speichern.

Veröffentlichung in OCI-Registry

# Anmeldung beim Registry
helm registry login registry.example.com

# Chart verpacken
helm package ./my-application

# Hochladen in OCI-Registry
helm push my-application-1.0.0.tgz oci://registry.example.com/charts

# Installation aus OCI-Registry
helm install my-app oci://registry.example.com/charts/my-application --version 1.0.0

Vorteile von OCI-Registries

  • Einheitliche Speicherung: Charts und Images an einem Ort
  • Standard-Tools: Nutzung bestehender Registry-Infrastruktur
  • Bessere Sicherheit: Nutzung von Registry-Authentifizierung und Scans
  • Einfachere Verwaltung: Kein separater Chart-Repository-Server erforderlich

Best Practices für Produktions-Helm-Charts

1. Werte-Struktur und Dokumentation

Dokumentieren Sie alle Werte mit Kommentaren:

# -- Anzahl der Replikate für die Anwendung
replicaCount: 3

# -- Bildkonfiguration
image:
  # -- Bild-Repository
  repository: myapp/backend
  # -- Bild-Ladepolitik
  pullPolicy: IfNotPresent
  # -- Bild-Tag (standardmäßig Chart appVersion)
  tag: ""

2. Ressourcenmanagement

Legen Sie immer Ressourcenanforderungen und -grenzen fest:

resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 500m
    memory: 512Mi

3. Sicherheitskontexte

Definieren Sie Sicherheitskontexte für Container:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  fsGroup: 1000
  capabilities:
    drop:
    - ALL
  readOnlyRootFilesystem: true

4. Gesundheitsprüfungen

Integrieren Sie Liveness- und Readiness-Proben:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

5. ConfigMap- und Secret-Verwaltung

Eine ordnungsgemäße Geheimnisverwaltung ist für Produktionsbereitstellungen entscheidend. Verwenden Sie externe Geheimnismanager (Sealed Secrets, External Secrets Operator oder Vault) anstelle der Speicherung von Geheimnissen in Werte-Dateien. Dadurch wird sichergestellt, dass sensible Daten wie Datenbankpasswörter, API-Schlüssel und Zertifikate sicher gehandhabt werden:

envFrom:
- secretRef:
    name: {{ include "my-application.fullname" . }}-secrets
- configMapRef:
    name: {{ include "my-application.fullname" . }}-config

6. Schema-Validierung

Erstellen Sie values.schema.json, um Benutzereingaben zu validieren:

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1
    },
    "image": {
      "type": "object",
      "properties": {
        "repository": {
          "type": "string"
        },
        "tag": {
          "type": "string"
        }
      },
      "required": ["repository"]
    }
  },
  "required": ["image"]
}

7. NOTES.txt für Benutzeranleitung

Bieten Sie Anweisungen nach der Installation:

1. Holen Sie sich die Anwendungs-URL, indem Sie ausführen:
{{- if .Values.ingress.enabled }}
  https://{{ (index .Values.ingress.hosts 0).host }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-application.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- end }}

2. Überwachen Sie die Bereitstellung:
  kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ include "my-application.name" . }}"

Helm-Chart-Tests und CI/CD-Integration

Chart-Tests mit chart-testing (ct)

# Installieren Sie chart-testing
brew install chart-testing

# Charts überprüfen
ct lint --config ct.yaml

# Installieren und testen Sie Charts
ct install --config ct.yaml

GitHub Actions-Beispiel

name: Helm Chart CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Helm einrichten
        uses: azure/setup-helm@v3
        with:
          version: 3.12.0

      - name: chart-testing einrichten
        uses: helm/chart-testing-action@v2

      - name: Charts überprüfen
        run: ct lint --config ct.yaml

      - name: Erstellen Sie ein kind-Cluster
        uses: helm/kind-action@v1

      - name: Installieren Sie Charts
        run: ct install --config ct.yaml

GitOps mit Helm: ArgoCD und Flux

GitOps-Tools wie ArgoCD und Flux integrieren sich nahtlos mit Helm-Charts und ermöglichen deklarative, automatisierte Bereitstellungen. Diese Tools überwachen Ihr Git-Repository auf Änderungen und synchronisieren automatisch Helm-Releases, was die kontinuierliche Bereitstellung vereinfacht. Für komplexe Mikroservice-Architekturen sollten Sie prüfen, wie verteilte Transaktionsmuster wie das Saga-Muster helfen können, die Konsistenz über die mit Helm bereitgestellten Dienste zu verwalten.

ArgoCD-Anwendung

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-application
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/my-app-chart
    targetRevision: main
    path: charts/my-application
    helm:
      valueFiles:
        - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Flux HelmRelease

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: my-application
  namespace: flux-system
spec:
  interval: 5m
  chart:
    spec:
      chart: my-application
      version: '1.x.x'
      sourceRef:
        kind: HelmRepository
        name: my-charts
  values:
    replicaCount: 5
    image:
      tag: "2.0.0"

Fehlerbehebung bei häufigen Helm-Problemen

Problem: Fehlgeschlagenes Upgrade hängt im Status “Pending”

# Überprüfen Sie den Release-Status
helm list --all-namespaces

# Holen Sie sich Release-Details
helm status my-app -n namespace

# Löschen Sie erzwungene Geheimnisse, falls erforderlich (vorsichtig verwenden)
kubectl delete secret -n namespace -l owner=helm,name=my-app

Problem: Template-Rendering-Fehler

# Debuggen Sie das Template-Rendering
helm template my-app ./my-application --debug

# Validieren Sie gegen Kubernetes
helm template my-app ./my-application | kubectl apply --dry-run=client -f -

Problem: Werte werden nicht angewendet

# Überprüfen Sie die zusammengeführten Werte
helm get values my-app

# Zeigen Sie alle berechneten Werte an
helm get values my-app --all

Helm-Ökosystem und Tools

Das Helm-Ökosystem umfasst zahlreiche Tools, die seine Fähigkeiten erweitern und mit anderen Kubernetes-Technologien integrieren. Bei der Bereitstellung von Anwendungen, die erweiterte Netzwerkfunktionen wie Service-to-Service-Kommunikation, Verkehrsmanagement und Sicherheitsrichtlinien erfordern, sollten Sie die Integration mit einem Service Mesh mit Istio und Linkerd in Betracht ziehen, die über Helm-Charts bereitgestellt und verwaltet werden können.

Wichtige Tools

  • Helmfile: Deklarative Spezifikation zum Bereitstellen von Helm-Charts
  • Helm Diff: Vorschau von Änderungen vor dem Upgrade
  • Helm Secrets: Geheimnisse mit SOPS verwalten
  • Nova: Veraltete Helm-Charts finden
  • Pluto: Veraltete Kubernetes-APIs erkennen

Beispiel Helmfile

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

releases:
  - name: postgresql
    namespace: database
    chart: bitnami/postgresql
    version: 12.x.x
    values:
      - postgresql:
          auth:
            database: myapp
            username: appuser

  - name: my-app
    namespace: production
    chart: ./charts/my-application
    values:
      - values-production.yaml
    needs:
      - database/postgresql

Die Zukunft von Helm

Das Helm-Ökosystem entwickelt sich weiter:

  • OCI-Nativ: Vollständiger Übergang zu OCI-Registries als Standard
  • Verbesserte Sicherheit: Bessere Geheimnisverwaltung und Signaturfähigkeiten
  • Leistung: Schnellere Rendering- und Installationszeiten für große Charts
  • WASM-Unterstützung: WebAssembly für Chart-Plugins und Erweiterungen
  • Bessere Validierung: Erweiterte Schema-Validierung und Richtlinienumsetzung

Fazit

Helm ist zum de facto-Standard für Kubernetes-Paketverwaltung geworden, und die Beherrschung ist für moderne DevOps-Praktiken unerlässlich. Durch das Verständnis der Chart-Struktur, des Templatings, des Werte-Managements und der Best Practices können Sie wartbare, sichere und skalierbare Kubernetes-Bereitstellungen erstellen.

Beginnen Sie mit einfachen Charts, integrieren Sie schrittweise erweiterte Funktionen wie Haken und Abhängigkeiten und integrieren Sie GitOps-Tools für produktionsreife Infrastruktur. Die Helm-Community bietet Tausende von vorgefertigten Charts, aber die wahre Macht liegt in der Erstellung von benutzerdefinierten Charts, die auf die Bedürfnisse Ihrer Organisation zugeschnitten sind. Ob Sie zustandslose Anwendungen oder zustandsbehaftete Workloads mit persistentem Speicher bereitstellen, Helm vereinfacht die Komplexität des Kubernetes-Ressourcenmanagements. Für Teams, die neue Kubernetes-Cluster einrichten, sollten Sie die Installation von Kubernetes mit Kubespray in Betracht ziehen oder Kubernetes-Distributionen wie k3s oder MicroK8s für Entwicklungsumgebungen erkunden. Wenn Sie bewerten, welche Distribution Ihren Anforderungen an ein Homelab oder einen kleinen Cluster entspricht, sehen Sie sich unseren umfassenden Vergleich von Kubernetes-Distributionen für eine detaillierte Analyse an.