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.
Cada componente está plantillado directamente — sin dependencias de chart externas:
| Componente | Qué es |
|---|---|
postgres | Postgres 17 sin más, StatefulSet de instancia única |
keycloak | Autenticación — Direct Access Grant, sin UI de login alojada |
rustfs | Almacé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-api | El servicio de ingesta |
ledger-alerting | Ejecutor independiente de reverificación de la cadena/espejo/anclaje/outbox — la misma imagen que ledger-api, distinto comando, deliberadamente un singleton |
dashboard | La interfaz de navegador |
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 localingress.className: nginx en values.yaml); cámbielo ahí y en su fichero de secretos si el cliente usa otra cosaClusterIssuer ya creado — su nombre va en ingress.annotations["cert-manager.io/cluster-issuer"]StorageClass por defecto (o valores explícitos de storageClassName) — Postgres y el almacén de blobs incluido reclaman volúmenes persistentesmetrics-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 escalanEl 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>
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:
ingress.dashboardHost, ingress.apiHost, ingress.authHost.ledgerApi.image.repository/tag, ledgerAlerting.image.repository/tag (misma imagen que ledgerApi), dashboard.image.repository/tag.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.
helm install <release> . -n <namespace> --create-namespace \
-f values.yaml -f values-secrets.<client>.yaml
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.
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.
curl -s https://<apiHost>/healthz
y abra https://<dashboardHost> en un navegador.
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.
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.
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.
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).postgres — añada un pooler (por ejemplo, PgBouncer) como un Deployment aparte delante de él si el pooling se vuelve necesario bajo carga.docker build/push manual hoy en día, como en el paso 1 — todavía sin automatización de CI para ello.