Módulo 5: Gitops With Argocd

4. Manos a la obra: instalando ArgoCD

Descripción

Esta lección instala el "termostato" completo: ArgoCD, corriendo dentro de andes-cargo-cluster, con el manifiesto oficial del proyecto — el mismo tipo de instalación de un solo kubectl apply que ya usaste con OPA Gatekeeper... salvo que esta vez ese único comando falla, con un error real que ninguna guía anterior de este ecosistema encontró: una anotación demasiado grande para el límite que Kubernetes le pone a cualquier objeto. Esta lección documenta el error completo, la corrección de una sola bandera que lo resuelve, y termina con argocd version confirmando la misma versión —v3.5.1— tanto en el cliente que instalas en tu máquina como en el servidor que corre dentro del clúster.

Conexión con el módulo

Esta lección es la contraparte de la lección 3: ahí instalaste la "libreta" (Gitea); aquí instalas quien la lee. La lección 5 explica la anatomía del recurso Application que vas a usar para conectar ambas piezas por primera vez en la lección 6.


Paso 1 — Instala el CLI de argocd en tu máquina

Todo lo que sigue en esta lección corre contra el clúster (kubectl apply), pero vas a necesitar también el CLI de argocd en tu propia máquina para el resto del módulo —argocd login, argocd app get, argocd app sync—. En macOS, con Homebrew:

brew install argocd
argocd version --client

Qué esperar (literal, ejecutado — GoVersion, BuildDate y Platform dependen de tu sistema y de cuándo se publicó el binario que instalaste):

argocd: v3.5.1+109ca7c.dirty
  BuildDate: 2026-08-12T14:32:28Z
  GitCommit: 109ca7ca71139e514114499d294a492e7910a965
  GitTreeState: dirty
  GitTag: v3.5.1
  GoVersion: go1.26.5
  Compiler: gc
  Platform: darwin/arm64

v3.5.1 — confirma que instalaste una versión igual o más reciente que la que este módulo documenta antes de seguir. Si tu sistema operativo no es macOS, la página de instalación oficial documenta el binario equivalente para Linux y Windows.


Paso 2 — El manifiesto oficial, y el error real que produce un kubectl apply sin más

El proyecto ArgoCD publica un manifiesto de instalación completo, listo para aplicar contra cualquier clúster de Kubernetes:

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

Qué pasó, literal, la primera vez que se ejecutó este comando para esta lección —decenas de recursos se crean sin problema, y el comando termina con un error:

namespace/argocd created
configmap/argocd-gpg-keys-cm created
configmap/argocd-notifications-cm created
configmap/argocd-rbac-cm created
...
networkpolicy.networking.k8s.io/argocd-server-network-policy created
The CustomResourceDefinition "applicationsets.argoproj.io" is invalid: metadata.annotations: Too long: may not be more than 262144 bytes

El mensaje es preciso, y la causa no tiene nada que ver con ArgoCD en sí: kubectl apply (a diferencia de kubectl create) guarda, por defecto, una copia completa del manifiesto aplicado en la anotación kubectl.kubernetes.io/last-applied-configuration de cada objeto —el mecanismo que le permite calcular diferencias en la próxima aplicación—. El CRD applicationsets.argoproj.io es, por sí solo, un documento YAML enorme (define un esquema completo de validación para el recurso ApplicationSet), y esa copia completa, codificada dentro de una sola anotación, supera el límite duro de 262144 bytes (256 KiB) que Kubernetes impone a cualquier anotación de cualquier objeto —un límite de etcd, no específico de ArgoCD.


Paso 3 — La corrección: --server-side

La solución no es partir el manifiesto ni reducirlo — es cambiar el modo de aplicación. kubectl apply --server-side usa Server-Side Apply, una función de Kubernetes donde el propio kube-apiserver calcula las diferencias de campo, en vez de que el cliente (kubectl) le mande una copia completa del manifiesto como anotación. Sin esa copia completa embebida como anotación, el límite de 262144 bytes deja de ser un problema:

kubectl apply -n argocd --server-side --force-conflicts \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Qué esperar (literal, ejecutado — completo, sin ningún error esta vez):

customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/applicationsets.argoproj.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io serverside-applied
serviceaccount/argocd-application-controller serverside-applied
serviceaccount/argocd-applicationset-controller serverside-applied
serviceaccount/argocd-dex-server serverside-applied
serviceaccount/argocd-notifications-controller serverside-applied
serviceaccount/argocd-redis serverside-applied
serviceaccount/argocd-repo-server serverside-applied
serviceaccount/argocd-server serverside-applied
role.rbac.authorization.k8s.io/argocd-application-controller serverside-applied
role.rbac.authorization.k8s.io/argocd-applicationset-controller serverside-applied
role.rbac.authorization.k8s.io/argocd-dex-server serverside-applied
role.rbac.authorization.k8s.io/argocd-notifications-controller serverside-applied
role.rbac.authorization.k8s.io/argocd-redis serverside-applied
role.rbac.authorization.k8s.io/argocd-server serverside-applied
clusterrole.rbac.authorization.k8s.io/argocd-application-controller serverside-applied
clusterrole.rbac.authorization.k8s.io/argocd-applicationset-controller serverside-applied
clusterrole.rbac.authorization.k8s.io/argocd-server serverside-applied
rolebinding.rbac.authorization.k8s.io/argocd-application-controller serverside-applied
rolebinding.rbac.authorization.k8s.io/argocd-applicationset-controller serverside-applied
rolebinding.rbac.authorization.k8s.io/argocd-dex-server serverside-applied
rolebinding.rbac.authorization.k8s.io/argocd-notifications-controller serverside-applied
rolebinding.rbac.authorization.k8s.io/argocd-redis serverside-applied
rolebinding.rbac.authorization.k8s.io/argocd-server serverside-applied
clusterrolebinding.rbac.authorization.k8s.io/argocd-application-controller serverside-applied
clusterrolebinding.rbac.authorization.k8s.io/argocd-applicationset-controller serverside-applied
clusterrolebinding.rbac.authorization.k8s.io/argocd-server serverside-applied
configmap/argocd-cm serverside-applied
configmap/argocd-cmd-params-cm serverside-applied
configmap/argocd-gpg-keys-cm serverside-applied
configmap/argocd-notifications-cm serverside-applied
configmap/argocd-rbac-cm serverside-applied
configmap/argocd-ssh-known-hosts-cm serverside-applied
configmap/argocd-tls-certs-cm serverside-applied
secret/argocd-notifications-secret serverside-applied
secret/argocd-secret serverside-applied
service/argocd-applicationset-controller serverside-applied
service/argocd-dex-server serverside-applied
service/argocd-metrics serverside-applied
service/argocd-notifications-controller-metrics serverside-applied
service/argocd-redis serverside-applied
service/argocd-repo-server serverside-applied
service/argocd-server serverside-applied
service/argocd-server-metrics serverside-applied
deployment.apps/argocd-applicationset-controller serverside-applied
deployment.apps/argocd-dex-server serverside-applied
deployment.apps/argocd-notifications-controller serverside-applied
deployment.apps/argocd-redis serverside-applied
deployment.apps/argocd-repo-server serverside-applied
deployment.apps/argocd-server serverside-applied
statefulset.apps/argocd-application-controller serverside-applied
networkpolicy.networking.k8s.io/argocd-application-controller-network-policy serverside-applied
networkpolicy.networking.k8s.io/argocd-applicationset-controller-network-policy serverside-applied
networkpolicy.networking.k8s.io/argocd-dex-server-network-policy serverside-applied
networkpolicy.networking.k8s.io/argocd-notifications-controller-network-policy serverside-applied
networkpolicy.networking.k8s.io/argocd-redis-network-policy serverside-applied
networkpolicy.networking.k8s.io/argocd-repo-server-network-policy serverside-applied
networkpolicy.networking.k8s.io/argocd-server-network-policy serverside-applied

--force-conflicts es necesario porque, si corriste el intento fallido del Paso 2 primero, algunos objetos ya tienen un "administrador de campo" (field manager) asignado por el kubectl apply normal — la bandera le dice a Server-Side Apply que tome posesión de esos campos de todas formas, en vez de rechazar el cambio por conflicto. Fíjate en algo interesante del propio manifiesto: ya trae sus propios objetos NetworkPolicy para cada componente de ArgoCD (argocd-server-network-policy, argocd-repo-server-network-policy, etc.) — el mismo mecanismo que construiste a mano en el Módulo 4, aquí aplicado por el propio proyecto para aislar sus componentes internos entre sí.


Paso 4 — Espera a que todos los Pods arranquen

kubectl wait --for=condition=Available deployment --all -n argocd --timeout=180s
kubectl get pods -n argocd

Qué esperar (literal, ejecutado — el sufijo hash de cada Pod es tu valor variable; los nombres base y la cuenta de siete Pods son literales):

deployment.apps/argocd-applicationset-controller condition met
deployment.apps/argocd-dex-server condition met
deployment.apps/argocd-notifications-controller condition met
deployment.apps/argocd-redis condition met
deployment.apps/argocd-repo-server condition met
deployment.apps/argocd-server condition met

NAME                                                READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                     1/1     Running   0          43s
argocd-applicationset-controller-579c4c54b8-6rjfl   1/1     Running   0          43s
argocd-dex-server-744c5d4467-6mvzj                  1/1     Running   0          43s
argocd-notifications-controller-dd4ff84c-t5sdn      1/1     Running   0          43s
argocd-redis-84497fb7c5-9ll4x                       1/1     Running   0          43s
argocd-repo-server-5f46d9f598-5fqmj                 1/1     Running   0          43s
argocd-server-7f4549bb69-nnxl5                      1/1     Running   0          43s

Nota que argocd-application-controller corre como StatefulSet (el -0 en vez de un sufijo hash) — es el único componente de los siete con estado propio que preservar (el caché de comparación entre Git y el clúster), a diferencia de los otros seis, que son Deployment sin estado.


Paso 5 — La contraseña inicial, sin necesitar ningún túnel

ArgoCD genera una contraseña de administrador aleatoria en su primer arranque y la guarda como un Secret de Kubernetes — nunca en texto plano en ningún manifiesto. El CLI la lee directamente vía la API de Kubernetes, sin necesitar que la UI web esté siquiera accesible todavía:

argocd admin initial-password -n argocd --core

Qué esperar (literal, ejecutado — la contraseña generada es tu valor variable, distinta en cada instalación):

f0gLUztVzFzjD82q

 This password must be only used for first time login. We strongly recommend you update the password using `argocd account update-password`.

La bandera --core es lo que hace posible este paso sin ningún port-forward: le dice al CLI que hable directamente con la API de Kubernetes (usando tu ~/.kube/config, el mismo que usa kubectl) en vez de con el servidor HTTP de ArgoCD. Es el mismo mecanismo, en espíritu, que kubectl get secret ... -o jsonpath — una forma alternativa de llegar al mismo Secret.


Paso 6 — Accede a la UI vía port-forward, y confirma la versión completa

kubectl port-forward -n argocd svc/argocd-server 8080:443

En otra terminal:

argocd login localhost:8080 --username admin --password 'f0gLUztVzFzjD82q' --insecure

Qué esperar (literal, ejecutado):

'admin:login' logged in successfully
Context 'localhost:8080' updated

--insecure es necesario porque el certificado TLS que argocd-server genera para sí mismo en la instalación es autofirmado —el mismo tipo de certificado que ya viste con los admission webhooks de Gatekeeper, mencionado en el diseño de este ecosistema—; no hay ninguna autoridad certificadora real detrás en este laboratorio local. En un EKS real (Módulo 7), este paso se reemplaza por un certificado válido, normalmente emitido por cert-manager contra un dominio real.

argocd version

Qué esperar (literal, ejecutado — confirma que cliente y servidor coinciden en la misma versión):

argocd: v3.5.1+109ca7c.dirty
  BuildDate: 2026-08-12T14:32:28Z
  GitCommit: 109ca7ca71139e514114499d294a492e7910a965
  GitTreeState: dirty
  GitTag: v3.5.1
  GoVersion: go1.26.5
  Compiler: gc
  Platform: darwin/arm64
argocd-server: v3.5.1
  BuildDate: 2026-08-12T11:28:06Z
  GitCommit: 109ca7ca71139e514114499d294a492e7910a965
  GitTreeState: clean
  GitTag: v3.5.1
  GoVersion: go1.26.4
  Compiler: gc
  Platform: linux/arm64
  Kustomize Version: v5.8.1 2026-02-09T16:15:27Z
  Helm Version: v4.2.1+gd591a19
  Kubectl Version: v0.36.1
  Jsonnet Version: v0.22.0

argocd-server: v3.5.1 — la versión que corre dentro del clúster, en linux/arm64 (la arquitectura del nodo kind), frente a argocd: v3.5.1 — el CLI que instalaste en tu darwin/arm64 (o la plataforma de tu propia máquina). Que ambas coincidan en v3.5.1 no es casualidad de este laboratorio: es una práctica recomendada real —un CLI mucho más nuevo o más viejo que el servidor puede tener comandos o formatos de salida que no coinciden—, y el manifiesto oficial que aplicaste en el Paso 3 siempre instala la última versión estable, la misma que brew install argocd te dio en el Paso 1.

También trae, de regalo, algo que vas a necesitar en las lecciones 5 y 8: argocd-server embebe su propia copia de Kustomize (v5.8.1) y Helm (v4.2.1) — ArgoCD puede sincronizar aplicaciones que usan cualquiera de las dos herramientas de composición de YAML, no solo manifiestos planos como los de andes-cargo-k8s. Esta guía usa manifiestos planos a propósito, por simplicidad pedagógica, pero vale la pena saber que la opción existe.


El resumen visual: las dos mitades, ya instaladas

                    andes-cargo-cluster (kind)

  namespace: gitea                    namespace: argocd
  ┌─────────────────────┐            ┌──────────────────────────┐
  │  gitea (Pod)          │            │  argocd-server               │
  │  └─ andes-cargo/       │◄───────────│  argocd-repo-server           │
  │     andes-cargo-k8s    │  (todavía  │  argocd-application-controller│
  │     (commit 45b14f6)   │   sin      │  argocd-applicationset-       │
  └─────────────────────┘   conectar)  │    controller                 │
                                        │  argocd-dex-server             │
  namespace: andes-cargo                │  argocd-redis                  │
  ┌─────────────────────┐            │  argocd-notifications-        │
  │  andes-cargo-status-api│            │    controller                 │
  │  (3 réplicas, M1-M4)   │            └──────────────────────────┘
  └─────────────────────┘

  Las tres piezas existen. Ninguna sabe todavía de las otras dos —
  eso es, exactamente, lo que la lección 6 conecta.

Errores comunes

Correr kubectl apply sin --server-side y no reconocer el error como "de tamaño de anotación" (el error real de esta lección). Qué pasa: alguien ve Too long: may not be more than 262144 bytes y asume que el manifiesto está corrupto o que descargó un archivo incompleto. Cómo detectarlo: el mensaje exacto menciona metadata.annotations — es un límite de tamaño de anotación, no un problema de sintaxis YAML ni de red. Cómo corregirlo: agrega --server-side --force-conflicts al mismo comando, como hizo el Paso 3 — no hace falta partir el manifiesto ni descargarlo de nuevo.

Usar argocd login sin --insecure contra este laboratorio (de expectativa, TLS). Qué pasa: el CLI rechaza la conexión con un error de certificado no confiable. Cómo detectarlo: el mensaje menciona certificate signed by unknown authority o similar. Cómo corregirlo: --insecure es correcto y esperado contra el certificado autofirmado de este laboratorio local — en un EKS real con un certificado válido de por medio, la bandera dejaría de ser necesaria (y de recomendarse).

Confundir la contraseña de argocd admin initial-password con una contraseña permanente (de higiene de credenciales, el propio CLI ya lo advierte). Qué pasa: alguien sigue usando la contraseña generada automáticamente indefinidamente, incluso en un clúster que va a vivir más allá de este laboratorio. Cómo detectarlo: el propio mensaje del Paso 5 lo dice explícitamente: "This password must be only used for first time login." Cómo corregirlo: para este laboratorio de aprendizaje, no hace falta cambiarla —el clúster es efímero y local—; en cualquier instalación real, el siguiente comando después del primer login sería argocd account update-password.


Ejercicios

Ejercicio 1 — Explica el error de --server-side a un colega que nunca lo vio. En dos o tres frases, sin copiar el texto de esta lección, explica por qué kubectl apply normal falla con el CRD de ApplicationSet y por qué --server-side lo resuelve.

Ver solución

Una explicación razonable: "kubectl apply normal guarda una copia completa del manifiesto que acabas de aplicar dentro de una anotación del propio objeto, para poder calcular diferencias la próxima vez. El CRD de ApplicationSet es un documento YAML tan grande que esa copia completa supera el límite de 256 KiB que Kubernetes le pone a cualquier anotación. --server-side cambia el mecanismo: en vez de que el cliente mande una copia completa, el servidor de Kubernetes calcula las diferencias de campo directamente — sin esa copia completa embebida, el límite de tamaño deja de ser un problema."

Ejercicio 2 — Confirma que entiendes qué es --core. Sin volver a leer el Paso 5, explica la diferencia entre argocd admin initial-password -n argocd --core y argocd login — ¿por qué el primero no necesita ningún port-forward activo?

Ver solución

argocd admin initial-password --core habla directamente con la API de Kubernetes (el mismo ~/.kube/config que usa kubectl), leyendo el Secret argocd-initial-admin-secret como cualquier otro objeto de Kubernetes — no necesita que el servidor HTTP de ArgoCD (argocd-server) sea alcanzable desde tu máquina en absoluto. argocd login, en cambio, sí habla con el servidor HTTP/gRPC de ArgoCD (argocd-server) — por eso necesita el port-forward del Paso 6, o cualquier otro camino de red que llegue hasta ese Service.

Ejercicio 3 — Predice qué versión reportaría argocd version si actualizaras solo el CLI de tu máquina. Si corrieras brew upgrade argocd mañana y el proyecto ya hubiera publicado v3.6.0, sin tocar nada dentro del clúster, ¿qué mostraría argocd version?

Ver solución

Mostraría argocd: v3.6.0 (el CLI actualizado en tu máquina) junto a argocd-server: v3.5.1 (la versión que sigue corriendo dentro del clúster, sin cambios, porque brew upgrade solo toca el binario local). Sería un desajuste de versiones entre cliente y servidor — el tipo exacto de situación que la nota del Paso 6 advierte evitar en un entorno real, aunque el CLI probablemente siga funcionando para las operaciones básicas de este módulo.


Resumen y siguiente paso

Esta lección instaló ArgoCD v3.5.1 completo dentro de andes-cargo-cluster, con el manifiesto oficial del proyecto — y documentó, con el error literal, por qué un kubectl apply sin más falla contra el CRD de ApplicationSet (el límite de 256 KiB de cualquier anotación de Kubernetes) y por qué --server-side --force-conflicts lo resuelve sin partir ningún manifiesto. Confirmaste acceso a la UI vía port-forward, obtuviste la contraseña inicial sin necesitar ningún túnel (--core), y verificaste que cliente y servidor coinciden en la misma versión.

Antes de avanzar deberías poder: explicar el error de tamaño de anotación sin ayuda; distinguir --core de un login normal contra el servidor HTTP; y confirmar, en tu propia terminal, que argocd version reporta v3.5.1 en ambos lados.

Siguiente lección: anatomía de una Application de ArgoCD. Ahí conoces, en detalle, el único recurso que falta para conectar las dos piezas que instalaste en esta lección y la anterior — source, destination y syncPolicy, con la diferencia exacta entre sincronización manual y automática.

Recursos

  1. Argo CD — Getting Started — documentación oficial de instalación, fuente del manifiesto usado en esta lección.
  2. Argo CD — CLI Installation — instalación del CLI para distintos sistemas operativos.
  3. Kubernetes — Server-Side Apply — documentación oficial del mecanismo que resuelve el error del Paso 2 de esta lección.
  4. Kubernetes — Annotations — el límite de tamaño de 256 KiB por anotación, citado en el error de esta lección.