Warum Helm und GitOps zusammengehören
Kubernetes-Manifeste von Hand zu verwalten funktioniert bei einem Service in einem Environment. Bei zehn Services in drei Environments wird es unübersichtlich. Bei zwanzig Services mit unterschiedlichen Konfigurationen pro Stage ist es nicht mehr beherrschbar.
Helm Charts und GitOps lösen dieses Problem auf unterschiedlichen Ebenen: Helm abstrahiert die Kubernetes-Manifeste zu parametrierbaren Templates. GitOps stellt sicher, dass der Zustand im Cluster dem Zustand im Git-Repository entspricht. Zusammen ergeben sie einen reproduzierbaren, auditierbaren Deployment-Prozess.
Helm Chart Struktur
Ein gut strukturiertes Helm Chart folgt Konventionen, die Wartbarkeit und Wiederverwendbarkeit fördern:
charts/vop-service/
├── Chart.yaml
├── values.yaml # Default-Werte
├── values-dev.yaml # Environment-spezifisch
├── values-qa.yaml
├── values-prod.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── hpa.yaml # Horizontal Pod Autoscaler
│ ├── networkpolicy.yaml
│ ├── serviceaccount.yaml
│ └── _helpers.tpl # Template-Hilfsfunktionen
└── tests/
└── test-connection.yaml
Values per Environment
Der Kern von Helm ist die Trennung von Template und Konfiguration. Ein values.yaml enthält die Defaults, environment-spezifische Values überschreiben gezielt:
# values.yaml (Defaults)
replicaCount: 1
image:
repository: registry.rypox.de/vop-service
tag: latest
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
kafka:
bootstrapServers: kafka:9092
consumerGroup: vop-service
# values-prod.yaml (Produktion)
replicaCount: 3
image:
tag: "1.4.2"
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi
kafka:
bootstrapServers: kafka-prod.vop-system:9093
consumerGroup: vop-service-prod
Best Practices für Helm Charts
Einige Regeln, die sich in unseren Projekten bewährt haben:
- Keine Secrets in Values, Secrets gehören in Vault oder Kubernetes Secrets, nicht in Values-Dateien
- Resource Limits immer setzen, Ein Pod ohne Limits kann den gesamten Node destabilisieren
- Health Checks konfigurieren, Liveness und Readiness Probes sind Pflicht, nicht optional
- Labels konsistent halten,
app.kubernetes.io/name,app.kubernetes.io/version,app.kubernetes.io/componentnach den Kubernetes-Empfehlungen - Chart-Versionen semantisch vergeben, Das Chart hat eine eigene Version, unabhängig von der Applikationsversion
GitLab CI Pipeline
Unsere CI/CD-Pipeline in GitLab CI bildet den gesamten Lifecycle ab: Build, Test, Deploy auf Test, Deploy auf QA und Deploy auf Produktion.
Pipeline-Stages
stages:
- build
- test
- package
- deploy-test
- integration-test
- deploy-qa
- deploy-prod
build:
stage: build
script:
- ./gradlew build -x test
artifacts:
paths:
- build/libs/*.jar
test:
stage: test
script:
- ./gradlew test
artifacts:
reports:
junit: build/test-results/test/*.xml
package:
stage: package
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
deploy-test:
stage: deploy-test
script:
- helm upgrade --install vop-service ./charts/vop-service
-f ./charts/vop-service/values-test.yaml
--set image.tag=$CI_COMMIT_SHA
--namespace vop-test
environment:
name: test
deploy-qa:
stage: deploy-qa
script:
- helm upgrade --install vop-service ./charts/vop-service
-f ./charts/vop-service/values-qa.yaml
--set image.tag=$CI_COMMIT_SHA
--namespace vop-qa
environment:
name: qa
when: manual
deploy-prod:
stage: deploy-prod
script:
- helm upgrade --install vop-service ./charts/vop-service
-f ./charts/vop-service/values-prod.yaml
--set image.tag=$CI_COMMIT_SHA
--namespace vop-prod
environment:
name: production
when: manual
rules:
- if: $CI_COMMIT_BRANCH == "main"
Wichtige Designentscheidungen
- Test-Deployment automatisch, Jeder Merge in den Develop-Branch deployt automatisch auf Test. Schnelles Feedback
- QA und Produktion manuell, Ein Klick in der Pipeline, aber bewusst. Kein versehentliches Produktions-Deployment
- Image-Tag ist Commit-SHA, Jedes Deployment ist exakt einem Commit zuordenbar. Kein
latest-Tag in Produktion - Integration Tests zwischen Test und QA, Nach dem Test-Deployment laufen automatisierte API-Tests gegen die Test-Umgebung
Deployment-Strategien
Rolling Update (Standard)
Kubernetes’ Standard-Strategie ersetzt Pods schrittweise. Konfiguriert über die Deployment-Spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
maxUnavailable: 0 stellt sicher, dass während des Updates keine Kapazität verloren geht. Neue Pods müssen ihre Readiness Probe bestehen, bevor alte Pods terminiert werden.
Canary Deployments
Für kritische Services deployen wir zunächst eine einzelne Instanz der neuen Version und überwachen die Metriken:
- Helm Release mit
replicaCount: 1und neuer Version deployen - Traffic Split über Istio VirtualService: 10% auf Canary
- Metriken vergleichen: Error Rate, Latenz, Business-Metriken
- Bei Erfolg: Vollständiges Rollout. Bei Problemen: Rollback
Rollback
Helm speichert Revisions. Ein Rollback ist ein einzelner Befehl:
helm rollback vop-service 3 --namespace vop-prod
Das stellt die dritte Revision wieder her, inklusive aller Konfigurationswerte. In Kombination mit dem Commit-SHA als Image-Tag ist jeder Zustand reproduzierbar.
GitOps mit ArgoCD
Für die langfristige Entwicklung evaluieren wir Argo CD als GitOps-Controller. Der Unterschied zu unserer aktuellen Pipeline: Statt dass die CI-Pipeline helm upgrade ausführt, überwacht ArgoCD ein Git-Repository und synchronisiert den Cluster-Zustand automatisch.
Vorteile:
- Git als Single Source of Truth, Der gewünschte Zustand ist im Repository, nicht im Cluster
- Drift Detection, Manuelle Änderungen am Cluster werden erkannt und korrigiert
- Self-Healing, Gelöschte Ressourcen werden automatisch wiederhergestellt
- Audit Trail, Jede Änderung ist ein Git-Commit mit Autor und Zeitstempel
Fazit
Die Kombination aus Helm Charts und einer durchdachten CI/CD-Pipeline ist die Grundlage für zuverlässige Deployments in Kubernetes. Helm abstrahiert die Komplexität der Kubernetes-Manifeste, environment-spezifische Values halten die Konfiguration sauber getrennt, und die Pipeline automatisiert den Weg von Code zu laufendem Service. Das Ziel ist immer dasselbe: Deployments sollen langweilig sein, vorhersagbar, wiederholbar und schnell rückgängig zu machen.