Le cluster kind et l'Ingress
Tester un chart sur un cluster partagé de préproduction, c'est attendre une fenêtre, risquer de casser le travail d'une autre équipe et ne jamais pouvoir tout jeter. Un cluster kind (Kubernetes in Docker) se crée en une minute, coûte zéro et se supprime sans trace. Il a des limites, qu'il faut connaître pour ne pas en tirer de fausses conclusions : pas de LoadBalancer, pas de vraie isolation entre nœuds, des images qui ne viennent pas d'un registre.
Le mécanisme
Chaque nœud kind est un conteneur Docker qui fait tourner un kubelet et containerd. Le trafic entre par un port de la machine hôte, mappé sur le port 80 du conteneur control-plane, où tourne le contrôleur Ingress NGINX.
flowchart LR H["Hôte : 127.0.0.1:8081"] -->|"extraPortMappings"| CP["Conteneur lab-k8s-ia-control-plane :80"] CP --> IC["Pod ingress-nginx-controller (hostPort 80)"] IC -->|"Ingress host=assistant.127.0.0.1.nip.io"| W1["Conteneur lab-k8s-ia-worker : pods API"] IC --> W2["Conteneur lab-k8s-ia-worker2 : pods API"] D["docker build"] -->|"kind load docker-image"| W1 D --> W2 MS["metrics-server"] -->|"CPU / mémoire des pods"| HPA["HPA"]
Trois points distinguent kind d'un cluster managé :
| Sujet | kind | GKE (projet suivant) |
|---|---|---|
| Entrée | port de l'hôte + hostPort sur un nœud étiqueté ingress-ready | Service LoadBalancer ou Gateway avec IP publique |
| Images | kind load docker-image copie l'image dans chaque nœud | tirées d'Artifact Registry |
| Métriques CPU | metrics-server à installer, avec --kubelet-insecure-tls | fournies |
| NetworkPolicy | appliquées par kindnet depuis kind 0.24 | Dataplane V2 (Cilium) |
La mise en œuvre
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
ingress-ready: "true"
extraPortMappings:
- { containerPort: 80, hostPort: 8081, listenAddress: "127.0.0.1", protocol: TCP }
- role: worker
- role: workerlistenAddress: "127.0.0.1" : sans lui, le port s'ouvre sur toutes les interfaces et votre service de test devient joignable depuis le réseau Wi-Fi du bureau. Deux nœuds de travail suffisent pour vérifier la répartition des pods et le comportement du PodDisruptionBudget lors d'un drain.
La cible make up enchaîne cluster, extensions, image, Secret et chart. Les extensions sont installées depuis des manifestes à version figée :
INGRESS_NGINX := https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/kind/deploy.yaml
METRICS_SERVER := https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.9.0/components.yaml
K := kubectl --context $(CTX)
cluster:
@kind get clusters | grep -qx $(CLUSTER) || kind create cluster --name $(CLUSTER) --config k8s/kind-config.yaml --wait 120s
addons:
@$(K) apply -f $(INGRESS_NGINX) >/dev/null
@$(K) apply -f $(METRICS_SERVER) >/dev/null
@$(K) -n kube-system patch deploy metrics-server --type=json \
-p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]' >/dev/null 2>&1 || true
@$(K) -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180sToutes les commandes passent --context kind-lab-k8s-ia. Sur un poste qui a aussi un contexte de production dans ~/.kube/config, un kubectl apply sans contexte explicite part là où pointe le contexte courant, et le contexte courant change dès qu'un autre outil crée un cluster.
Le nom assistant.127.0.0.1.nip.io est résolu par un DNS public vers 127.0.0.1 : pas de modification de /etc/hosts. Évitez *.localtest.me sur une machine qui préfère IPv6 : il peut se résoudre en ::1, alors que le port kind n'écoute qu'en IPv4.
make cluster addons
kubectl --context kind-lab-k8s-ia get nodes
kubectl --context kind-lab-k8s-ia top nodesCe qui casse en production
| Symptôme | Cause | Parade |
|---|---|---|
kind create cluster échoue : port is already allocated | le port 8081 est pris par un autre processus | changer hostPort et HTTP_PORT du Makefile |
Pods en ErrImagePull pour lab-k8s-ia/inference:0.1.0 | image construite mais pas chargée dans les nœuds | make image (build + kind load) ; garder imagePullPolicy: IfNotPresent |
kubectl top : Metrics API not available, HPA en <unknown> | metrics-server absent ou refuse les certificats des kubelets kind | --kubelet-insecure-tls, en lab uniquement |
| Une commande de lab modifie un cluster de production | contexte kubectl courant mal positionné | --context explicite partout, jamais de contexte implicite dans un Makefile |
| L'Ingress répond 404 | l'en-tête Host ne correspond pas à ingress.host | appeler l'URL avec le bon nom, ou curl -H 'Host: …' |
La règle de décision
- kind pour tout ce qui se vérifie sur un poste : chart, sondes, rollouts, NetworkPolicy, HPA. Ne mesurez pas la performance absolue sur kind : les nœuds partagent le même CPU.
- Versions figées pour chaque manifeste externe, jamais
mainnilatest. - Ingress NGINX seulement là où il est déjà exploité ; Gateway API pour tout nouveau cluster.
Pratique
Un collègue lance make up sur son poste ; quelques secondes plus tard, son kubectl get pods affiche les pods de production de l'équipe paiement. Quelle est l'explication la plus probable ?
kind change le contexte courant au moment de la création, puis tout autre outil peut le rechanger. Une commande sans --context agit sur le contexte du moment. C'est pourquoi chaque commande du Makefile passe --context kind-lab-k8s-ia. kind ne rejoint jamais un autre cluster et l'Ingress ne route que dans son cluster.
L'HPA affiche cpu: <unknown>/60% depuis 10 minutes. kubectl top pods renvoie « Metrics API not available ». Que corrigez-vous ?
Le message vient de l'API metrics.k8s.io, servie par metrics-server. Tant qu'elle ne répond pas, l'HPA ne peut rien calculer. Des requests CPU absentes donneraient un message différent (missing request for cpu). La cible à 60 % et la NetworkPolicy de l'application n'empêchent pas metrics-server de répondre.
Exercice
Ajoutez un troisième nœud de travail, recréez le cluster et vérifiez que l'Ingress fonctionne toujours. Combien de temps prend la recréation complète (make down up) sur votre machine ?
Voir la solution
Ajoutez - role: worker dans k8s/kind-config.yaml, puis :
time (make down && make up)
kubectl --context kind-lab-k8s-ia get nodes # 4 nœuds Ready
curl -s http://assistant.127.0.0.1.nip.io:8081/health/liveL'Ingress continue de fonctionner parce que son pod est épinglé au nœud control-plane (étiquette ingress-ready), le seul à recevoir le port de l'hôte. Sur la machine de référence, make up complet a pris 1 min 40 s, images comprises.
Résultat obtenu
Exécution du 2026-10-15, après make up (1 min 40 s) :
$ kubectl --context kind-lab-k8s-ia get nodes
NAME STATUS ROLES AGE VERSION
lab-k8s-ia-control-plane Ready control-plane 19m v1.37.0
lab-k8s-ia-worker Ready <none> 19m v1.37.0
lab-k8s-ia-worker2 Ready <none> 19m v1.37.0
$ kubectl --context kind-lab-k8s-ia -n ingress-nginx get pods -o wide
ingress-nginx-controller-596f5b6bcf-66dcl Running lab-k8s-ia-control-plane
$ kubectl --context kind-lab-k8s-ia top nodes # pendant le test de charge de la leçon 5
NAME CPU(cores) MEMORY(bytes)
lab-k8s-ia-control-plane 330m 1119Mi
lab-k8s-ia-worker 3590m 354Mi
lab-k8s-ia-worker2 1848m 406MiJe sais faire
- Créer un cluster kind multi-nœuds avec un port d'entrée limité à
127.0.0.1 - Installer Ingress NGINX et metrics-server à version figée
- Charger une image locale dans les nœuds kind
- Expliquer pourquoi un nouveau cluster de production part sur Gateway API