Skip to content

ADR-0021: app-manager en d — adapter k8s nativo, sandbox-first, CI externo + GitOps

Date: 2026-09-11 · Status: Accepted (decisiones del operador 2026-09-08 → 2026-09-11) Related: OBJ-APPMANAGER-WETOPI3-001, ADR-0009 (vault), ARCH-PLATFORM-GITOPS, SVC-VAULT

Context

app-manager (worker headless RabbitMQ, 9.2.0) orquesta wepods (WordPress/Drupal) en Swarm c ejecutando playbooks Ansible por SSH. El cluster d (RKE2) es la nueva plataforma. Durante el ciclo wetopi3 (2026-09-08→11) el operador tomó cuatro decisiones de arquitectura que este ADR consolida. Hallazgos de red verificados: registry.service.wetopi.priv y el broker (192.168.97.x) pertenecen a la LAN de c y NO son alcanzables desde la LAN de d (10.72.0.0/23, ops01) — la ruta c↔d no existe.

Decision

D1 — Sandbox-first (2026-09-09). d es un sandbox de iteración rápida: wepods de prueba y versiones de app-manager se despliegan directamente, sin mecánica canary. El canary de consumidores competidores (decisión previa del 2026-09-09) quedó REVOCADO. La transición productiva de wepods c→d y la retirada del runtime de c se difieren a un objetivo propio futuro.

D2 — Sin Ansible en d (2026-09-11). El runtime de d no usa Ansible ni SSH: app-manager gestiona wepods directamente contra la API de Kubernetes (adapter infrastructure/k8s/ con la misma interface { exec } por DI; cliente oficial @kubernetes/client-node con ServiceAccount in-cluster; RBAC en wetopi-prod: statefulsets/pvcs/secrets/pods/pods/exec). El dominio (get-playbook-config-command.js, el cerebro) queda intacto y el contrato de eventos no cambia: …manager.event.<op>-done / …manager.error.<op>. La forma k8s de un wepod: StatefulSet + PVC + Secret (doc de-swarm-a-kubernetes.md Parte II/III). Los tests por evento (DP-05) se escriben ANTES del adapter (DP-06) como contrato.

D3 — Builds en CI externos; imagen de base pública (2026-09-11). No hay builder en d (Zot PLANNED). La línea Swarm sigue con su GitLab CI (tags→ registry de c). La línea k8s construye en GitHub Actions (repo espejo privado wetopi-platform/app-manager, tags wetopi3-*) y publica en GHCR — el registry que d ya consume (patrón docs-runtime). Base image pública node:24.4-bookworm (el Dockerfile instala deps y copia la app; USER node; CMD node server/index.js) — elimina la dependencia del registry privado inalcanzable. Digest-pinned en manifiestos; GIT_SHA baked (label source.revision). Despliegue: SOLO Git→Argo CD (no hay CI kubectl apply).

D4 — Secretos por Infisical, trío v1beta1 (2026-09-11). Credenciales RabbitMQ y config viven en el vault (harness/prod, /services/external/rabbitmq-wetopi) y se materializan con el patrón canónico del Infisical Operator v0.11.8: InfisicalConnection (endpoint interno vault.vault.svc.cluster.local — Decision A: jamás el edge) + InfisicalAuth (Universal Auth, identidad de máquina por workload) + InfisicalStaticSecret (sources→target Secret nativo, creationPolicy: Owner). Grupo CRD: secrets.infisical.com (el infisical.io/v1beta1 + InfisicalSecret de los manifiestos originales era una combinación inexistente — corregido). El operator se despliega primero (era "future reviewed change" de SVC-VAULT; app-manager es su primer consumidor real).

Alternatives considered

  • Canary de consumidores competidores (aceptado 09-09, revocado 09-09): correcto para cutover productivo, innecesario para un sandbox vacío.
  • Ruta de red c↔d / mirror del registry privado: resuelve el pull pero añade complejidad de red (DECISIÓN REQUERIDA pendiente para el BROKER); la base pública la evita para builds.
  • Mantener Ansible contra nodos de d: duplicaría la superficie SSH y el spawn de 2-3 s por operación; la API k8s es el diseño objetivo del doc.
  • Ansible dentro de la imagen con SSH: mantiene lastre heredado de c.

D5 — nginx-certs-* fuera del runtime de d (operador, 2026-09-12)

Las operaciones nginx-certs-create/remove/update (3 de las 26) quedan fuera del scope del app-manager de d: no las gestiona el adapter k8s y nadie las ejecuta en el sandbox. En c siguen como hoy (Ansible, playbooks conservados en el repo). Si d necesita gestionar certificados en el futuro (cert-manager ya existe en la plataforma), será una tarjeta/decisión propia — no se implementó "por si acaso" (guardrails).

Consequences

  • Positivas: cero dependencia del registry privado para el ciclo de vida de builds; contrato de eventos estable (c y d interoperan por el mismo exchange); dominio reutilizable; el cerebro queda testeable por DI.
  • Negativas/costes: el broker sigue en la red de c → DP-04 (consumo real de prod:d.manager.tasks) queda bloqueado hasta ruta de red o broker en d (objetivo final declarado: mover RabbitMQ a d); espejo GitHub↔GitLab de la rama (push dual manual por ahora).
  • Pendientes operador: ruta d↔red de c (o adelantar broker→d); visibilidad del package GHCR; rotación de tokens al cierre.