SVSANNVIT
Erste Schritte

Hosting auf Kubernetes

Deployen Sie das Sannvit Ledger mit dem Helm-Chart auf den Kubernetes-Cluster eines Kunden, für horizontale Skalierung über einen einzelnen Host hinaus.

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.

Was das Chart bereitstellt

Jede Komponente ist direkt templated — keine externe Chart-Abhängigkeit:

KomponenteWas es ist
postgresEinfaches Postgres 17, Single-Instance-StatefulSet
keycloakAuthentifizierung — Direct Access Grant, keine gehostete Login-UI
rustfsBlob Store für Payload-Inhalte und der WORM-gesperrte Bucket hinter der append-only Integritätsebene — optional, siehe Eigenen S3-kompatiblen Speicher verwenden
ledger-apiDer Ingest-Service
ledger-alertingEigenständiger Runner für Chain-Reverifizierung/Mirror/Anchor/Outbox — dasselbe Image wie ledger-api, anderer Befehl, bewusst ein Singleton
dashboardDie Browser-UI

Voraussetzungen

Wird bereits im Cluster des Kunden erwartet — nicht Aufgabe dieses Charts, es bereitzustellen:

  • kubectl, konfiguriert für den Zielcluster, und lokal Helm 3.8+
  • Ein Ingress-Controller — standardmäßig nginx-ingress (ingress.className: nginx in values.yaml); ändern Sie das dort und in Ihrer Secrets-Datei, falls der Kunde etwas anderes betreibt
  • cert-manager installiert, mit bereits angelegtem ClusterIssuer — dessen Name kommt in ingress.annotations["cert-manager.io/cluster-issuer"]
  • Eine Standard-StorageClass (oder explizite storageClassName-Werte) — Postgres und der gebündelte Blob Store beanspruchen Persistent Volumes
  • metrics-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 nie
  • DNS-Kontrolle für die Dashboard-/API-/Auth-Hostnamen — Records müssen noch nicht live sein, nur anlegbar

1. Images bauen und pushen

Das 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>

2. Secrets und Konfiguration des Kunden vorbereiten

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:

  • Setzen Sie die Hostnamen: ingress.dashboardHost, ingress.apiHost, ingress.authHost.
  • Setzen Sie die Image-Referenzen aus Schritt 1: ledgerApi.image.repository/tag, ledgerAlerting.image.repository/tag (dasselbe Image wie ledgerApi), dashboard.image.repository/tag.
  • Wenn der Kunde sein eigenes Postgres statt des im Chart enthaltenen verwenden möchte, lesen Sie vorher Bekannte Einschränkungen.

3. DNS auf den Ingress-Controller ausrichten

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.

4. Installieren

helm install <release> . -n <namespace> --create-namespace \
  -f values.yaml -f values-secrets.<client>.yaml

5. Rollout beobachten

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.

6. Migrationen anwenden

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.

7. Überprüfen

curl -s https://<apiHost>/healthz

und öffnen Sie https://<dashboardHost> im Browser.

8. Erste Anmeldung

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.

Upgrade durchführen

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.

Eigenen S3-kompatiblen Speicher verwenden

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.

Bekannte Einschränkungen

  • Postgres-HA. Single-Instance-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).
  • Connection Pooling. Services verbinden sich direkt mit dem postgres-Service — fügen Sie bei Bedarf unter Last einen Pooler (z. B. PgBouncer) als separates Deployment davor hinzu.
  • Keycloak-Clustering. Läuft als einzelne Replica — eine Skalierung über eine hinaus erfordert, Keycloaks eigene Clustering-Konfiguration separat einzurichten.
  • Image-Veröffentlichung. Aktuell manuelles docker build/push wie in Schritt 1 — dafür gibt es noch keine CI-Automatisierung.