--- title: "Cómo implementar GitOps en Kubernetes con Flux CD" description: "Instala Flux CD en Kubernetes, conéctalo a un repositorio de GitHub y despliega aplicaciones con Kustomize y Helm que se sincronizan solas desde Git." category: "CI/CD DevOps" language: es source: https://cubepath.com/es/docs/ci-cd-devops/flujo-de-trabajo-gitops-con-flux-cd updated: 2026-09-25 --- # Cómo implementar GitOps en Kubernetes con Flux CD Flux CD es un operador de GitOps para Kubernetes: unos controladores dentro del clúster leen un repositorio Git cada pocos minutos y aplican lo que encuentran, de modo que Git pasa a ser la única fuente de verdad y cualquier cambio manual en el clúster se revierte. En este tutorial instalarás la CLI de Flux en Ubuntu 24.04, harás el bootstrap de Flux contra un repositorio de GitHub, desplegarás una aplicación con una `Kustomization`, otra con una `HelmRelease` y configurarás avisos de errores en Discord. ## Requisitos previos Para seguir esta guía necesitas: - Una máquina con Ubuntu 24.04 desde la que administras el clúster (tu equipo o un VPS de CubePath), con `git` y `kubectl` instalados. - Un clúster de Kubernetes en una versión soportada, con acceso de administrador desde `kubectl`. Comprueba el acceso con `kubectl get nodes`. - Una cuenta de GitHub y un token personal. Un token clásico con el scope `repo`, o uno de grano fino con permisos de lectura y escritura en **Administration** y **Contents**, sirve para que Flux cree el repositorio y suba su configuración. ## Paso 1: Instalar la CLI de Flux La CLI `flux` se usa para hacer el bootstrap, crear manifiestos y consultar el estado de la sincronización. El proyecto publica un script de instalación que descarga el binario de la última versión en `/usr/local/bin`. Descárgalo y revísalo antes de ejecutarlo: ```bash curl -fsSL -o install-flux.sh https://fluxcd.io/install.sh less install-flux.sh ``` Ejecútalo con `sudo` para que pueda escribir en `/usr/local/bin`: ```bash sudo bash install-flux.sh ``` Comprueba la versión instalada: ```bash flux version --client ``` ```output flux: v2.9.5 ``` Opcionalmente, activa el autocompletado para Bash: ```bash echo '. <(flux completion bash)' >> ~/.bashrc source ~/.bashrc ``` ## Paso 2: Comprobar que el clúster es compatible Antes de instalar nada, Flux puede verificar que la versión de Kubernetes y tu `kubectl` cumplen sus requisitos: ```bash flux check --pre ``` ```output ► checking prerequisites ✔ Kubernetes 1.34.1 >=1.32.0-0 ✔ prerequisites checks passed ``` Si la comprobación falla por la versión de Kubernetes, actualiza el clúster antes de seguir: Flux solo da soporte a las versiones de Kubernetes que no han llegado a fin de vida. ## Paso 3: Hacer el bootstrap de Flux con GitHub El comando `flux bootstrap github` crea el repositorio si no existe, sube a él los manifiestos de los controladores de Flux, los instala en el namespace `flux-system` y configura una clave de despliegue SSH para que el clúster pueda leer el repositorio. A partir de ese momento Flux se gestiona a sí mismo desde Git. Exporta tu token y tu usuario de GitHub en la sesión actual. Sustituye `your_github_token` y `your_github_user` por tus valores: ```bash export GITHUB_TOKEN=your_github_token export GITHUB_USER=your_github_user ``` Lanza el bootstrap. `--path` es el directorio del repositorio que Flux sincronizará para este clúster, y `--personal` indica que el repositorio pertenece a tu usuario y no a una organización: ```bash flux bootstrap github \ --owner="$GITHUB_USER" \ --repository=fleet-infra \ --branch=main \ --path=./clusters/my-cluster \ --personal ``` El proceso tarda uno o dos minutos. Las últimas líneas deberían indicar que todos los componentes están sanos: ```output ► confirming components are healthy ✔ helm-controller: deployment ready ✔ kustomize-controller: deployment ready ✔ notification-controller: deployment ready ✔ source-controller: deployment ready ✔ all components are healthy ``` Verifica el estado de la instalación y de la primera sincronización: ```bash flux check flux get kustomizations ``` ```output NAME REVISION SUSPENDED READY MESSAGE flux-system main@sha1:3f1c2a9b False True Applied revision: main@sha1:3f1c2a9b ``` > Nota: el token solo se usa durante el bootstrap. Flux accede al repositorio con la clave SSH de solo lectura que ha registrado como deploy key, así que puedes cerrar la sesión o borrar la variable `GITHUB_TOKEN` después. ## Paso 4: Clonar el repositorio de configuración Desde ahora, los cambios en el clúster se hacen con commits, no con `kubectl apply`. Clona el repositorio que ha creado el bootstrap: ```bash git clone https://github.com/$GITHUB_USER/fleet-infra cd fleet-infra ``` La estructura inicial es esta: ```text fleet-infra/ clusters/ my-cluster/ flux-system/ gotk-components.yaml gotk-sync.yaml kustomization.yaml ``` `gotk-components.yaml` contiene los controladores de Flux y `gotk-sync.yaml` define el `GitRepository` y la `Kustomization` llamados `flux-system` que sincronizan todo el directorio `clusters/my-cluster`. Cualquier manifiesto que añadas a ese directorio se aplicará en el clúster. ## Paso 5: Desplegar una aplicación desde otro repositorio Git Vas a desplegar [podinfo](https://github.com/stefanprodan/podinfo), una aplicación web de ejemplo cuyo repositorio incluye manifiestos de Kustomize en el directorio `kustomize`. Primero crea una fuente `GitRepository` que apunte a ese repositorio. La opción `--export` hace que `flux create` genere el YAML en lugar de aplicarlo directamente: ```bash flux create source git podinfo \ --url=https://github.com/stefanprodan/podinfo \ --branch=master \ --interval=1m \ --export > ./clusters/my-cluster/podinfo-source.yaml ``` El archivo generado tiene este aspecto: ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository metadata: name: podinfo namespace: flux-system spec: interval: 1m0s ref: branch: master url: https://github.com/stefanprodan/podinfo ``` Ahora crea una `Kustomization` que aplique el directorio `./kustomize` de esa fuente en el namespace `default`. Con `--prune=true`, Flux borra del clúster los objetos que desaparezcan de Git, y con `--wait=true` espera a que los recursos estén listos antes de marcar la reconciliación como correcta: ```bash flux create kustomization podinfo \ --target-namespace=default \ --source=podinfo \ --path="./kustomize" \ --prune=true \ --wait=true \ --interval=30m \ --retry-interval=2m \ --health-check-timeout=3m \ --export > ./clusters/my-cluster/podinfo-kustomization.yaml ``` Sube los dos archivos al repositorio: ```bash git add clusters/my-cluster/podinfo-source.yaml clusters/my-cluster/podinfo-kustomization.yaml git commit -m "Add podinfo" git push ``` Flux detecta el commit en el siguiente intervalo (un minuto como máximo). Sigue la reconciliación en directo y pulsa `Ctrl+C` cuando `podinfo` aparezca como `True`: ```bash flux get kustomizations --watch ``` ```output NAME REVISION SUSPENDED READY MESSAGE flux-system main@sha1:8d4e7c21 False True Applied revision: main@sha1:8d4e7c21 podinfo master@sha1:b2f1a0e3 False True Applied revision: master@sha1:b2f1a0e3 ``` Comprueba que la aplicación está desplegada: ```bash kubectl -n default get deployments,services ``` ```output NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/podinfo 2/2 2 2 58s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/podinfo ClusterIP 10.43.118.204 9898/TCP,9999/TCP 58s ``` Si no quieres esperar al intervalo, fuerza la sincronización del repositorio y de todo lo que depende de él: ```bash flux reconcile kustomization flux-system --with-source ``` ## Paso 6: Desplegar un chart de Helm con HelmRelease Para aplicaciones distribuidas como chart de Helm, Flux usa dos objetos: un `HelmRepository` que indica de dónde descargar el chart y una `HelmRelease` que describe la versión y los valores. El `helm-controller` hace el `helm install` o `helm upgrade` por ti cada vez que cambian. Crea el archivo del repositorio de charts: ```bash nano clusters/my-cluster/podinfo-helm.yaml ``` Añade los dos objetos. La versión `6.x` permite que Flux actualice automáticamente a cualquier versión menor o de parche de la serie 6, y `install.createNamespace` crea el namespace de destino si no existe: ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: HelmRepository metadata: name: podinfo namespace: flux-system spec: interval: 1h url: https://stefanprodan.github.io/podinfo --- apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: podinfo namespace: flux-system spec: interval: 10m releaseName: podinfo targetNamespace: podinfo-helm install: createNamespace: true chart: spec: chart: podinfo version: "6.x" sourceRef: kind: HelmRepository name: podinfo values: replicaCount: 2 ui: message: "Desplegado con Flux" ``` Confirma el cambio y súbelo: ```bash git add clusters/my-cluster/podinfo-helm.yaml git commit -m "Add podinfo HelmRelease" git push flux reconcile kustomization flux-system --with-source ``` Comprueba el estado de la release y los pods: ```bash flux get helmreleases -n flux-system kubectl -n podinfo-helm get pods ``` ```output NAME REVISION SUSPENDED READY MESSAGE podinfo 6.15.0 False True Helm install succeeded for release podinfo-helm/podinfo.v1 with chart podinfo@6.15.0 NAME READY STATUS RESTARTS AGE podinfo-6c9f8b7d5f-4xk2p 1/1 Running 0 40s podinfo-6c9f8b7d5f-q8m7z 1/1 Running 0 40s ``` Para cambiar un valor, por ejemplo el número de réplicas, edita `values` en el archivo, haz commit y push. Flux ejecutará el `helm upgrade` correspondiente. ## Paso 7: Recibir avisos de errores en Discord El `notification-controller` envía eventos de Flux a servicios externos. Un `Provider` define el destino y una `Alert` qué eventos se envían. Aquí se usa un webhook de Discord; Slack, Microsoft Teams y otros se configuran igual cambiando el `type`. Crea en Discord un webhook para el canal deseado (**Editar canal**, **Integraciones**, **Webhooks**) y guarda su URL en un secret del clúster. El secret no se sube a Git para no exponer la URL. Sustituye `your_discord_webhook_url` por la tuya: ```bash kubectl -n flux-system create secret generic discord-webhook \ --from-literal=address=your_discord_webhook_url ``` Crea el archivo de notificaciones: ```bash nano clusters/my-cluster/notifications.yaml ``` ```yaml --- apiVersion: notification.toolkit.fluxcd.io/v1beta3 kind: Provider metadata: name: discord namespace: flux-system spec: type: discord secretRef: name: discord-webhook --- apiVersion: notification.toolkit.fluxcd.io/v1beta3 kind: Alert metadata: name: flux-errors namespace: flux-system spec: providerRef: name: discord eventSeverity: error eventSources: - kind: GitRepository name: "*" - kind: Kustomization name: "*" - kind: HelmRelease name: "*" ``` Súbelo al repositorio y comprueba que ambos objetos están listos: ```bash git add clusters/my-cluster/notifications.yaml git commit -m "Add Discord alerts" git push flux reconcile kustomization flux-system --with-source flux get alert-providers flux get alerts ``` A partir de ahora, cualquier reconciliación fallida (un YAML inválido, una imagen que no arranca, un chart que no existe) llegará al canal de Discord con el nombre del objeto y el error. ## Paso 8: Comprobar que Flux corrige los cambios manuales La ventaja principal de GitOps es que el estado del clúster no se desvía de Git. Para verlo, borra el deployment de podinfo a mano: ```bash kubectl -n default delete deployment podinfo ``` Fuerza la reconciliación de la `Kustomization` (sin forzarla, Flux lo haría en el siguiente intervalo de 30 minutos): ```bash flux reconcile kustomization podinfo kubectl -n default get deployment podinfo ``` ```output NAME READY UP-TO-DATE AVAILABLE AGE podinfo 2/2 2 2 12s ``` El deployment ha vuelto a crearse a partir de lo que hay en Git. Si necesitas hacer un cambio manual temporal, por ejemplo durante una incidencia, suspende antes la sincronización y reanúdala al terminar: ```bash flux suspend kustomization podinfo flux resume kustomization podinfo ``` ## Organizar el repositorio para varios clústeres Cuando gestionas más de un clúster, lo habitual es separar lo común de lo específico de cada entorno. Una estructura sencilla que funciona bien con Flux: ```text fleet-infra/ apps/ base/ # manifiestos comunes de las aplicaciones staging/ # parches de Kustomize para staging production/ # parches de Kustomize para producción infrastructure/ # ingress, cert-manager, repositorios de Helm clusters/ staging/ # bootstrap de Flux y Kustomizations del clúster de staging production/ # bootstrap de Flux y Kustomizations del clúster de producción ``` Cada clúster se inicializa con su propio `flux bootstrap github --path=./clusters/`, y dentro de `clusters/` defines `Kustomization` que apuntan a `./infrastructure` y a `./apps/`. Con el campo `dependsOn` puedes hacer que las aplicaciones esperen a que la infraestructura esté lista: ```yaml apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: apps namespace: flux-system spec: interval: 10m dependsOn: - name: infrastructure sourceRef: kind: GitRepository name: flux-system path: ./apps/production prune: true ``` ## Solución de problemas **Una Kustomization se queda en `False`.** Lee el mensaje completo y los eventos del objeto: ```bash flux get kustomizations kubectl -n flux-system describe kustomization podinfo ``` Los errores más habituales son un YAML mal formado, un `path` que no existe en el repositorio o un recurso que no llega a estar listo antes de `--health-check-timeout`. **Los cambios de Git no llegan al clúster.** Comprueba que la fuente ha descargado el último commit y fuerza su actualización: ```bash flux get sources git flux reconcile source git flux-system ``` Si el `GitRepository` muestra un error de autenticación, la deploy key puede haberse borrado en GitHub. Vuelve a ejecutar el mismo comando `flux bootstrap github`, que es idempotente y la regenera. **Ver los logs de los controladores.** `flux logs` agrega los logs de todos los controladores de Flux y permite filtrar por nivel: ```bash flux logs --level=error --all-namespaces ``` ## Conclusión Tienes Flux CD gestionando el clúster desde un repositorio de GitHub: los controladores se actualizan a sí mismos desde Git, las aplicaciones se despliegan con `Kustomization` y `HelmRelease`, los errores llegan a Discord y cualquier cambio manual se revierte. Como siguientes pasos puedes cifrar los secretos que guardes en Git con SOPS y la integración de descifrado de Flux, activar la automatización de imágenes (`--components-extra=image-reflector-controller,image-automation-controller` en el bootstrap) para que Flux actualice las etiquetas de imagen por ti, o añadir un segundo clúster siguiendo la estructura por entornos descrita arriba.