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.