Ein zweiter Deployment-Weg neben Docker Compose — für einen Cluster, der bereits existiert und ein Helm-Release benötigt, oder der speziell die zustandslosen Services horizontal skalieren muss (z. B. hinter einem HPA), statt alles auf einem Host zu betreiben.
Verwenden Sie Docker Compose für ein Single-Host-Self-Hosted-Deployment — es ist einfacher zu betreiben und bleibt die Referenzimplementierung dafür, wie alle Services miteinander verdrahtet sind.
Verwenden Sie dieses Chart, sobald der Cluster eines Kunden bereits existiert oder die Skalierungsanforderungen über das hinauswachsen, was ein Compose-Host leisten kann.
Jede Komponente ist direkt templated — keine externe Chart-Abhängigkeit:
| Komponente | Was es ist |
|---|---|
postgres | Einfaches Postgres 17, Single-Instance-StatefulSet |
keycloak | Authentifizierung — Direct Access Grant, keine gehostete Login-UI |
rustfs | Blob Store für Payload-Inhalte und der WORM-gesperrte Bucket hinter der append-only Integritätsebene — optional, siehe Eigenen S3-kompatiblen Speicher verwenden |
ledger-api | Der Ingest-Service |
ledger-alerting | Eigenständiger Runner für Chain-Reverifizierung/Mirror/Anchor/Outbox — dasselbe Image wie ledger-api, anderer Befehl, bewusst ein Singleton |
dashboard | Die Browser-UI |
Wird bereits im Cluster des Kunden erwartet — nicht Aufgabe dieses Charts, es bereitzustellen:
kubectl, konfiguriert für den Zielcluster, und lokal Helm 3.8+ingress.className: nginx in values.yaml); ändern Sie das dort und in Ihrer Secrets-Datei, falls der Kunde etwas anderes betreibtClusterIssuer — dessen Name kommt in ingress.annotations["cert-manager.io/cluster-issuer"]StorageClass (oder explizite storageClassName-Werte) — Postgres und der gebündelte Blob Store beanspruchen Persistent Volumesmetrics-server, falls Sie Autoscaling aktiviert lassen — die Defaults schalten HPAs für ledger-api/dashboard ein, und ohne diesen zeigen diese HPAs <unknown> für CPU an und skalieren nieDas Chart erwartet, dass ledgerApi.image.repository / dashboard.image.repository bereits in einer Registry existieren, aus der der Cluster pullen kann — es baut oder veröffentlicht sie nicht selbst.
docker build -t <registry>/<client>/ledger-api:<tag> -f api/Dockerfile api
docker push <registry>/<client>/ledger-api:<tag>
docker build -t <registry>/<client>/ledger-dashboard:<tag> -f dashboard/Dockerfile dashboard
docker push <registry>/<client>/ledger-dashboard:<tag>
cd k8s/ledger
cp values-example-secrets.yaml values-secrets.<client>.yaml
Füllen Sie values-secrets.<client>.yaml aus — sie ist gitignored, eine Datei pro Kunde, nie committet:
ingress.dashboardHost, ingress.apiHost, ingress.authHost.ledgerApi.image.repository/tag, ledgerAlerting.image.repository/tag (dasselbe Image wie ledgerApi), dashboard.image.repository/tag.kubectl get svc -n <ingress-namespace> <ingress-controller-service>
Legen Sie A/CNAME-Records für alle drei Hostnamen aus Schritt 2 an, die auf diese Adresse zeigen. Das kann bereits vor der Installation passieren — die HTTP-01-Challenges von cert-manager schlagen dann nur so lange fehl (und TLS-Zertifikate werden nicht ausgestellt), bis die Records auflösbar sind, also erledigen Sie das so früh wie praktikabel.
helm install <release> . -n <namespace> --create-namespace \
-f values.yaml -f values-secrets.<client>.yaml
kubectl -n <namespace> get pods -w
Postgres und Keycloak brauchen beim ersten Start am längsten. Wenn etwas in CrashLoopBackOff oder Pending hängen bleibt, sind kubectl -n <namespace> logs <pod> und kubectl -n <namespace> describe pod <pod> die ersten beiden Befehle, zu denen Sie greifen sollten — ein Pending-PVC bedeutet fast immer, dass keine Standard-StorageClass existiert.
Das Chart bringt ein leeres Postgres hoch — Schema-Migrationen sind ein separater Schritt, genau wie beim Compose-Weg:
kubectl -n <namespace> port-forward svc/postgres 5432:5432 &
for f in api/drizzle/0*.sql; do
psql -h localhost -U postgres -d postgres -v ON_ERROR_STOP=1 -f "$f"
done
cd api && DATABASE_URL=postgresql://postgres:<PGPASSWORD>@localhost:5432/postgres npm run db:functions
PGPASSWORD ist secrets.postgresPassword aus Ihrer values-secrets-Datei. Wenden Sie die api/drizzle/*.sql-Dateien in numerischer Reihenfolge an — 0000_baseline.sql legt das vollständige Schema von Grund auf an, kein separater Bootstrap-Schritt nötig. npm run db:functions wendet anschließend jede Funktion/View/Trigger an, die kein drizzle-kit-Objekt ist.
curl -s https://<apiHost>/healthz
und öffnen Sie https://<dashboardHost> im Browser.
Es gibt keinen vorab angelegten Admin-Benutzer. Legen Sie einen mit demselben Script an, das auch der Compose-Weg verwendet:
kubectl -n <namespace> exec deploy/ledger-api -- node dist/seed_admin.js \
you@example.com --org-name="Your Org Name"
Lassen Sie --org-name weg und übergeben Sie stattdessen --org=<id>, falls die Org bereits existiert. Gibt einmalig ein generiertes Passwort aus — melden Sie sich damit unter https://<dashboardHost> an und laden Sie danach alle anderen über das Dashboard ein.
Die Ledger-App (neues ledger-api/dashboard-Image): wiederholen Sie Schritt 1, erhöhen Sie ledgerApi.image.tag/dashboard.image.tag in der values-Datei des Kunden, dann:
helm upgrade <release> . -n <namespace> -f values.yaml -f values-secrets.<client>.yaml
Schema-Änderungen: Wenden Sie neue api/drizzle/*.sql-Dateien genauso an wie in Schritt 6, bevor Sie ledger-api/ledger-alerting auf eine Version upgraden, die diese erwartet.
Standardmäßig betreibt dieses Chart ein gebündeltes Blob-Store-StatefulSet. Wenn ein Kunde bereits einen eigenen S3-kompatiblen Bucket hat (AWS S3, Cloudflare R2, Backblaze B2, eigenes MinIO/RustFS usw.) und das gebündelte StatefulSet mit seinem PVC nicht möchte, setzen Sie in der values-Datei des Kunden:
rustfs:
enabled: false
storage:
endpoint: "s3.us-east-1.amazonaws.com"
port: 443
useSsl: true
region: "us-east-1"
secrets:
s3RootUser: "<access key scoped to the client's bucket>"
s3RootPassword: "<secret key>"
ledgerApi.storage.bucket benennt weiterhin den zu verwendenden Bucket — legen Sie ihn entweder vorab an oder gewähren Sie den angegebenen Zugangsdaten die Berechtigung CreateBucket; die Ledger API legt ihn beim ersten Upload automatisch an, falls er fehlt.
StatefulSet — dieses Chart skaliert die zustandslose API-Schicht, nicht die Datenbank. Für echte HA zeigen Sie stattdessen auf eine externe verwaltete Postgres-Instanz (postgres.enabled: false, dann die Datenbank-URLs von ledger-api/ledger-alerting/keycloak über values-Overrides darauf richten) oder einen Postgres-Operator (CloudNativePG, StackGres).postgres-Service — fügen Sie bei Bedarf unter Last einen Pooler (z. B. PgBouncer) als separates Deployment davor hinzu.docker build/push wie in Schritt 1 — dafür gibt es noch keine CI-Automatisierung.