Ir al contenido

Primeros pasos en Kubernetes (Helm)

El chart de Helm en deploy/helm/olivares despliega el control plane con los mismos valores por defecto seguros que cualquier otra vía: un StatefulSet del núcleo (sin root, sistema de ficheros raíz de solo lectura, todas las capabilities eliminadas), TLS activado, exposición equivalente a loopback (un Service ClusterIP — nada público hasta que tú lo decidas) y sin credenciales por defecto. A partir de ahí optas por Postgres, HA activo-pasivo, copias de seguridad de DR cifradas programadas y el DaemonSet de colectores para la ingesta distribuida.

Todo lo de abajo se renderizó y comprobó contra el chart de este repositorio (helm lint + helm template, incluidas las configuraciones de HA y de copias de seguridad y las propias guard rails del chart). El chart requiere Kubernetes ≥ 1.25.

1. La instalación por defecto (un solo nodo, SQLite)

Sección titulada «1. La instalación por defecto (un solo nodo, SQLite)»
  1. Instala desde el checkout:

    Ventana de terminal
    helm install olivares deploy/helm/olivares

    Esto renderiza exactamente tres objetos de carga de trabajo — un StatefulSet (1 réplica, SQLite en un PVC persistente de 8 Gi), un Service ClusterIP (8443 HTTPS, 8444 gRPC) y un ServiceAccount sin privilegios sobre la API de Kubernetes. Las probes apuntan a /readyz (readiness — drena cuando el almacén está caído o la réplica no es líder) y /livez (liveness — solo el proceso, nunca una dependencia, así que una caída del almacén nunca puede provocar un bucle de reinicios).

  2. Lee el token de configuración de un solo uso desde el log del pod (se imprime solo en stdout — en Kubernetes, eso es el log del contenedor):

    Ventana de terminal
    kubectl logs -l app.kubernetes.io/component=core | sed -n '/FIRST-BOOT SETUP/,/========================/p'
  3. Haz port-forward y completa la configuración de primer arranque (el mismo flujo que en todas partes):

    Ventana de terminal
    # The Service is named <release>-olivares-core:
    kubectl port-forward svc/olivares-core 8443:8443
    curl -ksf -X POST https://127.0.0.1:8443/v1/setup \
    -H 'Content-Type: application/json' \
    -d '{"token":"<olst_ token>","email":"you@example.com","password":"<strong-password>"}'

Como alternativa, sin Helm en absoluto, el repositorio incluye un manifiesto plano con los mismos valores por defecto seguros de un solo nodo, regenerado desde el chart por CI:

Ventana de terminal
kubectl apply -f deploy/manifests/install.yaml

Para Argo CD / Flux / Kustomize (kustomize build --enable-helm), consulta deploy/gitops/ — envoltorios declarativos sobre este mismo chart.

El motor requiere un rol de base de datos de mínimo privilegio: LOGIN, sin superusuario, sin BYPASSRLS — la seguridad a nivel de fila es la red de seguridad del tenant, y el motor se niega a arrancar contra un rol privilegiado. Aprovisiona el rol con el canónico deploy/postgres/01-app-role.sql (el chart puede ejecutarlo por ti como un Job de pre-instalación), pon el DSN en un Secret y apunta el chart a él:

Ventana de terminal
kubectl create secret generic olivares-pg \
--from-literal=dsn='postgres://olivares_app:***@db:5432/olivares?sslmode=verify-full' \
--from-literal=admin-dsn='postgres://olivares_admin:***@db:5432/olivares?sslmode=verify-full'
helm install olivares deploy/helm/olivares \
--set core.engine=postgres \
--set postgres.dsnSecret=olivares-pg \
--set postgres.adminDsnKey=admin-dsn

La clave admin-dsn del mismo Secret contiene un rol dedicado NOSUPERUSER BYPASSRLS. Es opcional solo si nunca vas a activar las copias de seguridad: es lo que hace completas las lecturas del sistema entre tenants — la lista de organizaciones y la cobertura multi-tenant —, y activar la copia de seguridad la exige (copias de seguridad cifradas programadas). Crearla ahora convierte ese paso posterior en un cambio de una sola línea.

Con Postgres como almacén compartido, ejecuta varias réplicas. Una se elige como líder (un advisory lock de Postgres); las réplicas en espera responden a /readyz con 503 {"status":"standby"} para que el Service solo enrute al líder, y una réplica en espera toma el relevo automáticamente cuando el líder desaparece.

Hay dos cosas estructuralmente obligatorias, y el chart fuerza ambas (verificamos que las guardas se renderizan como fallos duros, no como avisos):

| Requisito | Por qué | Guarda del chart si falta | |---|---|---| | core.engine=postgres | El fichero SQLite es local a un solo pod — no puede ser un almacén compartido. | core.replicaCount > 1 requires core.engine=postgres … | | core.auditSigningKeySecret | Cada réplica debe firmar el ledger de auditoría con la misma clave Ed25519, o la cadena de hashes se bifurca en el failover. | core.replicaCount > 1 requires core.auditSigningKeySecret … |

Ventana de terminal
kubectl create secret generic olivares-audit-key \
--from-file=audit-signing.key=<base64-ed25519-private-key-file>
helm install olivares deploy/helm/olivares \
--set core.engine=postgres \
--set postgres.dsnSecret=olivares-pg \
--set core.replicaCount=3 \
--set core.auditSigningKeySecret=olivares-audit-key

Los tamaños canónicos de HA son 3 o 5 réplicas, activo-pasivo. Este diseño de único escritor es deliberado (encaja con el modelo de consistencia del producto); los números de preparación para producción documentan los niveles de disponibilidad que soporta.

El chart incluye un CronJob que ejecuta olivares dr backup — un paquete cifrado y seguro para la continuidad del ledger (instantánea del almacén + claves de firma selladas bajo tu KEK + manifiesto de la punta de cadena por tenant):

Ventana de terminal
kubectl create secret generic dr-kek --from-literal=passphrase='<strong passphrase>'
helm upgrade olivares deploy/helm/olivares --reuse-values \
--set backup.enabled=true \
--set backup.kekSecret=dr-kek \
--set postgres.adminDsnKey=admin-dsn # solo Postgres, y OBLIGATORIO con backups

En Postgres el chart se niega a renderizar sin postgres.adminDsnKey: el pg_dump de la copia de seguridad mantiene row_security=off y aborta como rol de aplicación bajo FORCE ROW LEVEL SECURITY, así que un CronJob cableado al DSN de la aplicación fallaría en cada ejecución y te dejaría sin copia de seguridad alguna.

Valores por defecto: cada 6 horas (backup.schedule fija tu RPO), retención local de 14 días, un PVC de destino dedicado. Replica el destino fuera del sitio y mantén la KEK separada de los paquetes — una copia de seguridad en el mismo clúster no es recuperación ante desastres. Ensaya restauraciones con olivares dr verify; el procedimiento completo está en copia de seguridad y restauración.

5. DaemonSet de colectores (ingesta distribuida)

Sección titulada «5. DaemonSet de colectores (ingesta distribuida)»

Para la topología distribuida, un DaemonSet de colectores ejecuta conectores de fuentes en cada nodo y empuja observaciones al núcleo por gRPC — los colectores no tienen listener entrante. El chart requiere un token de ingesta y acepta material mTLS opcional:

Ventana de terminal
helm upgrade olivares deploy/helm/olivares --reuse-values \
--set collectors.enabled=true \
--set collectors.ingestTokenSecret=olivares-ingest-token # ingest:write bearer token

Da a los colectores sus fuentes mediante collectors.sourcesConfig (el mismo JSON de fuentes que en todas partes), y activa el mTLS con verificación de certificado de cliente para el canal colector→núcleo con tls.grpcClientCaSecret — consulta endurecimiento.

Activa serviceMonitor si ejecutas el operador de Prometheus; en cualquier caso, el motor expone /metrics sin autenticación (no transporta datos de tenant) y el repositorio incluye reglas de alerta listas para usar. Consulta monitorizar con Prometheus.