Argo CD es la herramienta GitOps de la CNCF que mantiene el estado de Kubernetes sincronizado con lo que hay en un repositorio Git. Una sola instancia puede gestionar muchos clústeres: el clúster donde está instalada actúa como plano de control y el resto se registran como destinos. En este tutorial registrarás dos clústeres remotos, desplegarás la misma aplicación en todos con un ApplicationSet, controlarás el orden de despliegue con sync waves y hooks, restringirás permisos con proyectos y RBAC, y configurarás avisos en Slack.

Requisitos previos

Para seguir esta guía necesitas:

  • Argo CD 2.8 o superior (los ejemplos se han escrito para Argo CD 3.x) instalado en un clúster de gestión, en el namespace argocd, y accesible desde tu equipo en argocd.tu_dominio o mediante port-forward.
  • Uno o más clústeres Kubernetes adicionales como destino, con sus contextos en tu ~/.kube/config. En esta guía se llaman staging y produccion-eu.
  • kubectl configurado para el clúster de gestión, con permisos de administrador.
  • Un repositorio Git con manifiestos de Kubernetes o overlays de Kustomize. En los ejemplos se usa https://github.com/tu_organizacion/k8s-manifests.git.
  • Para el paso de notificaciones: un workspace de Slack donde puedas crear una app con un token de bot.

Paso 1: Instalar el CLI de Argo CD e iniciar sesión

Descarga el binario de la última versión estable e instálalo en /usr/local/bin:

curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm argocd-linux-amd64

Comprueba la versión del cliente:

argocd version --client --short

Obtén la contraseña inicial del usuario admin, que Argo CD genera en la instalación:

argocd admin initial-password -n argocd

Inicia sesión contra el servidor de Argo CD con esa contraseña:

argocd login argocd.tu_dominio --username admin
'admin:login' logged in successfully
Context 'argocd.tu_dominio' updated

Si no tienes Argo CD expuesto con un Ingress, abre un port-forward en otra terminal con kubectl -n argocd port-forward svc/argocd-server 8080:443 y usa argocd login localhost:8080 --insecure.

Paso 2: Registrar los clústeres de destino

argocd cluster add crea en el clúster remoto una ServiceAccount argocd-manager con permisos de administrador y guarda sus credenciales en un Secret del clúster de gestión. Las etiquetas que añadas al registrarlo son las que usarán después los ApplicationSets para decidir dónde desplegar.

Lista los contextos disponibles en tu kubeconfig:

kubectl config get-contexts -o name

Registra cada clúster con un nombre legible y etiquetas de entorno y región:

argocd cluster add staging --name staging --label env=staging --label region=eu
argocd cluster add produccion-eu --name produccion-eu --label env=produccion --label region=eu

Argo CD te pedirá confirmación antes de crear la ServiceAccount en cada clúster. Comprueba que están registrados:

argocd cluster list
SERVER                           NAME            VERSION  STATUS      MESSAGE  PROJECT
https://10.0.10.10:6443          staging                  Unknown     Cluster has no applications and is not being monitored.
https://10.0.20.10:6443          produccion-eu            Unknown     Cluster has no applications and is not being monitored.
https://kubernetes.default.svc   in-cluster      1.33     Successful

El estado Unknown es normal hasta que haya alguna aplicación desplegada en ese clúster. Las etiquetas se guardan en el Secret del clúster; puedes verlas con:

kubectl -n argocd get secrets -l argocd.argoproj.io/secret-type=cluster --show-labels

Paso 3: Crear un proyecto para limitar destinos

Un AppProject define qué repositorios se pueden usar, en qué clústeres y namespaces se puede desplegar y qué tipos de recursos se permiten. Es la base del RBAC: los permisos se conceden por proyecto.

nano proyecto-web.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: web
  namespace: argocd
spec:
  description: Aplicaciones web desplegadas en staging y producción
  sourceRepos:
    - https://github.com/tu_organizacion/k8s-manifests.git
  destinations:
    - name: staging
      namespace: web-*
    - name: produccion-eu
      namespace: web-*
  clusterResourceWhitelist:
    - group: ""
      kind: Namespace

Con esta configuración, las aplicaciones del proyecto web solo pueden desplegar desde ese repositorio, en namespaces que empiecen por web- y en los dos clústeres registrados. El único recurso de ámbito de clúster permitido es Namespace.

kubectl apply -f proyecto-web.yaml
argocd proj get web

Paso 4: Desplegar en varios clústeres con un ApplicationSet

Un ApplicationSet genera objetos Application a partir de una plantilla y un generador. El generador clusters produce una aplicación por cada clúster registrado que cumpla un selector de etiquetas, así que un clúster nuevo recibe la aplicación en cuanto lo registras con la etiqueta adecuada.

Este ejemplo supone que el repositorio tiene overlays de Kustomize en apps/frontend/overlays/staging y apps/frontend/overlays/produccion:

nano appset-frontend.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: frontend
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - clusters:
        selector:
          matchExpressions:
            - key: env
              operator: In
              values: ["staging", "produccion"]
  template:
    metadata:
      name: "frontend-{{.name}}"
    spec:
      project: web
      source:
        repoURL: https://github.com/tu_organizacion/k8s-manifests.git
        targetRevision: main
        path: "apps/frontend/overlays/{{index .metadata.labels \"env\"}}"
      destination:
        server: "{{.server}}"
        namespace: web-frontend
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

Las variables disponibles con el generador clusters son, entre otras, .name y .server del clúster y .metadata.labels con sus etiquetas. missingkey=error hace que el ApplicationSet falle de forma visible si una plantilla usa una variable inexistente, en vez de generar una ruta vacía.

kubectl apply -f appset-frontend.yaml

Comprueba que se ha generado una aplicación por clúster y que se sincronizan:

argocd appset get frontend
argocd app list
NAME                          CLUSTER                  NAMESPACE     PROJECT  STATUS  HEALTH   SYNCPOLICY
argocd/frontend-produccion-eu https://10.0.20.10:6443  web-frontend  web      Synced  Healthy  Auto-Prune
argocd/frontend-staging       https://10.0.10.10:6443  web-frontend  web      Synced  Healthy  Auto-Prune

Una aplicación por carpeta con el generador Git

Si el repositorio tiene una carpeta por aplicación, el generador git con directories crea una Application por carpeta y se actualiza solo cuando añades o borras carpetas. Este ejemplo despliega todo lo que haya bajo apps/ en el clúster de gestión, excepto apps/experimental:

nano appset-carpetas.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apps-por-carpeta
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - git:
        repoURL: https://github.com/tu_organizacion/k8s-manifests.git
        revision: main
        directories:
          - path: "apps/*"
          - path: "apps/experimental"
            exclude: true
  template:
    metadata:
      name: "{{.path.basename}}"
    spec:
      project: default
      source:
        repoURL: https://github.com/tu_organizacion/k8s-manifests.git
        targetRevision: main
        path: "{{.path.path}}"
      destination:
        name: in-cluster
        namespace: "{{.path.basename}}"
      syncPolicy:
        syncOptions:
          - CreateNamespace=true
kubectl apply -f appset-carpetas.yaml
argocd appset get apps-por-carpeta

Los generadores se pueden combinar con matrix para desplegar cada carpeta en cada clúster, pero empieza con un solo generador hasta que el resultado sea el esperado.

Paso 5: Ordenar el despliegue con sync waves y hooks

Por defecto Argo CD aplica los recursos en un orden fijo por tipo (namespaces, luego ConfigMaps y Secrets, luego Deployments...). Cuando necesitas un orden propio, por ejemplo que una base de datos esté lista antes que la aplicación, usa la anotación argocd.argoproj.io/sync-wave. Argo CD aplica las olas de menor a mayor y espera a que los recursos de una ola estén sanos antes de pasar a la siguiente.

Estas anotaciones van en los manifiestos del repositorio Git, no se aplican a mano con kubectl. Por ejemplo, en el StatefulSet de la base de datos:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgresql
  annotations:
    argocd.argoproj.io/sync-wave: "0"

Y en el Deployment de la aplicación:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  annotations:
    argocd.argoproj.io/sync-wave: "1"

Los recursos sin anotación pertenecen a la ola 0. Puedes usar valores negativos para lo que debe ir antes de todo.

Los hooks son recursos que Argo CD ejecuta en un momento concreto de la sincronización en lugar de mantenerlos desplegados. El caso típico es un Job que ejecuta las migraciones de la base de datos antes de actualizar la aplicación. Añade este archivo al mismo directorio del repositorio:

apiVersion: batch/v1
kind: Job
metadata:
  name: migraciones
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
  backoffLimit: 1
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: registry.tu_dominio/frontend:1.4.2
          command: ["python", "manage.py", "migrate"]
          envFrom:
            - secretRef:
                name: frontend-db

Con PreSync, Argo CD crea el Job, espera a que termine y solo entonces aplica el resto de recursos. Si el Job falla, la sincronización se detiene y la versión anterior sigue en marcha. BeforeHookCreation borra el Job de la ejecución anterior justo antes de crear el nuevo, así que siempre puedes consultar los logs de la última migración.

Tras hacer commit y push, comprueba el orden en la sincronización:

argocd app sync frontend-staging
argocd app get frontend-staging

En la salida verás el Job migraciones con fase PreSync completado antes que el resto de recursos.

Paso 6: Configurar el RBAC

Los permisos de Argo CD se definen en el ConfigMap argocd-rbac-cm. Cada línea p concede una acción sobre un recurso, y las líneas g asignan roles a usuarios o a grupos de tu proveedor SSO. Para aplicaciones, el objeto tiene la forma <proyecto>/<aplicación>.

nano argocd-rbac-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-rbac-cm
    app.kubernetes.io/part-of: argocd
data:
  policy.default: role:readonly
  policy.csv: |
    # Desarrolladores: ver todo, sincronizar y reiniciar solo en el proyecto web
    p, role:desarrollador, applications, get, web/*, allow
    p, role:desarrollador, applications, sync, web/*, allow
    p, role:desarrollador, applications, action/apps/Deployment/restart, web/*, allow
    p, role:desarrollador, logs, get, web/*, allow

    # SRE: control total de las aplicaciones y lectura de clústeres
    p, role:sre, applications, *, */*, allow
    p, role:sre, clusters, get, *, allow

    # Grupos del proveedor SSO
    g, tu_organizacion:developers, role:desarrollador
    g, tu_organizacion:sre, role:sre
    g, tu_organizacion:platform, role:admin

policy.default: role:readonly da acceso de solo lectura a cualquier usuario autenticado que no tenga otro rol. Si prefieres que no vean nada, deja ese campo vacío.

Antes de aplicarlo, valida la sintaxis y comprueba un permiso concreto con el propio CLI:

argocd admin settings rbac validate --policy-file argocd-rbac-cm.yaml
argocd admin settings rbac can role:desarrollador sync applications 'web/frontend-staging' --policy-file argocd-rbac-cm.yaml
Yes
kubectl apply -f argocd-rbac-cm.yaml

Los cambios se aplican sin reiniciar nada. Un usuario puede comprobar sus propios permisos con argocd account can-i sync applications 'web/frontend-staging'.

Paso 7: Recibir avisos en Slack

El controlador de notificaciones viene incluido en Argo CD desde la versión 2.3. Solo tienes que configurar el servicio y elegir qué eventos quieres recibir.

Instala el catálogo oficial de triggers y plantillas (on-sync-failed, on-deployed, on-health-degraded...). Este comando sustituye el ConfigMap argocd-notifications-cm, así que hazlo antes de añadir configuración propia:

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml

Crea una app en Slack con el permiso chat:write, instálala en el workspace, invita al bot a los canales y guarda su token en el Secret de notificaciones:

kubectl -n argocd patch secret argocd-notifications-secret --type merge \
  -p '{"stringData":{"slack-token":"xoxb-tu_token_de_slack"}}'

Añade el servicio Slack al ConfigMap. $slack-token hace referencia a la clave del Secret, no al token en claro:

kubectl -n argocd patch configmap argocd-notifications-cm --type merge \
  -p '{"data":{"service.slack":"token: $slack-token\n"}}'

Suscribe las aplicaciones a los eventos. Para las generadas por un ApplicationSet, añade la anotación en template.metadata.annotations del ApplicationSet; si la pones directamente sobre la Application, el controlador la eliminará en la siguiente reconciliación:

  template:
    metadata:
      name: "frontend-{{.name}}"
      annotations:
        notifications.argoproj.io/subscribe.on-sync-failed.slack: alertas-despliegues
        notifications.argoproj.io/subscribe.on-health-degraded.slack: alertas-despliegues
        notifications.argoproj.io/subscribe.on-deployed.slack: despliegues

Aplica de nuevo el ApplicationSet y fuerza un despliegue para comprobar que llega el mensaje:

kubectl apply -f appset-frontend.yaml
argocd app sync frontend-staging

Si no llega nada, revisa los logs del controlador:

kubectl -n argocd logs deploy/argocd-notifications-controller --tail=50

Solución de problemas

El ApplicationSet no genera aplicaciones. Revisa la sección status.conditions con kubectl -n argocd describe applicationset frontend. Con missingkey=error, un error de plantilla aparece ahí. Con el generador clusters, confirma que las etiquetas del Secret del clúster coinciden con el selector.

argocd cluster add falla con context not found. El argumento es el nombre del contexto de kubeconfig, no el del clúster. Consúltalo con kubectl config get-contexts -o name.

Una aplicación se queda OutOfSync tras sincronizar. Algún controlador del clúster está modificando campos después de aplicarlos (réplicas gestionadas por un HPA, anotaciones añadidas por un webhook). Consulta la diferencia con argocd app diff <app> y excluye esos campos con ignoreDifferences en la plantilla.

Error application destination ... is not permitted in project. El destino no está en spec.destinations del AppProject, o el namespace no coincide con el patrón. Ajusta el proyecto en lugar de mover la aplicación a default.

Conclusión

Con esta configuración, una única instancia de Argo CD despliega la misma aplicación en todos los clústeres etiquetados, respeta el orden entre base de datos, migraciones y aplicación, limita qué puede hacer cada equipo y avisa en Slack cuando algo falla. Como siguientes pasos, conecta Argo CD a tu proveedor SSO para que los grupos del RBAC sean reales, guarda los Secrets en Git de forma segura con Sealed Secrets o External Secrets Operator, y valora Argo Rollouts si necesitas despliegues canary o blue-green.