Configuración Avanzada de Argo CD para Multi-Cluster
Argo CD es la solución GitOps de referencia para Kubernetes que sincroniza automáticamente el estado de los clústeres con la definición declarativa almacenada en Git. En entornos avanzados con múltiples clústeres, ApplicationSets y sincronización progresiva permiten gestionar cientos de aplicaciones con flujos de despliegue complejos y seguros desde un único plano de control.
Requisitos Previos
- Argo CD instalado en el clúster de gestión (v2.8+)
argocdCLI instalado- Acceso a múltiples clústeres Kubernetes (kubeconfigs)
- Repositorio Git con las definiciones de las aplicaciones
# Instalar el CLI de Argo CD
VERSION=$(curl -L -s https://raw.githubusercontent.com/argoproj/argo-cd/stable/VERSION)
curl -sSL -o /usr/local/bin/argocd \
"https://github.com/argoproj/argo-cd/releases/download/v${VERSION}/argocd-linux-amd64"
chmod +x /usr/local/bin/argocd
# Iniciar sesión en Argo CD
argocd login argocd.midominio.com \
--username admin \
--password $(kubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
Gestión de Múltiples Clústeres
# Agregar clústeres externos a Argo CD
# Opción 1: Usando el contexto de kubeconfig existente
argocd cluster add produccion-us-east \
--kubeconfig ~/.kube/config \
--name "produccion-us-east"
argocd cluster add produccion-eu-west \
--kubeconfig ~/.kube/config \
--name "produccion-eu-west"
argocd cluster add staging \
--kubeconfig ~/.kube/config \
--name "staging"
# Listar los clústeres registrados
argocd cluster list
# Etiquetar clústeres para el uso con ApplicationSets
kubectl label secret -n argocd \
$(kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=cluster -o name | grep produccion-us-east) \
region=us-east \
env=produccion \
tier=frontend
ApplicationSets para Despliegues a Escala
ApplicationSet permite generar múltiples Applications de Argo CD a partir de plantillas.
# ApplicationSet con generador de lista: desplegar en clústeres específicos
cat > appset-list.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: mi-aplicacion-multi-cluster
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: produccion-us-east
url: https://k8s-us-east.miempresa.com
values:
replicas: "5"
env: produccion
- cluster: produccion-eu-west
url: https://k8s-eu-west.miempresa.com
values:
replicas: "3"
env: produccion
- cluster: staging
url: https://k8s-staging.miempresa.com
values:
replicas: "1"
env: staging
template:
metadata:
name: "mi-aplicacion-{{cluster}}"
spec:
project: mi-proyecto
source:
repoURL: https://github.com/miempresa/k8s-manifests.git
targetRevision: HEAD
path: "apps/mi-aplicacion/overlays/{{values.env}}"
destination:
server: "{{url}}"
namespace: mi-aplicacion
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF
kubectl apply -f appset-list.yaml
# ApplicationSet con generador de clusters: desplegar en todos los clústeres con cierto label
cat > appset-cluster-selector.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: monitoring-stack
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
env: produccion
values:
revision: "main"
template:
metadata:
name: "monitoring-{{name}}"
spec:
project: default
source:
repoURL: https://github.com/miempresa/k8s-manifests.git
targetRevision: "{{values.revision}}"
path: monitoring
destination:
server: "{{server}}"
namespace: monitoring
syncPolicy:
automated:
prune: false
selfHeal: true
EOF
kubectl apply -f appset-cluster-selector.yaml
# ApplicationSet con generador de git: una app por carpeta en el repo
cat > appset-git-directories.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: apps-por-carpeta
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/miempresa/k8s-apps.git
revision: HEAD
directories:
- path: "apps/*"
- path: "apps/experimental/*"
exclude: true # Excluir apps experimentales
template:
metadata:
name: "{{path.basename}}"
spec:
project: default
source:
repoURL: https://github.com/miempresa/k8s-apps.git
targetRevision: HEAD
path: "{{path}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{path.basename}}"
syncPolicy:
syncOptions:
- CreateNamespace=true
EOF
kubectl apply -f appset-git-directories.yaml
Sync Waves y Hooks
Los sync waves permiten controlar el orden de despliegue dentro de una Application.
# Ejemplo de Application con sync waves ordenados
cat > app-con-waves.yaml << 'EOF'
# Wave -1: Namespace y CRDs (se aplican primero)
apiVersion: v1
kind: Namespace
metadata:
name: mi-app
annotations:
argocd.argoproj.io/sync-wave: "-1"
---
# Wave 0: Secrets y ConfigMaps
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: mi-app
annotations:
argocd.argoproj.io/sync-wave: "0"
data:
config.yaml: |
database_url: postgres://db:5432/miapp
---
# Wave 1: Base de datos (debe estar lista antes que la app)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql
namespace: mi-app
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
# ... spec del StatefulSet
---
# Wave 2: La aplicación principal
apiVersion: apps/v1
kind: Deployment
metadata:
name: mi-app
namespace: mi-app
annotations:
argocd.argoproj.io/sync-wave: "2"
spec:
# ... spec del Deployment
EOF
# Hook PreSync: ejecutar migraciones antes del despliegue
cat > migration-hook.yaml << 'EOF'
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
namespace: mi-app
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: migration
image: mi-app:latest
command: ["python", "manage.py", "migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
restartPolicy: Never
EOF
kubectl apply -f migration-hook.yaml
RBAC Avanzado
# Configurar RBAC granular en argocd-rbac-cm
kubectl edit configmap argocd-rbac-cm -n argocd
# Contenido del ConfigMap argocd-rbac-cm
data:
policy.default: role:readonly
policy.csv: |
# Los administradores tienen acceso completo
p, role:admin, applications, *, */*, allow
p, role:admin, clusters, *, *, allow
p, role:admin, repositories, *, *, allow
p, role:admin, projects, *, *, allow
# Los desarrolladores solo pueden sincronizar sus apps
p, role:desarrollador, applications, get, */*, allow
p, role:desarrollador, applications, sync, mi-proyecto/*, allow
p, role:desarrollador, applications, action/apps/Deployment/restart, mi-proyecto/*, allow
p, role:desarrollador, logs, get, */*, allow
# El equipo SRE puede gestionar todos los clusters de producción
p, role:sre, applications, *, produccion/*, allow
p, role:sre, clusters, get, *, allow
# Asignar roles a grupos de SSO
g, miempresa:sre-team, role:sre
g, miempresa:developers, role:desarrollador
g, miempresa:platform-team, role:admin
Notificaciones
# Instalar el controlador de notificaciones de Argo CD
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/release-1.0/manifests/install.yaml
# Configurar notificaciones de Slack
kubectl edit configmap argocd-notifications-cm -n argocd
# Configuración de notificaciones (argocd-notifications-cm)
data:
service.slack: |
token: $slack-token
username: ArgoCD
icon: ":argo:"
template.app-sync-failed: |
message: |
Application {{.app.metadata.name}} sync FAILED
Sync Status: {{.app.status.sync.status}}
Health Status: {{.app.status.health.status}}
slack:
attachments: |
[{
"color": "#E96D76",
"title": "{{ .app.metadata.name}}",
"title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}",
"fields": [
{"title": "Sync Status", "value": "{{.app.status.sync.status}}", "short": true},
{"title": "Repository", "value": "{{.app.spec.source.repoURL}}", "short": true}
]
}]
trigger.on-sync-failed: |
- when: app.status.operationState.phase in ['Error', 'Failed']
send: [app-sync-failed]
trigger.on-deployed: |
- when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
send: [app-sync-succeeded]
# Anotar una Application para recibir notificaciones
kubectl annotate application mi-aplicacion \
-n argocd \
notifications.argoproj.io/subscribe.on-sync-failed.slack=canal-alertas \
notifications.argoproj.io/subscribe.on-deployed.slack=canal-despliegues
Gestión de Secretos
# Integración con Sealed Secrets
# Instalar el controller de Sealed Secrets
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace kube-system
# Cifrar un Secret para guardarlo en Git
kubeseal --format=yaml \
--controller-namespace=kube-system \
< secret-original.yaml \
> sealed-secret.yaml
# Guardar el sealed-secret.yaml en el repositorio Git (es seguro)
git add sealed-secret.yaml
git commit -m "Agregar secreto cifrado de la aplicación"
# Alternativa: usar External Secrets Operator con ArgoCD
cat > external-secret.yaml << 'EOF'
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: mi-app
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-secret
data:
- secretKey: password
remoteRef:
key: secret/mi-app/db
property: password
EOF
Entrega Progresiva con Rollouts
# Instalar Argo Rollouts para despliegues canary y blue-green
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
-f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# Definir un Rollout con estrategia canary
cat > rollout-canary.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: mi-aplicacion
namespace: mi-app
spec:
replicas: 10
selector:
matchLabels:
app: mi-aplicacion
template:
metadata:
labels:
app: mi-aplicacion
spec:
containers:
- name: app
image: mi-aplicacion:v2.0.0
strategy:
canary:
steps:
- setWeight: 10 # 10% del tráfico a la nueva versión
- pause: {duration: 5m}
- setWeight: 30
- pause: {duration: 10m}
- setWeight: 60
- pause: {} # Pausa manual antes del 100%
# Análisis automático para decidir continuar o revertir
analysis:
templates:
- templateName: success-rate
startingStep: 1
args:
- name: service-name
value: mi-aplicacion-canary
EOF
kubectl apply -f rollout-canary.yaml
Solución de Problemas
Application en estado OutOfSync que no se sincroniza:
# Forzar la sincronización
argocd app sync mi-aplicacion --force
# Ver los recursos que están fuera de sync
argocd app diff mi-aplicacion
# Hard refresh (ignorar caché)
argocd app get mi-aplicacion --hard-refresh
# Ver los logs del controller
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller -f
ApplicationSet no genera Applications:
# Verificar el estado del ApplicationSet
kubectl describe applicationset mi-appset -n argocd
# Ver los logs del controller de ApplicationSet
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-applicationset-controller
Problemas de permisos RBAC:
# Verificar los permisos del usuario actual
argocd account can-i sync applications "mi-proyecto/mi-app"
# Ver la configuración RBAC actual
argocd admin settings rbac validate --policy-file policy.csv
Conclusión
La configuración avanzada de Argo CD con ApplicationSets, sync waves y RBAC granular transforma la gestión de despliegues multi-cluster en un proceso controlado, reproducible y auditado. Al combinar estas capacidades con notificaciones y entrega progresiva mediante Argo Rollouts, los equipos obtienen un sistema GitOps completo que puede gestionar desde un puñado de aplicaciones hasta cientos de servicios distribuidos en múltiples regiones y clústeres.


