Del commit al despliegue: flujo GitOps¶
El diagrama de abajo refleja el pipeline real (build → push imagen → promoción segura → commit del tag → sync de Argo CD). Después del diagrama hay un ejemplo con hashes de commit reales, tomado de un cambio real hecho sobre este mismo repo.
Secuencia: un commit hasta quedar corriendo en el cluster¶
sequenceDiagram
actor Dev as Devops
participant Gitea
participant CI as Gitea Actions
participant Reg as Gitea Registry
participant Argo as Argo CD
participant K8s as Cluster k3d
Dev->>Gitea: push a fix/<rama>
Dev->>Gitea: abre PR → main
Gitea->>Gitea: merge a main
Gitea->>CI: dispara workflow (paths: workloads/**)
CI->>CI: build de la imagen (Docker)
CI->>Reg: push imagen vX.Y.Z
CI->>CI: verifica promoción segura (HEAD == origin/main)
CI->>Gitea: commit "chore(gitops): release vX.Y.Z" actualizando el manifiesto
Argo->>Gitea: detecta el nuevo commit (poll/webhook)
Argo->>K8s: sync (prune:true, selfHeal:true)
K8s->>K8s: rollout del nuevo pod
Por qué esta separación (build vs. deploy)¶
- El pipeline de CI solo construye y publica la imagen — nunca aplica nada directo al cluster.
- El commit al manifiesto (
sedsobre el tag de imagen) es lo único que representa "intención de desplegar" — y vive en el mismo repo Git que Argo CD vigila. - Argo CD es la única pieza con permisos de escritura sobre el
cluster real. Nada fuera de GitOps toca
kubectl applyen producción. - El paso de "promoción segura" (comparar
HEADcontraorigin/main) evita que un pipeline viejo sobreescriba el tag con una versión anterior si hubo un push más reciente mientras corría.
Ejemplo real: un commit de principio a fin¶
Fase 3b del ADR 0006 (facetas del sidebar del catálogo, ver
decisiones/0006-motor-busqueda-facetas.md) recorrió el flujo
completo dos veces seguidas el mismo día:
| Paso | Commit / evento real |
|---|---|
| Feature | 83d448b — feat(ecommerce): facetas reales de Meilisearch en el sidebar del catalogo, en fix/adr0006-etapa2-fase3b-facetas-sidebar |
Merge a main |
6b5450f |
| CI build+push | Gitea Actions task 258, imagen gitea.cruzcloud.net/devops/ecommerce-frontend |
| Release commit (CD) | 951682a — chore: release v1.0.171 [skip ci], bump automático del tag en frontend/patches/deployment.yaml |
| Sync de Argo CD | dos ciclos distintos: primero sincronizó contra 6b5450f (el merge, todavía con el tag viejo), y en el ciclo siguiente detectó 951682a y aplicó el tag nuevo |
| Rollout | frontend-deploy con 2/2 réplicas en v1.0.171 |
El gate en vivo contra ese mismo cambio encontró un bug real
(estimatedTotalHits en 0 con limit=0 en Meilisearch) — el fix
repitió el mismo ciclo completo: 884dbd1 → merge ba3e6c7 → release
v1.0.172 → sync → rollout. Dos vueltas completas del diagrama de
arriba en la misma sesión, cada una verificada contra el endpoint real
antes de darla por cerrada.
Detalle no obvio: Argo CD sincroniza el merge commit primero (todavía con la imagen vieja, porque el bump de tag es un commit posterior que hace el propio pipeline) y recién en un segundo ciclo de poll detecta el commit de release. Son dos sincronizaciones distintas, no una sola — hay que esperar ambas antes de asumir que el pod nuevo está corriendo.
selfHeal, con la configuración real¶
Todas las Applications de este repo comparten el mismo syncPolicy
(apps/ecommerce-app.yaml):
Con selfHeal: true, si alguien corre kubectl scale deployment
frontend-deploy --replicas=1 directo contra el cluster (saltándose
Git por completo), Argo CD no se queda en mostrar OutOfSync — en el
siguiente ciclo de reconciliación vuelve a aplicar el manifiesto tal
como está en main y el Deployment queda otra vez en las réplicas
declaradas ahí. El cambio manual no se pierde silenciosamente: queda
un instante como drift visible en la UI de Argo CD, pero nunca se
vuelve el estado final. La única forma de que ese cambio persista es
que exista en Git — lo mismo que describe ADR 0002.