# 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`: ```yaml syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true ``` (configuración real de `apps/ecommerce-app.yaml`, repetida en cada Application del repo) ## Consecuencias - **`selfHeal: true` cambia el modelo mental de quién puede tocar el cluster de verdad.** No es solo que Argo CD detecte diferencias y las muestre como `OutOfSync` — las revierte activamente. Si alguien corre `kubectl scale` o `kubectl edit` directo 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](../arquitectura/gitops-flujo.md). - **`prune: true`** significa que borrar un recurso del repo (por ejemplo, quitar `meilisearch.yaml` de una `kustomization.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.