SVSANNVIT
Primeros pasos

Alojamiento en Kubernetes

Despliegue Sannvit Ledger en el clúster de Kubernetes de un cliente con el chart de Helm, para escalado horizontal más allá de un único host.

Una segunda vía de despliegue junto a Docker Compose — para un clúster que ya existe y necesita un release de Helm, o que específicamente necesita escalar horizontalmente los servicios sin estado (por ejemplo, detrás de un HPA) en lugar de ejecutar todo en un único host.

Use Docker Compose para un despliegue autoalojado de un único host — es más sencillo de operar, y sigue siendo la implementación de referencia de cómo se conectan entre sí todos los servicios.

Use este chart en cuanto el clúster de un cliente ya exista, o cuando las necesidades de escalado superen lo que puede dar un único host de Compose.

Qué despliega el chart

Cada componente está plantillado directamente — sin dependencias de chart externas:

ComponenteQué es
postgresPostgres 17 sin más, StatefulSet de instancia única
keycloakAutenticación — Direct Access Grant, sin UI de login alojada
rustfsAlmacén de blobs para el contenido de los payloads, y el bucket con bloqueo WORM que respalda el plano de integridad append-only — opcional, vea Aporte su propio almacenamiento compatible con S3
ledger-apiEl servicio de ingesta
ledger-alertingEjecutor independiente de reverificación de la cadena/espejo/anclaje/outbox — la misma imagen que ledger-api, distinto comando, deliberadamente un singleton
dashboardLa interfaz de navegador

Requisitos previos

Se espera que ya existan en el clúster del cliente — no es responsabilidad de este chart proporcionarlos:

  • kubectl configurado para el clúster objetivo, y Helm 3.8+ en local
  • Un ingress controller — nginx-ingress por defecto (ingress.className: nginx en values.yaml); cámbielo ahí y en su fichero de secretos si el cliente usa otra cosa
  • cert-manager instalado, con un ClusterIssuer ya creado — su nombre va en ingress.annotations["cert-manager.io/cluster-issuer"]
  • Una StorageClass por defecto (o valores explícitos de storageClassName) — Postgres y el almacén de blobs incluido reclaman volúmenes persistentes
  • metrics-server, si mantiene el autoescalado activado — los valores por defecto activan HPAs para ledger-api/dashboard, y sin él esos HPAs muestran <unknown> para CPU y nunca escalan
  • Control de DNS para los hostnames del dashboard/API/auth — los registros no necesitan estar activos todavía, solo poder crearse

1. Construya y publique las imágenes

El chart espera que ledgerApi.image.repository / dashboard.image.repository ya existan en un registro al que el clúster pueda acceder para hacer pull — no las construye ni las publica.

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. Prepare los secretos y la configuración del cliente

cd k8s/ledger
cp values-example-secrets.yaml values-secrets.<client>.yaml

Rellene values-secrets.<client>.yaml — está en el gitignore, un fichero por cliente, nunca se hace commit de él:

  • Establezca los hostnames: ingress.dashboardHost, ingress.apiHost, ingress.authHost.
  • Establezca las referencias de imagen del paso 1: ledgerApi.image.repository/tag, ledgerAlerting.image.repository/tag (misma imagen que ledgerApi), dashboard.image.repository/tag.
  • Si el cliente quiere aportar su propio Postgres en lugar del incluido en el chart, vea Limitaciones conocidas antes de continuar.

3. Apunte el DNS al ingress controller

kubectl get svc -n <ingress-namespace> <ingress-controller-service>

Cree registros A/CNAME para los tres hostnames del paso 2 apuntando a esa dirección. Esto puede hacerse antes de la instalación — los retos HTTP-01 de cert-manager simplemente no tendrán éxito (y los certificados TLS no se emitirán) hasta que los registros resuelvan, así que hágalo tan pronto como le resulte conveniente.

4. Instale

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

5. Observe el despliegue

kubectl -n <namespace> get pods -w

Postgres y Keycloak son los que más tardan en el primer arranque. Si algo se queda atascado en CrashLoopBackOff o Pending, kubectl -n <namespace> logs <pod> y kubectl -n <namespace> describe pod <pod> son los dos primeros comandos a los que recurrir — un PVC en Pending casi siempre significa que no hay una StorageClass por defecto.

6. Aplique las migraciones

El chart levanta un Postgres vacío — las migraciones de esquema son un paso aparte, igual que en la vía de Compose:

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 es secrets.postgresPassword de su fichero values-secrets. Aplique los ficheros api/drizzle/*.sql en orden numérico — 0000_baseline.sql crea el esquema completo desde cero, sin necesidad de un paso de arranque aparte. npm run db:functions aplica después cada función/vista/trigger que no sea un objeto de drizzle-kit.

7. Verifique

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

y abra https://<dashboardHost> en un navegador.

8. Primer inicio de sesión

No hay un usuario administrador sembrado. Cree uno con el mismo script que usa la vía de Compose:

kubectl -n <namespace> exec deploy/ledger-api -- node dist/seed_admin.js \
  you@example.com --org-name="Your Org Name"

Omita --org-name y pase --org=<id> en su lugar si la org ya existe. Imprime una contraseña generada una sola vez — inicie sesión en https://<dashboardHost> con ella, y luego invite a todos los demás a través del dashboard.

Actualización

La aplicación del ledger (nueva imagen de ledger-api/dashboard): repita el paso 1, incremente ledgerApi.image.tag/dashboard.image.tag en el fichero values del cliente, y luego:

helm upgrade <release> . -n <namespace> -f values.yaml -f values-secrets.<client>.yaml

Cambios de esquema: aplique cualquier fichero api/drizzle/*.sql nuevo de la misma forma que en el paso 6, antes de actualizar ledger-api/ledger-alerting a una versión que los espere.

Aporte su propio almacenamiento compatible con S3

Por defecto este chart ejecuta un StatefulSet de almacén de blobs incluido. Si un cliente ya tiene su propio bucket compatible con S3 (AWS S3, Cloudflare R2, Backblaze B2, su propio MinIO/RustFS, etc.) y no quiere el StatefulSet incluido ni su PVC, establezca en el fichero values del cliente:

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 sigue nombrando el bucket a usar — pre-créelo o conceda a las credenciales dadas el permiso CreateBucket; la API del ledger lo crea de forma perezosa en la primera subida si falta.

Limitaciones conocidas

  • HA de Postgres. StatefulSet de instancia única — este chart escala la capa sin estado de la API, no la base de datos. Para una HA real, apunte a una instancia de Postgres gestionada externa en su lugar (postgres.enabled: false, y luego apunte las URLs de base de datos de ledger-api/ledger-alerting/keycloak a ella mediante overrides de values) o a un operador de Postgres (CloudNativePG, StackGres).
  • Pooling de conexiones. Los servicios conectan directamente al Service postgres — añada un pooler (por ejemplo, PgBouncer) como un Deployment aparte delante de él si el pooling se vuelve necesario bajo carga.
  • Clustering de Keycloak. Se ejecuta como una única réplica — escalar más allá de una necesita configurar aparte el propio clustering de Keycloak.
  • Publicación de imágenes. docker build/push manual hoy en día, como en el paso 1 — todavía sin automatización de CI para ello.