01 - Cluster kind piloté en GitOps par Flux
Je te propose de monter un homelab Kubernetes sur ton poste : jetable, reproductible, et entièrement décrit dans Git. Tu casses tout ? kind delete cluster, un bootstrap, et Flux remet le cluster dans l'état décrit par le dépôt.
C'est le premier épisode d'une série. On pose ici les fondations (le cluster, Flux, la structure du dépôt et une première app) avant d'ajouter la Gateway API dans l'épisode 02.
Les choix de stack¶
Flux ou Argo CD ?¶
- Argo CD : son interface web montre l'arbre des ressources, le diff avec Git et l'état de synchronisation. Idéal pour voir la réconciliation, et c'est souvent l'outil qu'on présente en premier.
- Flux : plus léger, piloté uniquement par CRD et CLI. Avec
flux bootstrap, il écrit sa propre configuration dans Git et se gère lui-même dès le départ. L'image automation (Flux commite lui-même les nouveaux tags d'image) est intégrée.
Pour un homelab où tout doit vivre dans Git, Flux colle parfaitement. Si tu préfères Argo CD, la logique reste la même : jette un œil à mes slides GitOps avec Argo CD.
Quelle distribution locale ?¶
| Option | Points forts | Limites |
|---|---|---|
| kind | Kubernetes upstream dans des conteneurs Docker, cluster créé ou détruit en une minute, multi-nœuds via un fichier YAML | Ingress et registre local à configurer soi-même |
| k3d | k3s dans Docker, très léger, registre local intégré | Distribution k3s (Traefik et stockage local par défaut), un peu différente d'un Kubernetes standard |
| minikube | Addons (ingress, registre) activables en une commande | Plus lourd, plus lent à recréer |
| k3s seul | Idéal pour un homelab permanent (VM, Raspberry Pi) | Ne tourne pas nativement sur macOS |
On part sur kind : c'est le plus proche d'un vrai cluster, et le plus rapide à jeter puis recréer.
| Brique | Version testée |
|---|---|
| Docker Desktop | macOS arm64 |
| kind | 0.33 (Kubernetes 1.37) |
| Flux | 2.10 |
| metrics-server (chart) | 3.14 |
| podinfo | 6.17 |
Prérequis¶
Il te faut aussi Docker Desktop démarré, et un compte GitLab ou GitHub pour héberger le dépôt.
Attention à la version de kubectl
kubectl ne tolère qu'une version mineure d'écart avec le cluster. Un vieux kubectl installé par un autre outil (Rancher Desktop par exemple) peut passer devant celui de Homebrew dans ton PATH : vérifie avec kubectl version.
1. Le cluster kind¶
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: homelab
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30080 # NodePort HTTP exposé sur localhost:8080
hostPort: 8080
- containerPort: 30443 # NodePort HTTPS exposé sur localhost:8443
hostPort: 8443
- role: worker
- role: worker
kind create cluster --config kind-config.yaml
kubectl wait --for=condition=Ready nodes --all --timeout=120s
kubectl get nodes
NAME STATUS ROLES VERSION
homelab-control-plane Ready control-plane v1.37.0
homelab-worker Ready <none> v1.37.0
homelab-worker2 Ready <none> v1.37.0
- Un control-plane et deux workers : assez pour voir des pods se répartir sur plusieurs nœuds, sans charger ta machine.
- Les
extraPortMappings: sur macOS, le réseau Docker n'est pas routable depuis ta machine, impossible de joindre directement l'IP d'un nœud. Le port mapping est la seule porte d'entrée :localhost:8080arrive sur le port30080du control-plane. - Le contexte kubectl : kind ajoute le contexte
kind-homelabdans~/.kube/configet le rend actif. k9s le voit directement,:ctxpour changer de contexte.
2. Bootstrap Flux¶
Ce que fait le bootstrap :
- Il crée le dépôt s'il n'existe pas (privé par défaut), ou réutilise celui qui existe.
- Il pousse les manifestes de Flux dans
clusters/kind/flux-system/et installe les contrôleurs dans le namespaceflux-system. - Il génère une clé SSH, stocke la partie privée dans le Secret
flux-systemet enregistre la partie publique comme deploy key du dépôt. C'est cette clé que Flux utilise ensuite : ton token ne sert qu'au bootstrap.
Deux options préparent la suite et t'évitent de refaire le bootstrap plus tard :
--read-write-key: la deploy key peut pousser des commits, indispensable pour l'image automation.--components-extra: installe les deux contrôleurs d'image automation, absents par défaut.
Garde ton token hors de l'historique
Lance le bootstrap dans ton propre terminal et ne commite jamais le token. La commande est idempotente : tu peux la relancer pour mettre à jour Flux ou changer ses options.
Vérifie que tout tourne :
Tu dois voir six contrôleurs : source, kustomize, helm, notification, et les deux contrôleurs d'image.
3. Structurer le dépôt¶
Flux applique tout ce qui se trouve dans clusters/kind/. On y déclare une Kustomization Flux par couche, pour que l'infrastructure soit prête avant les applications :
flowchart LR
G[("Dépôt Git")] --> FS["Kustomization<br>flux-system"]
FS --> IC["infra-controllers<br>metrics-server"]
IC -->|dependsOn| AP["apps<br>podinfo"] .
├── clusters/kind/
│ ├── flux-system/ # généré par flux bootstrap, ne pas éditer à la main
│ ├── infrastructure.yaml # Kustomization infra-controllers
│ └── apps.yaml # Kustomization apps
├── infrastructure/
│ └── controllers/ # metrics-server
├── apps/
│ ├── base/podinfo/ # définition générique de l'app
│ └── kind/ # overlay propre au cluster kind
└── kind-config.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infra-controllers
namespace: flux-system
spec:
interval: 1h
retryInterval: 1m
timeout: 5m
sourceRef:
kind: GitRepository
name: flux-system
path: ./infrastructure/controllers
prune: true
wait: true
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 10m
retryInterval: 1m
timeout: 5m
dependsOn:
- name: infra-controllers
sourceRef:
kind: GitRepository
name: flux-system
path: ./apps/kind
prune: true
wait: true
dependsOn+wait: true:appsn'est appliquée qu'une fois toute l'infrastructureReady.prune: true: supprimer un manifeste du dépôt supprime la ressource du cluster.base/ overlay :apps/base/porte la définition générique d'une app,apps/kind/ce qui est propre à ce cluster. Le jour où tu ajoutes un deuxième cluster, il aura son propre overlay.
4. Premier composant d'infra : metrics-server¶
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: metrics-server
namespace: kube-system
spec:
interval: 24h
url: https://kubernetes-sigs.github.io/metrics-server/
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: metrics-server
namespace: kube-system
spec:
interval: 30m
chart:
spec:
chart: metrics-server
version: "3.x"
sourceRef:
kind: HelmRepository
name: metrics-server
values:
args:
# Les kubelets kind servent des certificats auto-signés
- --kubelet-insecure-tls
Avec un kustomization.yaml qui liste ce fichier dans infrastructure/controllers/, metrics-server alimente kubectl top et les colonnes CPU / mémoire de k9s :
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
homelab-control-plane 190m 1% 833Mi 10%
homelab-worker 113m 1% 321Mi 4%
homelab-worker2 130m 1% 486Mi 6%
5. Première app : podinfo¶
podinfo est l'app de démo de l'écosystème Flux. Son chart est publié dans un registre OCI : on utilise cette fois un OCIRepository référencé par chartRef, l'autre façon de sourcer un chart Helm avec Flux.
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: podinfo
namespace: podinfo
spec:
interval: 1h
url: oci://ghcr.io/stefanprodan/charts/podinfo
layerSelector:
mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
operation: copy
ref:
semver: "6.x"
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: podinfo
namespace: podinfo
spec:
interval: 5m
chartRef:
kind: OCIRepository
name: podinfo
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
driftDetection:
mode: enabled
La base contient aussi le Namespace podinfo et un kustomization.yaml qui liste les ressources.
Le Service podinfo prend le NodePort 30080, celui du port mapping kind. Pour déployer, tu pousses et tu demandes à Flux de relire le dépôt tout de suite (sinon, il le relit toutes les minutes) :
git add . && git commit -m "feat: add podinfo" && git push
flux reconcile kustomization flux-system --with-source
flux get helmreleases -A
curl localhost:8080
{
"hostname": "podinfo-c6497595d-zwqjm",
"version": "6.17.0",
"message": "greetings from podinfo v6.17.0"
}
6. Tester le drift¶
C'est le moment de vérifier que Git est bien la source de vérité : on modifie le cluster à la main et on regarde Flux remettre l'état décrit dans le dépôt.
La détection de drift d'une HelmRelease est désactivée par défaut
Sans driftDetection.mode: enabled, helm-controller ne regarde que l'état de la release Helm et ignore les modifications faites à la main dans le cluster : le scale et la suppression ci-dessous ne seraient jamais corrigés. Les ressources appliquées directement par une Kustomization Flux, elles, sont réappliquées à chaque intervalle.
# Scale manuel : Flux ramène à 2 replicas
kubectl -n podinfo scale deploy podinfo --replicas=5
flux reconcile helmrelease podinfo -n podinfo
kubectl -n podinfo get deploy podinfo
# Suppression : Flux recrée le Deployment
kubectl -n podinfo delete deploy podinfo
flux reconcile helmrelease podinfo -n podinfo
kubectl -n podinfo get deploy podinfo
# Trace des corrections
kubectl -n podinfo events --for helmrelease/podinfo
Warning DriftDetected HelmRelease/podinfo Cluster state of release podinfo/podinfo.v1 has drifted from the desired state:
Deployment/podinfo/podinfo removed
Normal DriftCorrected HelmRelease/podinfo Cluster state of release podinfo/podinfo.v1 has been corrected:
Deployment/podinfo/podinfo created
flux reconcile déclenche la correction tout de suite. Sans lui, elle arrive au prochain intervalle de la HelmRelease (5 minutes ici).
7. Repartir de zéro¶
Le cluster est jetable, tout son état vit dans le dépôt :
kind delete cluster --name homelab
kind create cluster --config kind-config.yaml
flux bootstrap gitlab ... # même commande qu'à l'étape 2
Flux réapplique alors toutes les couches dans l'ordre, sans aucune commande kubectl apply de ta part.
La suite¶
- Épisode 02 : Gateway API avec Envoy Gateway : on remplace le NodePort de podinfo par une vraie porte d'entrée, une
Gatewayet uneHTTPRoute. - Les prochaines pistes du lab : chiffrer les secrets dans Git avec SOPS et age, activer l'image automation (les contrôleurs sont déjà installés), brancher les notifications Flux et le monitoring.
- Pour les bases Helm : packager et valider un chart dans GitLab CI.