Guía Completa de GitOps en Producción
Implementá GitOps con ArgoCD o Flux en producción: reconciliación pull, multi-cluster, despliegue progresivo, secretos con SOPS e integración CI/CD.
Introducción
Alguien hace un hotfix a un Deployment de producción con kubectl edit a las 2 AM, el cambio no queda registrado en ningún lado, y el próximo deploy programado lo borra silenciosamente. GitOps existe para eliminar esa clase de problemas: Git se convierte en el único lugar donde vive el estado deseado, y un agente dentro del cluster lo descarga y aplica — nadie toca el cluster directamente.
Esta guía recorre un setup de GitOps en producción de punta a punta: instalación y configuración de ArgoCD o Flux, estructura del repositorio GitOps, gestión de secretos con SOPS, despliegue progresivo con rollouts canary, rollback automático y la integración con tu pipeline de CI. Todo lo que sigue es YAML funcional que podés adaptar — para profundizar en ArgoCD específicamente, mirá la Guía Completa de GitOps con ArgoCD.
Principios de GitOps
GitOps, según lo define el grupo de trabajo OpenGitOps, descansa sobre cuatro propiedades. Todo lo demás en esta guía se deriva de ellas:
Principios de GitOps:
1. Declarativo — El estado del sistema se describe declarativamente en Git
2. Versionado — Git guarda el historial completo del estado deseado
3. Pull — Los agentes descargan los cambios desde Git y los aplican (no push)
4. Reconciliación continua — Los agentes comparan constantemente el estado real vs el deseado
Beneficios:
- Auditoría: cada cambio es un commit de Git con autor y timestamp
- Rollback: volver a cualquier estado anterior con git revert
- Detección de drift: los agentes detectan y corrigen cambios manuales
- Seguridad: no hace falta acceso directo al cluster para desplegar
- Colaboración: los PRs habilitan code review para cambios de infraestructura
El modelo pull es la parte que los equipos subestiman al principio. Un pipeline push necesita credenciales del cluster en CI — comprometer el CI significa comprometer el cluster. Con pull, las credenciales nunca salen del cluster; el agente solo necesita acceso de lectura a un repo Git.
Cuándo usar GitOps
GitOps vale la pena cuando:
- Corrés múltiples clusters o entornos. Templar la misma app en us-east-1, eu-west-1 y staging con un solo ApplicationSet le gana a scriptear loops de
kubectl. - La auditoría y el compliance importan. Cada cambio de producción se convierte en un PR revisado con autor y timestamp — ese es tu log de auditoría.
- Necesitás rollbacks confiables.
git revertmás auto-sync es un mecanismo de rollback que no tenés que construir ni probar aparte. - Los cambios manuales siguen causando incidentes.
selfHealconvierte “¿quién tocó esto?” en un no-evento: el agente lo revierte y lo reporta. - Los equipos despliegan de forma independiente. Cada equipo es dueño de sus manifiestos vía PRs sin tener credenciales del cluster.
GitOps no encaja en: clusters hobby de un solo desarrollador donde el overhead del agente supera al beneficio, y workflows muy imperativos (bases de datos con failover manual, jobs únicos) que no se mapean a un estado deseado declarativo. Forzarlos a entrar en GitOps crea más problemas de los que resuelve.
Cómo funciona GitOps
El pipeline se divide limpiamente en dos: CI es dueño del repo de aplicación y produce artefactos inmutables; el agente GitOps es dueño del estado del cluster y lee un repo de configuración separado.
El loop de reconciliación es el mecanismo clave: el agente descarga el estado deseado del repo GitOps en cada intervalo (30s–5m según configuración), lo compara con el estado vivo del cluster y aplica la diferencia. Un kubectl edit manual aparece como drift en la próxima reconciliación — con self-heal activado se revierte automáticamente; sin self-heal solo se reporta como OutOfSync.
Tener dos repos — uno para el código de la aplicación y otro para los manifiestos de despliegue — es deliberado. El código de la app cambia con cada feature; la config del cluster solo debería cambiar en releases intencionales. Además le permite a CI escribir en el repo de config sin que el agente del cluster necesite acceso de escritura a nada.
Setup de ArgoCD
ArgoCD es un controlador que corre dentro del cluster y expone una UI web, una API y un CLI. Es la mejor opción cuando varios equipos necesitan visibilidad del estado de sync, o cuando querés un solo plano de control gestionando muchos clusters.
Instalación de ArgoCD
# Instalar ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Obtener la contraseña de admin
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Port forward para acceder a la UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Instalar el CLI de ArgoCD
brew install argocd # macOS
# o
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd && mv argocd /usr/local/bin/
La URL del manifiesto stable siempre apunta a la última release — fijalo a un tag versionado (v3.x.y/manifests/install.yaml) en producción para que los upgrades sean deliberados y no un efecto colateral de una reinstalación.
Manifiesto Application
Un Application es la unidad de trabajo de ArgoCD: un path del repo, un destino, una política de sync.
# ArgoCD Application para el servicio API
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-service
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io # Borrado en cascada
spec:
project: production
source:
repoURL: https://github.com/myorg/k8s-manifests
targetRevision: main
path: production/api-service
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Borra recursos eliminados de Git
selfHeal: true # Revierte cambios manuales
allowEmpty: false # Evita borrar todos los recursos
syncOptions:
- CreateNamespace=true
- PruneLast=true
- ApplyOutOfSyncOnly=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
revisionHistoryLimit: 10 # Guarda las últimas 10 revisiones de sync
Tres campos hacen el trabajo pesado:
prune: trueborra los recursos vivos que ya no existen en Git — sin esto, los manifiestos eliminados quedan para siempre.selfHeal: truees la reconciliación de drift real; sin esto ArgoCD reportaOutOfSyncpero deja el cambio manual en su lugar.allowEmpty: falsees tu cinturón de seguridad: si el path del repo queda vacío por accidente (un rename mal hecho, un commit vacío), ArgoCD se niega a vaciar el namespace en vez de podarlo todo.
revisionHistoryLimit: 10 importa para el rollback — delimita hasta dónde puede llegar argocd app rollback sin tocar Git.
AppProject para multi-equipo
AppProject es la frontera de RBAC: desde qué repos puede desplegar un equipo, a qué namespaces puede apuntar y qué objetos a nivel cluster puede tocar.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Aplicaciones del entorno de producción
# Repositorios de origen permitidos
sourceRepos:
- https://github.com/myorg/k8s-manifests
- https://github.com/myorg/api-config
# Namespaces de destino permitidos
destinations:
- server: https://kubernetes.default.svc
namespace: production
- server: https://kubernetes.default.svc
namespace: production-*
# Recursos a nivel cluster permitidos
clusterResourceWhitelist:
- group: ''
kind: Namespace
- group: rbac.authorization.k8s.io
kind: RoleBinding
# Recursos denegados
clusterResourceBlacklist:
- group: ''
kind: Node
- group: ''
kind: PersistentVolume
# Roles para acceso por equipo
roles:
- name: platform-team
description: Acceso del equipo de plataforma
policies:
- p, proj:production:platform-team, applications, get, production/*, allow
- p, proj:production:platform-team, applications, sync, production/*, allow
groups:
- platform-team
La separación whitelist/blacklist es la parte importante: los equipos de plataforma pueden gestionar sus propios namespaces y RBAC, pero no pueden crear Nodes ni PersistentVolumes — el radio de impacto de un Application malo queda dentro del proyecto.
ApplicationSet para multi-cluster
ApplicationSet genera un Application por destino, así que “desplegar api-service en todos los clusters de prod” es un solo manifiesto en vez de N copias.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: api-all-clusters
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: us-east-1
url: https://1.2.3.4
- cluster: eu-west-1
url: https://5.6.7.8
- cluster: ap-southeast-1
url: https://9.10.11.12
template:
metadata:
name: 'api-{{cluster}}'
spec:
project: production
source:
repoURL: https://github.com/myorg/k8s-manifests
targetRevision: main
path: production/api-service
destination:
server: '{{url}}'
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
El generador list es el más simple; los generadores cluster y git cubren casos dinámicos (un Application por secret de cluster registrado, o por directorio del repo). Para overrides de valores por cluster, combiná ApplicationSet con overlays de Kustomize en vez de bifurcar el árbol de manifiestos.
Setup de Flux
Flux es un conjunto de controladores componibles (source, kustomize, helm, notification) sin UI. Encaja en equipos que viven en la CLI, que quieren la menor huella posible, o que ya estandarizaron en Helm y Kustomize.
Instalación de Flux
# Instalar el CLI de Flux
brew install fluxcd/tap/flux # macOS
# Bootstrap de Flux con GitHub
flux bootstrap github \
--owner=myorg \
--repository=fleet-infra \
--branch=main \
--path=clusters/production \
--personal=false
# Verificar la instalación de Flux
flux check
flux get kustomizations
flux get helmreleases
flux bootstrap es en sí mismo GitOps: commitea los manifiestos de Flux en tu repo, así que los upgrades de Flux son PRs como todo lo demás. El comando es idempotente — corrélo de nuevo para actualizar Flux en el lugar.
Flux Kustomization
Un Kustomization ata un path del repo a un loop de reconciliación — el equivalente en Flux al Application de ArgoCD.
# clusters/production/api-service.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: api-service
namespace: flux-system
spec:
interval: 1m # Reconcilia cada minuto
path: ./production/api-service
sourceRef:
kind: GitRepository
name: k8s-manifests
prune: true # Borra recursos eliminados de Git
wait: true # Espera a que los recursos estén listos
timeout: 5m
targetNamespace: production
# Health checks
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: api
namespace: production
- apiVersion: v1
kind: Service
name: api-service
namespace: production
# Sustitución de variables post-build
postBuild:
substitute:
cluster_name: production
cluster_region: us-east-1
wait: true junto con healthChecks mejora el modo de fallo: un sync no cuenta como “aplicado” hasta que el Deployment reporta ready de verdad, así que una imagen rota aparece como un Kustomization fallido en vez de pods degradados en silencio. postBuild.substitute inyecta valores por cluster sin duplicar manifiestos — combinalo con placeholders ${cluster_name} en el YAML de origen.
Flux GitRepository
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: k8s-manifests
namespace: flux-system
spec:
interval: 30s
url: https://github.com/myorg/k8s-manifests
ref:
branch: main
secretRef:
name: github-deploy-key # Para repos privados
Flux HelmRelease
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: redis
namespace: production
spec:
interval: 5m
chart:
spec:
chart: redis
version: "18.x"
sourceRef:
kind: HelmRepository
name: bitnami
namespace: flux-system
values:
architecture: replication
auth:
enabled: true
existingSecret: redis-secret
existingSecretPasswordKey: redis-password
replica:
replicaCount: 3
persistence:
enabled: true
size: 10Gi
metrics:
enabled: true
serviceMonitor:
enabled: true
Fijá el chart a un rango de versión mayor (18.x) en vez de un * a secas — seguís recibiendo parches a través del historial de Git, pero un bump de major con breaking changes no puede salir sin revisión.
ArgoCD o Flux
| ArgoCD | Flux | |
|---|---|---|
| UI | UI web completa + API + CLI | Solo CLI (las UIs son de terceros) |
| Multi-cluster | Integrado, un solo plano de control | Instalación por cluster (o vía Flux sharding) |
| Modelo de apps | CRDs Application / ApplicationSet | CRDs Kustomization / HelmRelease / GitRepository |
| Despliegue progresivo | Argo Rollouts | Flagger |
| Huella | Más pesado (API server, UI, repo server) | Controladores mínimos |
| RBAC | A nivel proyecto, granular | Solo RBAC de Kubernetes |
Elegí ArgoCD cuando varios equipos necesitan visibilidad compartida y RBAC por app, o cuando gestionás muchos clusters desde un solo panel. Elegí Flux para equipos CLI-first, integración ajustada con Helm, o mínimo overhead del plano de control. Muchas organizaciones corren ambos — Flux para la infraestructura de plataforma, ArgoCD para los equipos de aplicación que quieren la UI.
Estructura del repositorio GitOps
Cómo estructures el repo de config determina cuánto dolerá el trabajo multi-entorno. Una estructura que escala:
k8s-manifests/
├── base/ # Manifiestos compartidos (base de kustomize)
│ └── api-service/
│ ├── deployment.yaml
│ └── service.yaml
├── production/ # Overlays por entorno
│ ├── api-service/
│ │ └── kustomization.yaml # tag de imagen, réplicas, host de ingress
│ └── secrets/
│ └── api-secrets.yaml # cifrado con SOPS
├── staging/
│ └── api-service/
│ └── kustomization.yaml
└── clusters/ # Bootstrap de Flux / wiring por cluster
└── production/
└── api-service.yaml
Dos reglas que funcionan en la práctica:
- Un repo GitOps por entorno (o cluster), no por app. Una única fuente de verdad solo funciona si “la verdad” está en un solo lugar; un repo por app la dispersa.
- Overlays antes que branches. Una branch
stagingque mergea amainpara promover suena atractiva y produce drift constante entre branches. Un directorio por entorno con overlays de Kustomize mantiene todos los entornos visibles a la vez.
Gestión de secretos con SOPS
Los secretos en plaintext en Git son un no rotundo — el historial de Git es para siempre. Las opciones compatibles con GitOps:
- SOPS + age/KMS: cifra los archivos de secretos del lado del cliente, commitealos, y dejá que el agente los descifre dentro del cluster. Funciona con Flux nativamente y con ArgoCD vía plugins.
- External Secrets Operator: mantené los secretos en AWS Secrets Manager, Vault o Key Vault; ESO los sincroniza a Secrets de Kubernetes. La mejor opción cuando ya existe un gestor de secretos.
- Sealed Secrets: cifrado contra una clave pública específica del cluster; el archivo sellado es seguro de commitear. Simple, pero la rotación es incómoda.
SOPS es el punto de partida más liviano:
# Instalar SOPS y age
brew install sops age
# Generar el par de claves age
age-keygen -o sops-key.txt
# Clave pública: age1xxxxx...
# Clave privada: AGE-SECRET-KEY-1xxxxx...
# Cifrar un archivo de secretos
sops --encrypt --age age1xxxxx... --in-place secret.yaml
# Descifrar un archivo de secretos
sops --decrypt secret.yaml
# Editar un archivo cifrado en el lugar
sops secret.yaml
# Rotar claves
sops updatekeys secret.yaml
# Secreto cifrado (SOPS)
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
namespace: production
type: Opaque
stringData:
DB_PASSWORD: ENC[AES256_GCM,data:abc123,iv:xxx,tag:yyy,type:str]
API_KEY: ENC[AES256_GCM,data:def456,iv:xxx,tag:yyy,type:str]
sops:
age:
- recipient: age1xxxxx...
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
...
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-07-04T12:00:00Z"
mac: ENC[AES256_GCM,data:mac,iv:xxx,tag:yyy,type:str]
# Kustomization de Flux con descifrado SOPS
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: api-secrets
namespace: flux-system
spec:
interval: 1m
path: ./production/secrets
sourceRef:
kind: GitRepository
name: k8s-manifests
prune: true
decryption:
provider: sops
secretRef:
name: sops-age-key # Secret que contiene la clave privada
La clave privada de age vive como un Secret del cluster (sops-age-key) — nunca en Git. Rotarla implica sops updatekeys en cada archivo cifrado más un Secret nuevo en el cluster; scripteá eso antes de necesitarlo a las 3 AM. Si necesitás almacenamiento centralizado de secretos más allá de Kubernetes, el External Configuration Store Pattern cubre ese lado.
Despliegue progresivo
El despliegue progresivo acota el radio de impacto de una release mala: enrutá una fracción del tráfico a la versión nueva, mirá la tasa de errores y la latencia, y promové o revertí automáticamente según las métricas.
Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
namespace: production
spec:
replicas: 10
strategy:
canary:
canaryService: api-canary
stableService: api-stable
trafficRouting:
nginx:
stableIngress: api-stable-ingress
steps:
- setWeight: 10
- pause: { duration: 2m }
- setWeight: 25
- pause: { duration: 2m }
- setWeight: 50
- pause: { duration: 5m }
- setWeight: 75
- pause: { duration: 5m }
analysis:
templates:
- templateName: success-rate
startingStep: 2 # Empieza el análisis después del 25% canary
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
# CI fija el tag inmutable en cada release — nunca "latest"
image: ghcr.io/myorg/api:v1.4.2
ports:
- containerPort: 8000
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
# Template de análisis para canary
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
namespace: production
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.99
failureLimit: 3 # Aborta después de 3 fallos
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{status!~"5.."}[2m]))
/
sum(rate(http_requests_total[2m]))
El AnalysisTemplate es lo que convierte esto en una quality gate en vez de un load balancer sofisticado: se consulta Prometheus cada minuto, y tres checks fallidos abortan el rollout. Ajustá successCondition a una señal en la que realmente alertás — un umbral de éxito del 99% con ventana de 1m detecta roturas obvias rápido; las regresiones sutiles necesitan pasos más largos y una métrica de latencia p95 al lado.
Flux Flagger
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: api
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api
service:
port: 80
targetPort: 8000
gateways:
- public-gateway.istio-system.svc.cluster.local
hosts:
- api.stackpractices.com
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 30s
webhooks:
- name: load-test
type: rollout
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://api.production:80/"
Flagger mueve stepWeight (10%) en cada interval (1m) hasta maxWeight, verificando la tasa de éxito y la duración p99 en cada paso. El webhook load-test importa más de lo que parece: sin tráfico de base, un canary que recibe 2 requests por minuto produce ruido en las métricas, no señal.
Rollback automático
Con GitOps, el rollback es una operación de Git — ese es el punto. Hay dos caminos y conviene saber cuándo aplica cada uno:
git revert+ push (preferido). El agente sincroniza el cluster al estado revertido. El rollback en sí queda como historial auditable y revisable — el mismo workflow que cualquier cambio.- Rollback puntual de ArgoCD (emergencia).
argocd app rollbackfija la app a una revisión anterior en el cluster sin commit de Git. Es más rápido, pero salta el rastro de auditoría — seguilo con ungit revertpara que Git y el cluster converjan de nuevo.
# Application de ArgoCD — ajustes de historial que habilitan el rollback
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-service
namespace: argocd
spec:
revisionHistoryLimit: 10 # argocd app rollback puede volver hasta estas revisiones
syncPolicy:
automated:
prune: true
selfHeal: true
# Rollback en ArgoCD — usá el ID de historial, no un SHA de Git
argocd app history api-service # encontrá el ID de revisión al que volver
argocd app rollback api-service <id> # fija esa revisión
# Flux: rollback revirtiendo el commit de Git
git revert <commit-sha>
git push origin main
# Flux sincroniza automáticamente al estado revertido en la próxima reconciliación
Ojo con la trampa del rollback con selfHeal: si revertís fijando una revisión mientras selfHeal está activo, ArgoCD ve el estado fijado como drift respecto a Git y te pelea el cambio. Desactivá el auto-sync primero (argocd app set api-service --sync-policy none), o hacé el git revert y dejá que el sync normal se encargue.
Integración con CI/CD
El trabajo de CI en GitOps es construir el artefacto y actualizar el repo GitOps — nunca tocar el cluster.
# GitHub Actions: build y actualización del repo GitOps
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build y push de la imagen
run: |
docker build -t ghcr.io/myorg/api:${{ github.sha }} .
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/myorg/api:${{ github.sha }}
- name: Actualizar el repo GitOps
run: |
git clone https://github.com/myorg/k8s-manifests.git
cd k8s-manifests
# Actualizar el tag de imagen en el manifiesto
sed -i "s|ghcr.io/myorg/api:.*|ghcr.io/myorg/api:${{ github.sha }}|" production/api-service/deployment.yaml
git config user.name "CI Bot"
git config user.email "ci@stackpractices.com"
git add .
git commit -m "Deploy api:${{ github.sha }}"
git push
Notas para endurecer ese workflow en producción:
- Tagueá las imágenes con
${{ github.sha }}(o semver), nuncalatest. GitOps solo funciona si el estado deseado en Git es exacto —latestes estado mutable escondido fuera de Git. - Preferí
kustomize edit set image api=ghcr.io/myorg/api:${{ github.sha }}sobresedcuando tus manifiestos sean Kustomize; entiende la sintaxis y no puede corromper un manifiesto. - Con muchas apps, salteá el
sedartesanal y usá ArgoCD Image Updater o los CRDsImagePolicy/ImageRepositoryde Flux — el propio agente escribe los commits de tags en el repo.
Código complementario: manifiestos GitOps para producción: todos los YAML de esta guía — ArgoCD, Flux, SOPS, rollouts y el workflow de CI.
Buenas prácticas
- Separá el repo de la app del repo de config. CI pushea tags de imagen al repo de config; el agente solo lo lee. Mezclarlos crea una tormenta de commits en el repo que leen tus auditores.
- Tags de imagen inmutables en todos lados. Un tag
v1.4.2en Git es un hecho;latestes un rumor. El rollback, la detección de drift y la promoción entre entornos asumen un estado deseado exacto. - Gateá los syncs por health, no solo por apply.
wait: true+healthChecksen Flux, sync waves + health assessment en ArgoCD — un sync que devuelve éxito mientras los pods crashean es peor que un fallo visible. - Usá sync waves para dependencias ordenadas. Anotá los recursos con
argocd.argoproj.io/sync-wave(CRDs y RBAC primero, apps al final) para que un entorno nuevo converja en el orden correcto. - Limitá
revisionHistoryLimitdeliberadamente. 10 es un default sano — suficiente margen de rollback, huella chica en etcd. - Ensayá la rotación de claves.
sops updatekeys, Secret nuevo en el cluster, re-cifrado — si nunca lo hiciste, la primera vez va a ser durante un incidente.
Errores comunes
selfHeal: true+allowEmptysin definir. Un accidente con el path vacío y el agente poda el namespace. Siempre definíallowEmpty: falseen sync policies automatizadas.- Secretos commiteados en plaintext “temporalmente”. Git recuerda. Si alguna vez tocó el repo sin cifrar, rotalo — la fuga ya pasó.
- Intervalos de reconciliación demasiado agresivos.
interval: 10s× 200 aplicaciones = carga constante sobre el API server y el proveedor de Git. Empezá en 1–5m y ajustá solo donde la latencia importe. - Podar CRDs o namespaces compartidos.
prune: trueen un Application dueño de objetos a nivel cluster puede borrar recursos de los que dependen otras apps. Mantené los objetos cluster-scoped en un Application separado, sin prune. - Dejar que CI haga
kubectl apply“solo esta vez”. Cada apply directo es drift que el agente va a revertir o a ocultar. Sin excepciones — así se mantiene el rastro de auditoría significativo. - Promoción de entornos via merge de branches. Staging→prod mergeando branches produce dos historiales divergentes. Promové copiando el tag de imagen (o el cambio de overlay) al path de prod vía PR.
Solución de Problemas
Fallos de sync que se repiten en producción, y cómo leerlos:
Application clavada en OutOfSync después de correr sync. Corré argocd app diff api-service — el diff suele mostrar un campo inmutable (spec.selector de un Deployment no puede cambiar en el lugar) o un admission webhook rechazando el manifiesto. Se arregla borrando el objeto para que el agente lo recree, o sumando la sync option Replace=true para ese recurso.
El borrado en cascada se cuelga para siempre. El resources-finalizer bloquea el borrado del Application mientras un recurso gestionado está trabado (loop de finalizers, namespace inalcanzable). Como último recurso, quitá el finalizer: kubectl -n argocd patch app api-service --type=json -p='[{"op":"remove","path":"/metadata/finalizers"}]' — los recursos huérfanos te tocan a vos.
El Kustomization de Flux muestra Ready=False pero los pods están Running. wait: true más healthChecks verifica los objetos exactos listados — un Deployment renombrado falla el check aunque la carga esté sana. kubectl -n flux-system describe kustomization api-service muestra qué objeto hizo timeout.
Errores de descifrado SOPS después de rotar claves. O el Secret del cluster con la clave privada de age no se actualizó, o el secretRef del Kustomization sigue apuntando al nombre viejo. Compará kubectl -n flux-system get secret sops-age-key contra la línea recipient del archivo cifrado — tienen que coincidir.
ApplicationSet generó apps para los clusters equivocados. El generador list o cluster matcheó más de lo previsto. Inspeccioná los nombres generados con kubectl -n argocd get applications antes de activar prune — un generador mal configurado a escala no solo crea apps, las borra.
See Also
- Guía Completa de GitOps con ArgoCD
- Guía Completa de Networking en Kubernetes
- Guía Completa de Terraform en Producción
- Patrón External Configuration Store
- Estrategias Multi-Cloud — Beneficios y Riesgos
Lectura Adicional
- Especificación OpenGitOps — los cuatro principios que implementa esta guía
- Documentación de ArgoCD — Applications, Projects, sync options, Image Updater
- Documentación de Flux — bootstrap, Kustomization, descifrado SOPS, Image Automation
- Documentación de Argo Rollouts — pasos canary, AnalysisTemplates, traffic routing
- Documentación de Flagger — CRD Canary, checks de métricas, webhooks
- SOPS — formatos de cifrado, backends age/KMS, rotación de claves
Preguntas frecuentes
¿Cuál es la diferencia entre ArgoCD y Flux?
Ambos implementan GitOps para Kubernetes. ArgoCD trae una UI web, multi-cluster desde un solo plano de control y ApplicationSets para templating. Flux es más liviano, es puros CRDs y tiene integración nativa más ajustada con Helm y Kustomize. Elegí ArgoCD si los equipos necesitan una UI y multi-cluster centralizado. Elegí Flux para workflows CLI-driven y mínima huella.
¿Cómo funciona la reconciliación de drift?
El agente compara continuamente el estado vivo del cluster con el estado deseado en Git. Cuando detecta una diferencia — un kubectl edit manual, un recurso borrado — reaplica el estado de Git. Con selfHeal: true (ArgoCD) o prune: true + reconciliación automatizada (Flux), los cambios manuales se revierten solos; sin self-heal, el drift solo se reporta como OutOfSync.
¿Cómo manejo los secretos en GitOps?
Nunca commitees secretos en plaintext. Cifrá los archivos del lado del cliente con SOPS (age o AWS KMS) y commiteá el cifrado — el agente los descifra dentro del cluster con una clave privada guardada como Secret del cluster. O mantené los secretos fuera de Git con External Secrets Operator (lee de AWS Secrets Manager, Vault, Key Vault) o Sealed Secrets. Detalles completos en Gestión de secretos con SOPS.
¿Qué es el despliegue progresivo en GitOps?
Sacar una versión nueva a una fracción del tráfico — 10%, 25%, 50% — mientras checks de métricas automatizados corren en cada paso, en vez de actualizar todos los pods a la vez. Argo Rollouts y Flagger lo implementan: si el check de tasa de éxito o latencia falla, el tráfico vuelve a la versión estable. Es una quality gate, no solo traffic shaping.
¿Debería usar un repo Git o varios para GitOps?
Un repo de config por cluster o entorno, y repos separados para el código de aplicación. Un solo repo GitOps mantiene visible el estado deseado completo y hace los cambios atómicos; el código de la app vive en otro lado para que CI pueda actualizar tags de imagen sin que el agente necesite escritura sobre el código. Ver Estructura del repositorio GitOps.
¿Cómo hago rollback de un deployment en GitOps?
Preferí git revert + push — el agente sincroniza al estado revertido y el rollback queda como historial auditado. En emergencias, argocd app rollback <app> <id> fija una revisión anterior al instante (el ID sale de argocd app history), pero seguilo con un revert para que Git se ponga al día. Detalles y la trampa del self-heal en Rollback automático.
¿Cómo actualiza CI el repo GitOps?
CI construye y pushea la imagen, clona el repo de config, actualiza el tag de imagen (kustomize edit set image o un sed templado), commitea y pushea. El agente toma el cambio en la próxima reconciliación. ArgoCD Image Updater o el ImagePolicy de Flux automatizan el commit del tag por completo.
¿GitOps funciona para bases de datos y cargas stateful?
Parcialmente. Desplegar el StatefulSet y su config vía GitOps funciona bien; las migraciones de esquema y las decisiones de failover no se mapean bien a sync declarativo y suelen quedar como jobs de CI u operators. La regla general: si la operación es "converger a este estado", GitOps encaja; si es "correr este procedimiento", dejala fuera del loop de sync.
Recursos Relacionados
GitOps con ArgoCD
Despliega aplicaciones Kubernetes con GitOps usando ArgoCD. Cubre instalación, ApplicationSets, estrategias de sync, Helm/Kustomize, RBAC y multi-cluster.
GuideKubernetes Networking
Kubernetes networking. Cubre Services, Ingress, NetworkPolicies, CNI plugins, DNS, service mesh, load balancing, external traffic, mTLS y troubleshooting con ejemplos practicos de YAML manifests y configuracion.
GuideTerraform en Producción
Gestionar infrastructure as code con Terraform en produccion. Cubre modules, state management, workspaces, drift detection, remote backends, variable validation, sentinel policies y CI/CD integration con ejemplos practicos de HCL.
GuideMonitoring y Alerting
Construir un production monitoring stack. Cubre Prometheus, Grafana, AlertManager, metrics instrumentation, alert rules, runbooks, SLI/SLO/SLA, distributed tracing con Jaeger, log aggregation y on-call best practices con ejemplos practicos de configuracion.
PatternPatrón de Almacén Externo de Configuración
Centraliza la configuración de aplicaciones fuera de los artefactos de despliegue para soportar actualizaciones en vivo, separación de entornos y cambios auditados.