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+)
  • argocd CLI 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.