FluxCD
Introducción
La lista de servicios utilizada por Sora Project es medianamente grande, así que requiere un poco de organización.
Me he basado en la arquitectura MonoRepo de FluxCD, la cual permite configurar todos los entornos y servicios en un único repositorio y rama, separando por carpetas. Esta define una base y la sobrescribe para cada entorno, permitiendo ahorrar mucho tiempo.
Los servicios se separan en dos partes:
- Infraestructura: Conforman la estructura base y son utilizados por el resto, también llamados servicios de plataforma. En este caso son aquellos separados por niveles.
- Servicios: Son los servicios que realmente quieres publicar y que hacen uso (directa o indirectamente) de los de plataforma.
Kubernetes no permite "bloquear" un despliegue en espera de que sus dependencias estén desplegadas, pero FluxCD sí que lo permite. Esto nos permite evitar explosiones tontas o añadir reintentos que añadiría complejidad al sistema.
Encriptación
En mi caso utilizo un repositorio público, es por ello por lo que los datos sensibles DEBEN estar encriptados.
FluxCD dispone de varios métodos, en mi caso utilizo SOPS con encriptación Age.
Normalmente se utilizaría el Vault original de la Cloud o uno desplegado, por ejemplo, el de HashiCorp. Alguna gente despliega el Vault dentro del propio clúster, pero tiene cierto peligro ya que la base de almacén está en el mismo clúster que estás desplegando, si esta falla pues ... tienes un problema 😄
Ojo con el tema, subir un fichero sin encriptar no tiene fácil solución, volverlo a subir hará que Git recuerde el original.
Inicialización
Requerimientos
Para gestionar FluxCD y los ficheros encriptados con SOPS y Age, se requieren los 3 comandos.
Este comando los instala mediante brew
, accesible en Unix:
brew install fluxcd/tap/flux sops age
Desplegar FluxCD
Para evitar problemas, es mejor confirmar que todo funcionará y dejar los servicios desplegados.
flux check --pre
flux install
flux check
Encriptación
Para simplificar, los datos sensibles se almacenan en secrets que después se asignan como un values externo. Estos suelen estar configurados a nivel de entorno.
Se debe crear el fichero de claves y registrarlo en el clúster.
age-keygen -o age.agekey && \
cat age.agekey | kubectl create secret generic sops-age -n flux-system --from-file=age.agekey=/dev/stdin && \
rm age.agekey
Para encriptar los ficheros necesitamos el Public key
que habrá impreso por pantalla, si lo perdemos, siempre podemos recuperarlo del secret.
El fichero age.agekey
permite desencriptar, así que ojo con él.
Conectar al repositorio Git
El repositorio debe ser accesible por el clúster, tener las carpetas básicas y tener un token con acceso de escritura.
La primera vez, es recomendable empezar con un repositorio "vacío", añadiendo poco a poco las carpetas o servicios siguientes, controlando que todo esté como se espera.
Recordar actualizar todos los ficheros encriptados con la clave generada previamente. Para ello se requiere el fichero sin encriptar (se detallan todos más adelante) y el siguiente comando indicando el Public key
:
sops --age=replace-me_public-key \
--encrypt --encrypted-regex '^(data|stringData)$' \
--in-place secret-file.yaml
En caso de GitLab, el token se crea directamente en el repositorio con el rol de Maintainer
y el scope de api
. Es altamente aconsejable tener uno diferente por entorno.
Este comando es para GitLab, FluxCD dispone de un bootstrap
diferente (aunque similares) para cada tipo de repositorio Git.
Ajustar según el token, owner (hace de grupo), nombre de repositorio y la carpeta padre para ese entorno. Esto conectará el clúster hacia ese repositorio y construirá sus ficheros.
export GITLAB_TOKEN="replace-me_token"
flux bootstrap gitlab --owner=reiizumi --repository=fluxcd-thor-network --path=clusters/testing --token-auth
Notas de FluxCD
Actualización manual
Los ficheros de configuración son recargados cuando se cumple el tiempo de intervalo, pero es posible lanzar una orden de actualización inmediata de todos los ficheros.
flux reconcile kustomization flux-system --with-source
Si el cambio proviene de un Helm, recargar toda la configuración no siempre funcionará (además de ser lento). En estos casos, se debe actualizar directamente el que toque.
Este ejemplo es para actualizar un OCIRepository
de un namespace concreto:
flux reconcile source oci <oci_name> -n <namespace>
Notificaciones con Slack
FluxCD permite enviar notificaciones a múltiples destinos, este proceso es para Slack.
Para utilizar las notificaciones, se requiere configurar una nueva App y el Canal al que estará asignado.
- Crear el canal desde la App
- Ir a la web y clicar en New App
- Seleccionar
From scratch
- Elegir
App Name
+Workspace
- Ir a
Incoming Webhooks
- Encender
Activate Incoming Webhooks
- Añadir
New Webhooks to Workspace
y escoger el canal - Copiar la URL para el Provider
- Encender
- Ir a
OAuth & Permissions
, enBot Token Scopes
- Añadir el
OAuth Scope
y rellenarchat:write
- Añadir el
- Ir a
OAuth & Permissions
- Utilizar el
Bot User OAuth Token
en elnotification-provider-secret.yaml
(explicado en Nivel 1) - Clicar en
Reinstall to Workspace
y elegir nuevamente el workspace
- Utilizar el
Notas rápidas
Un pequeño resumen sobre la configuración de FluxCD:
- Todas las carpetas tienen el
kustomization.yaml
. Este es quien apunta al resto de ficheros (o carpetas) y se encarga de sobrescribir al resto. Todas las carpetas tienen su fichero. - El fichero
kustomization.yaml
puede modificar valores (conpatches
) directamente, o apuntar a un ficheroyaml
que sobrescribe al original. - Para simplificar, mis ficheros tienen todos los parámetros, incluido los que cambian dependiendo del entorno. Estos se ven fácilmente ya que tienen replace-me como valor.
- Los datos sensibles están en secrets encriptados para cada entorno.
- Todos los servicios provienen de Helm charts.
- Cada servicio define el
kustomization.yaml
y namespace. el repositorio se define enrepository
conOCIRepository
siempre que esté disponible,HelmRepository
en caso contrario. Los charts se configuran en losrelease
.
Los OCIRepository
indican la versión, mientras que los HelmRepository
dejan esto para el HelmRelease
.
kustomization.yaml
Este es un ejemplo típico del fichero principal. Indica el resto de los ficheros (si no se indican, no se usan aunque existan) y añade el namespace a todos los ficheros, lo que ahorra problemas.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: cert-manager-system
resources:
- namespace.yaml
- repository.yaml
- release.yaml
Aunque este fichero indique un namespace, se debe crear su yaml para que sea creado. Además, si se llaman diferentes, el namespace cambiará su nombre según el de este fichero.
repository.yaml
Los repositorios se pueden recuperar de múltiples lugares, pero siempre intento utilizar los OCI
(que permiten indicar la versión), si no es posible, el HelmRepository (que deja la versión a ser definida en el release).
En general son similares. Indican un nombre (que es utilizado por el release), el tiempo para buscar por actualizaciones y la URL (ojo con el tipo de URL).
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
name: kube-prometheus-stack
spec:
interval: 24h
url: oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack
ref:
semver: "61.x.x"
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: cert-manager
spec:
interval: 24h
url: https://charts.jetstack.io
Los Helm charts definen las versiones según semver
. Estos ficheros actualizan automáticamente al detectar versiones superiores cuando esta versión no se indica (siempre actualiza a la última) o aparece una superior dentro de la limitación indicada. Para ello, cualquier valor de versión con un x
o no definida, se considera que permite cualquier actualización.
Ojo con las actualizaciones automáticas, ya que puede recuperar una versión incompatible con la anterior. Semver
funciona con x.y.z
, donde x
significa que el cambio es incompatible con el anterior (requiere un proceso manual), pero mucha gente no sigue esta estándar. Una actualización de y
o z
puede incorporar incompatibilidades que bloqueen el servicio. Nada se puede hacer contra ello ... 🔥
release.yaml
Los HelmRelease tienen una base similar al resto. Indican el chart (OCI o Helm), el intervalo para detectar cambios en ese fichero puede tener datos específicos (como la gestión de CRDs que Helm no dispone) o los reintentos y los values a definir.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-prom
spec:
chartRef:
kind: OCIRepository
name: kube-prometheus-stack
install:
crds: Create
upgrade:
crds: CreateReplace
driftDetection:
mode: enabled
ignore:
# Ignore "validated" annotation which is not inserted during install
- paths: [ "/metadata/annotations/prometheus-operator-validated" ]
target:
kind: PrometheusRule
interval: 24h
values:
prometheus:
prometheusSpec:
retention: replace-me
retentionSize: replace-me
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: replace-me
resources:
requests:
cpu: 100m
memory: 768Mi
limits:
cpu: 500m
memory: 1Gi
alertmanager:
enabled: false
grafana:
enabled: false
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
spec:
chart:
spec:
chart: cert-manager
version: '1.*.*'
sourceRef:
kind: HelmRepository
name: cert-manager
interval: 24h
install:
remediation:
retries: 1
values:
installCRDs: true