Helm-Chart für eine Next.js-App
Verwende für charts/<app>/ und wie Argo CD es konsumiert. Das ausgeführte Image stammt von docker-nextjs; die Pipeline, die beides veröffentlicht, ist github-actions.
Das Vorlagenverzeichnis charts/__APP__/ ist die Quelle der Wahrheit. Alle Werte mit Kommentaren: references/values.md. Argo CD-Verdrahtung: references/argocd.md.
Regeln
-
Ein Chart pro App im Pfad
charts/<app>/im Stammverzeichnis des App-Repositorys. Warum dort: Argo CD kann es direkt von Git synchronisieren (path: charts/<app>) für eine Entwicklungsumgebung und aus der OCI-Registry für die Produktion, mit den gleichen Dateien; es liegt außerhalb vonapps/, ist also weder im Docker-Build-Kontext noch ein pnpm-Workspace-Paket; und das Chart ändert sich im selben PR wie der dazugehörige Code. Das Chart ist absichtlich als Einzel-App konzipiert: kein Umbrella, keine Subcharts. Generische, wertegesteuerte Vorlagen (wie das Backend-Umbrella-Chart) nur dort, wo eine einzelne App den Schalter benötigt: Ingress, HPA, PDB, Topology Spread. -
versionoderappVersionniemals von Hand bearbeiten. Beide bleiben0.0.0in Git.release.yamlführthelm package --version X.Y.Z --app-version vX.Y.Zaus dem TagvX.Y.Zaus, sodass das veröffentlichte Chart und der Image-Tag immer übereinstimmen. -
image.taghat als StandardwertappVersion; setze es nur für git-synchronisierte Umgebungen. Aus der OCI istappVersionder Release-Tag und das Image istghcr.io/nuvraxis/<app>:vX.Y.Z. Aus Git synchronisiert istappVersion0.0.0, daher setzt die Argo CD Applicationimage.tag: sha-<short-sha>(odermain).image.repositoryist immer klein geschrieben. -
Probes: Startup, Liveness und Readiness, alle auf
/api/health. Der Startup-Probe (2s × 30) deckt den Boot ab; Liveness und Readiness starten erst danach. Die Route berührt niemals eine Abhängigkeit, sodass ein Datenbankausfall keine Pods neu startet. Zeige einen Probe nicht auf/: das rendert eine Seite. -
Der Security Context entspricht dem Image: uid/gid 1001,
runAsNonRoot,readOnlyRootFilesystem, keine Privilege Escalation, alle Capabilities entfernt,RuntimeDefaultseccomp.automountServiceAccountToken: falsesowohl beim ServiceAccount als auch beim Pod: Die App ruft keine Kubernetes-API auf. -
.next/cacheist einemptyDirmit einemsizeLimit. Es ist der einzige beschreibbare Pfad (/app/apps/<app>/.next/cache). Das Limit verhindert, dass ein ausufernder Image-Cache den Knoten füllt. -
Konfiguration über
envFrom: eine ConfigMap ausvalues.config, und ein bereits vorhandenes Secret.config: API_BASE_URL: https://api.nuvraxis.com existingSecret: portal-secretsDas Chart templatet niemals Secret-Werte: kein
kind: Secretmitdataaus values, kein--set secret=…. Das Secret wird von External Secrets, Sealed Secrets oder einem Operator erstellt, und nur sein Name steht in den values. Eine ConfigMap-Änderung rollt die Pods über die Annotationchecksum/configneu. -
Keine
NEXT_PUBLIC_*im Chart. Sie werden beim Image-Build eingebrannt (siehedocker-nextjs); sie zur Laufzeit zu setzen, bewirkt nichts. -
Ressourcen: Requests für CPU und Speicher, ein Speicher-Limit, kein CPU-Limit. CPU-Limits drosseln einen Node-Server während Lastspitzen und erhöhen die Tail-Latenz; das Speicher-Limit bleibt, weil der Node-Heap auf das wächst, was ihm gegeben wird.
-
Hohe Verfügbarkeit standardmäßig: 2 Replicas, PDB
maxUnavailable: 1, Rolling Update mitmaxUnavailable: 0, Topology Spread über Zonen und Nodes (ScheduleAnyway). Mitautoscaling.enabledlässt das Deploymentreplicasweg, sodass Argo CD und der HPA sich nicht darum streiten. -
Ingress ist optional, TLS über cert-manager-Annotationen.
ingress.enabled,className, hosts,tlsmit einemsecretNameundcert-manager.io/cluster-issuerin den Annotationen. Für Streaming-Antworten auf ingress-nginx fügenginx.ingress.kubernetes.io/proxy-buffering: "off"hinzu; für Ratenbegrenzunglimit-rps(siehenextjs-security). -
Keine Helm-Hooks, kein
lookup, keine zufälligen Werte. Argo CD rendert das Chart mithelm templateund wendet das Ergebnis an: Hooks werden zu Argo-Hooks mit anderer Semantik,lookupgibt nichts zurück, undrandAlphaNumändert sich bei jeder Synchronisierung und zeigt die App dauerhaft als nicht synchronisiert an. Alles, was gerendert wird, muss eine reine Funktion der Werte sein. -
Private GHCR-Pakete benötigen Pull-Berechtigungen. GHCR-Pakete sind beim ersten Push privat. Für Images: ein
docker-registry-Secret mit einem Token, dasread:packageshat, referenziert inimagePullSecrets(Produktionswerte referenzierenghcr-pull). Für das Chart in Argo CD: ein Repository-Secret mitenableOCI: "true"(siehereferences/argocd.md). Oder mache die Pakete in GitHub öffentlich, wenn der Code öffentlich ist. -
values-production.yamlenthält nur, was in der Produktion anders ist. Schichte es:-f values.yaml -f values-production.yaml. Kopiere nicht die Standardwerte hinein. -
Validiere mit
helm lint --strictundhelm template | kubeconform -strict. Warum kubeconform statt helm-unittest: Die Fehler, die ein Einzel-App-Chart tatsächlich hat, sind falsche Feldnamen, Typen oder API-Versionen in der gerenderten Ausgabe, was kubeconform gegen die realen Kubernetes-Schemata ohne Testcode erkennt. helm-unittest testet die Template-Logik, die dieses Chart kaum hat. Lint und validiere beide Werte-Dateien, wiepr.yamles tut.
Hinzufügen von Dingen
| Bedarf | Änderung |
|---|---|
| Eine Runtime-Umgebungsvariable | config: in values (nicht geheim) oder ein Schlüssel im vorhandenen Secret |
| Ein Geheimnis | Füge es dem externen Secret hinzu; im Chart ändert sich nichts |
| Ein weiterer Port oder Sidecar | Begründe es zuerst; eine Next-App benötigt einen Port |
| Ein Cron-Job | Ein separates Chart oder eine separate App, nicht dieses Deployment |
| NetworkPolicy | Füge templates/networkpolicy.yaml hinzu, umschaltbar über networkPolicy.enabled |
Überprüfen
helm lint --strict charts/<app>
helm lint --strict charts/<app> -f charts/<app>/values-production.yaml
helm template <app> charts/<app> -f charts/<app>/values-production.yaml \
| docker run --rm -i ghcr.io/yannh/kubeconform:v0.8.0 -strict -summary -kubernetes-version 1.34.0 -
Quellen
- https://helm.sh/docs/topics/charts/
- https://helm.sh/docs/chart_best_practices/
- https://helm.sh/docs/topics/registries/
- https://helm.sh/docs/overview/
- https://github.com/yannh/kubeconform
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- https://kubernetes.io/docs/concepts/security/pod-security-standards/
- https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
- https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
- https://argo-cd.readthedocs.io/en/stable/user-guide/helm/
- https://cert-manager.io/docs/usage/ingress/
- https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry