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:

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:

sudo bash install-flux.sh

Comprueba la versión instalada:

flux version --client
flux: v2.9.5

Opcionalmente, activa el autocompletado para 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:

flux check --pre
► 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:

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:

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:

► 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:

flux check
flux get kustomizations
NAME            REVISION                SUSPENDED       READY   MESSAGE
flux-system     main@sha1:3f1c2a9b      False           True    Applied revision: main@sha1:3f1c2a9b

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:

git clone https://github.com/$GITHUB_USER/fleet-infra
cd fleet-infra

La estructura inicial es esta:

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, 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:

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:

---
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:

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:

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:

flux get kustomizations --watch
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:

kubectl -n default get deployments,services
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   <none>        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:

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:

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:

---
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:

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:

flux get helmreleases -n flux-system
kubectl -n podinfo-helm get pods
NAME     REVISION  SUSPENDED  READY  MESSAGE
podinfo  6.15.0     False      True   Helm install succeeded for release podinfo-helm/podinfo.v1 with chart [email protected]

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:

kubectl -n flux-system create secret generic discord-webhook \
  --from-literal=address=your_discord_webhook_url

Crea el archivo de notificaciones:

nano clusters/my-cluster/notifications.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:

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:

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):

flux reconcile kustomization podinfo
kubectl -n default get deployment podinfo
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:

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:

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/<nombre>, y dentro de clusters/<nombre> defines Kustomization que apuntan a ./infrastructure y a ./apps/<entorno>. Con el campo dependsOn puedes hacer que las aplicaciones esperen a que la infraestructura esté lista:

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:

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:

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:

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.