ADR 0002 — Por qué Argo CD (y no kubectl apply manual o un script de CI)¶
Estado: aceptada Fecha: 2026-08 (retroactiva — decisión tomada al arrancar el lab, documentada en Fase 2 del portal)
Contexto¶
Desde el principio el lab iba a tener más de un repo Git (hoy:
apps-registry para las Applications y los manifiestos reales de los
workloads, platform-infra para la gobernanza de cada namespace,
scripts para bootstrap/operación) y más de una app corriendo en el
cluster (ecommerce, docs-portal, nginx, cada una con su propia
gobernanza). Se necesitaba una forma de mantener el estado del
cluster sincronizado con lo declarado en Git que escalara a "agregar
una app nueva" sin volverse un proceso manual que dependiera de que
una sola persona recordara qué kubectl apply correr y en qué orden.
También importaba la trazabilidad: en un lab que sirve de portafolio, poder señalar "este commit es exactamente lo que está corriendo en producción" (y por qué) tiene valor por sí mismo — no solo operativo.
Opciones consideradas¶
| Opción | A favor | En contra |
|---|---|---|
| Argo CD (GitOps, pull-based) | Reconciliación continua: un proceso dentro del cluster compara el estado real contra Git y corrige la diferencia solo, sin que nadie tenga que acordarse de aplicar nada. Patrón App of Apps nativo — agregar una app nueva es un archivo YAML más en apps/, no un paso manual nuevo |
Componente extra que mantener sano (su propio namespace, su propio consumo de recursos) |
kubectl apply manual |
Cero infraestructura extra, control total en cada comando | Sin detección de drift — si alguien edita algo a mano y lo olvida, nadie se entera hasta que rompe; no escala más allá de un solo operador que recuerde el estado completo de memoria |
kubectl apply desde el pipeline de CI (push-based) |
No agrega un componente nuevo al cluster, reutiliza el CI que ya existe para construir imágenes | El pipeline de CI necesitaría credenciales de escritura amplias sobre el cluster — más superficie de riesgo si esas credenciales se filtran. Push-based además significa que nada vuelve a comparar el estado después de aplicado: un drift posterior (alguien con kubectl directo) queda invisible hasta el siguiente deploy |
Decisión¶
Argo CD, patrón App of Apps: una Application raíz
(apps-registry/application.yaml) vigila la carpeta apps/, y cada
archivo ahí dentro es una Application hija que apunta a un workload
real. Toda Application se declara con el mismo syncPolicy:
(configuración real de apps/ecommerce-app.yaml, repetida en cada
Application del repo)
Consecuencias¶
selfHeal: truecambia el modelo mental de quién puede tocar el cluster de verdad. No es solo que Argo CD detecte diferencias y las muestre comoOutOfSync— las revierte activamente. Si alguien correkubectl scaleokubectl editdirecto contra un recurso gestionado, en el siguiente ciclo de reconciliación (poll periódico, o inmediato si el commit llegó por webhook) Argo CD lo vuelve a dejar exactamente como dice Git. Git deja de ser "la intención" y pasa a ser literalmente la única fuente de la que el cluster puede aprender su propio estado — ver flujo GitOps.prune: truesignifica que borrar un recurso del repo (por ejemplo, quitarmeilisearch.yamlde unakustomization.yaml) lo borra también del cluster en el siguiente sync, sin un paso manual de limpieza — confirmado en la práctica en varias fases de este lab (ver los ADRs de rediseño del storefront y de Meilisearch).- El rol de CI se redujo a "construir, publicar imagen, y dejar un
commit en Git" — nunca aplica nada directo al cluster. Esto evitó
tener que darle a CI credenciales de cluster, y dejó un problema real
documentado (la condición de carrera de promoción cuando dos
pipelines corren en serie sobre el mismo commit) resoluble
comparando contra Git (
git merge-base --is-ancestor) en vez de contra el estado del cluster. - Se paga con un componente más que mantener sano: Argo CD corre en su propio namespace, con su propio consumo de recursos y su propia superficie de fallo — un Argo CD caído no rompe lo que ya está corriendo (el cluster sigue con el último estado aplicado), pero sí detiene la sincronización de cambios nuevos.
- Para revertir esta decisión haría falta volver a un modelo push-based (CI aplicando directo) o puramente manual, perdiendo la reconciliación continua y aceptando que un drift manual pueda pasar desapercibido.