Aller au contenu
Conteneurs & Orchestration medium

Créer des clusters Kubernetes locaux avec k3d

22 min de lecture

logo k3d

Ce guide vous permet de créer un cluster Kubernetes local en 20 secondes avec k3d. Vous apprendrez à démarrer un cluster single-node, multi-nœuds avec workers, un cluster HA (haute disponibilité) avec 3 control-planes et etcd embarqué, et à utiliser un registre d’images local intégré. Toutes les commandes ont été testées avec k3d v5.8.3 et k3s v1.31.5.

  • Installer k3d et créer votre premier cluster en une commande
  • Déployer un cluster multi-nœuds (servers + agents)
  • Configurer un cluster HA avec 3 control-planes et etcd distribué
  • Importer des images Docker locales dans le cluster
  • Créer un registre d’images local intégré au cluster
  • Exposer des services via Ingress (Traefik intégré) ou NodePort
  • Utiliser un fichier de configuration YAML pour vos clusters

k3d n’est pas le seul outil pour créer un cluster Kubernetes local. Voici un comparatif pour vous aider à choisir :

Critèrek3dMinikubeKind
Cas d’usageDev local, CI/CD, clusters jetablesDev local, apprentissageCI/CD, tests
RuntimeConteneurs Docker (k3s)VM ou conteneursConteneurs Docker (kubeadm)
DatastoreSQLite (1 server) / etcd embarqué (HA)etcd (kubeadm)etcd (kubeadm)
Multi-nœuds--servers + --agents--nodes✅ via config YAML
HA control plane--servers 3 (etcd embarqué)--ha (kube-vip)✅ multi control-planes
Ingress✅ Traefik intégré⚠️ Addon à activer⚠️ À installer
Registre local--registry-create (intégré)⚠️ Addon cassé (v1.37)⚠️ Manuel
Démarrage~15-20s~30-60s~20-30s
RessourcesLéger (~300 MiB/nœud)2 CPU, 2 Go RAM/nœud~500 MiB/nœud
Config as Code✅ Fichier YAML⚠️ Flags CLI✅ Fichier YAML

Choisissez k3d si :

  • Vous voulez le cluster Kubernetes le plus rapide à créer et détruire
  • Vous avez besoin d’un registre d’images local sans configuration manuelle
  • Vous travaillez avec des pipelines CI/CD qui nécessitent des clusters éphémères
  • Vous voulez Traefik comme Ingress Controller prêt à l’emploi

Comprendre l’architecture de k3d vous aidera à diagnostiquer les problèmes et à choisir la bonne topologie.

k3d crée des conteneurs Docker qui exécutent chacun une instance de k3s. Contrairement à Minikube qui peut utiliser des VMs, k3d utilise exclusivement Docker (ou Podman) comme runtime. C’est ce qui le rend si rapide à démarrer.

Quand vous créez un cluster avec k3d cluster create, voici ce qui se passe :

  1. Un réseau Docker dédié est créé pour isoler le cluster
  2. Un ou plusieurs conteneurs server (control-planes k3s) sont lancés
  3. Un ou plusieurs conteneurs agent (workers k3s) rejoignent le cluster
  4. Un load balancer (serverlb, basé sur Nginx) est créé devant les servers
  5. Le kubeconfig est automatiquement fusionné dans votre fichier par défaut

Points clés :

  • Chaque nœud (server ou agent) est un conteneur Docker exécutant k3s
  • Le load balancer serverlb distribue le trafic API vers tous les servers
  • Les ports exposés sur le serverlb sont proxifiés vers les nœuds server
  • k3s utilise containerd comme runtime à l’intérieur des conteneurs
  • Traefik est déployé automatiquement comme Ingress Controller

Contrairement à Minikube (qui a un daemon Docker dans chaque nœud), k3d utilise le daemon Docker de votre machine. Les nœuds tournent comme des conteneurs classiques. Pour importer une image, k3d la copie directement dans les conteneurs nœuds — pas besoin de docker-env ou de double daemon.

Avant de commencer, vérifiez que vous avez :

  • Docker installé et fonctionnel (v20.10.5 minimum) — Guide d’installation Docker
  • kubectl installé — Guide kubectl
  • RAM : ~512 Mo par nœud (2 Go recommandés pour un cluster HA)
  • Disque : quelques Go pour les images Docker

Vérification :

Fenêtre de terminal
docker version --format '{{.Server.Version}}'
27.5.1
Fenêtre de terminal
kubectl version --client --short 2>/dev/null || kubectl version --client
Fenêtre de terminal
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

Vérification :

Fenêtre de terminal
k3d version
k3d version v5.8.3
k3s version v1.31.5-k3s1 (default)

Le cas le plus simple : un seul server (control-plane) qui sert aussi de worker.

Fenêtre de terminal
k3d cluster create demo -p "8888:80@loadbalancer"
OptionDescription
demoNom du cluster (sera préfixé k3d- dans Docker)
-p "8888:80@loadbalancer"Mappe le port 8888 local vers le port 80 du load balancer

Le cluster est prêt en 15 à 20 secondes. k3d configure automatiquement votre kubeconfig pour utiliser ce cluster.

Vérification :

Fenêtre de terminal
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE
k3d-demo-server-0 Ready control-plane,master 15s v1.31.5+k3s1 172.21.0.2 K3s v1.31.5+k3s1
Fenêtre de terminal
k3d cluster list
NAME SERVERS AGENTS LOADBALANCER
demo 1/1 0/0 true

Pour voir les conteneurs Docker créés :

Fenêtre de terminal
docker ps --filter "name=k3d-demo" --format "table {{.Names}}\t{{.Image}}"
NAMES IMAGE
k3d-demo-serverlb ghcr.io/k3d-io/k3d-proxy:5.8.3
k3d-demo-server-0 rancher/k3s:v1.31.5-k3s1

Pour simuler un environnement plus réaliste avec un control-plane et des workers dédiés :

Fenêtre de terminal
k3d cluster create multinode --servers 1 --agents 2 -p "8888:80@loadbalancer"

Cette commande crée :

  • 1 server (control-plane k3s, k3d-multinode-server-0)
  • 2 agents (workers k3s, k3d-multinode-agent-0, k3d-multinode-agent-1)
  • 1 load balancer (k3d-multinode-serverlb)

Vérification :

Fenêtre de terminal
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION
k3d-multinode-server-0 Ready control-plane,master 17s v1.31.5+k3s1
k3d-multinode-agent-0 Ready <none> 16s v1.31.5+k3s1
k3d-multinode-agent-1 Ready <none> 16s v1.31.5+k3s1
Fenêtre de terminal
k3d node create extra-agent --cluster multinode --role agent
Fenêtre de terminal
k3d node delete k3d-extra-agent-0

Le mode HA crée un cluster avec 3 servers (control-planes), chacun exécutant une instance d’etcd embarqué (embedded etcd). Cette topologie est celle recommandée pour la production Kubernetes.

  • k3d démarre le premier server avec --cluster-init pour activer le mode etcd distribué
  • Les servers suivants rejoignent le cluster etcd existant
  • Le load balancer serverlb distribue les requêtes API entre les 3 servers
  • Si un server tombe, les 2 restants maintiennent le quorum etcd
Fenêtre de terminal
k3d cluster create ha-demo --servers 3 --agents 2 -p "8888:80@loadbalancer"

Le démarrage prend environ 50 secondes (chaque server est démarré séquentiellement pour garantir la convergence etcd).

Vérification :

Fenêtre de terminal
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION
k3d-ha-demo-server-0 Ready control-plane,etcd,master 50s v1.31.5+k3s1
k3d-ha-demo-server-1 Ready control-plane,etcd,master 38s v1.31.5+k3s1
k3d-ha-demo-server-2 Ready control-plane,etcd,master 20s v1.31.5+k3s1
k3d-ha-demo-agent-0 Ready <none> 18s v1.31.5+k3s1
k3d-ha-demo-agent-1 Ready <none> 18s v1.31.5+k3s1
Fenêtre de terminal
k3d cluster list
NAME SERVERS AGENTS LOADBALANCER
ha-demo 3/3 2/2 true

Notez le rôle etcd sur chaque nœud server : c’est le signe que le mode etcd embarqué distribué est actif.

Fenêtre de terminal
k3d node create extra-server --cluster ha-demo --role server

Plutôt que de passer de nombreux flags CLI, k3d supporte les fichiers de configuration YAML. C’est la méthode recommandée pour les clusters reproductibles.

k3d-config.yaml
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: my-cluster
servers: 1
agents: 2
Fenêtre de terminal
k3d cluster create --config k3d-config.yaml
k3d-config.yaml
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: from-config
servers: 1
agents: 2
ports:
- port: 8888:80
nodeFilters:
- loadbalancer
registries:
create:
name: registry.localhost
host: "0.0.0.0"
hostPort: "5111"
options:
k3d:
wait: true

Ce fichier crée un cluster avec :

  • 1 server + 2 agents
  • Le port 80 du Ingress Controller exposé sur le port 8888 local
  • Un registre d’images local accessible sur registry.localhost:5111
Fenêtre de terminal
k3d cluster create --config k3d-config.yaml
ChampDescription
apiVersionToujours k3d.io/v1alpha5 (version actuelle)
kindSimple (le seul type public pour l’instant)
metadata.nameNom du cluster
servers / agentsNombre de nœuds de chaque type
portsMapping de ports (identique au flag -p)
registries.createCrée un registre local avec le cluster
options.k3d.waitAttend que le cluster soit prêt avant de rendre la main
k3d-config-avance.yaml
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: avance
servers: 1
agents: 2
volumes:
- volume: /my/host/path:/mnt/data
nodeFilters:
- server:0
env:
- envVar: MY_VAR=my-value
nodeFilters:
- server:0
options:
k3s:
extraArgs:
- arg: "--disable=traefik"
nodeFilters:
- server:*
k3d:
disableLoadbalancer: false
kubeconfig:
updateDefaultKubeconfig: true
switchCurrentContext: true
OptionUsage
volumesMonte un dossier local dans un nœud
envInjecte des variables d’environnement
options.k3s.extraArgsPasse des arguments à k3s (ex : désactiver Traefik)
options.k3d.disableLoadbalancerSupprime le load balancer serverlb
options.kubeconfigContrôle la fusion du kubeconfig

Le problème : Vous avez buildé une image Docker sur votre machine, mais quand vous la déployez dans k3d, Kubernetes ne la trouve pas.

Pourquoi ? Les conteneurs k3s utilisent containerd (pas le daemon Docker de votre machine). Votre image existe dans Docker local, pas dans containerd.

La commande k3d image import copie une image depuis Docker vers tous les nœuds du cluster :

Fenêtre de terminal
# Builder votre image
docker build -t mon-app:v1 .
# Importer dans le cluster k3d
k3d image import mon-app:v1 -c demo

Déployer l’image :

pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-local
spec:
containers:
- name: app
image: mon-app:v1
imagePullPolicy: Never
Fenêtre de terminal
kubectl apply -f pod.yaml
kubectl get pod test-local
NAME READY STATUS RESTARTS AGE
test-local 1/1 Running 0 10s

Méthode 2 : Registre local intégré (recommandé)

Section intitulée « Méthode 2 : Registre local intégré (recommandé) »

k3d peut créer un registre d’images local (conteneur Docker registry) connecté au cluster. C’est la méthode la plus fiable car elle fonctionne exactement comme un registre distant.

  1. Créer un cluster avec registre :

    Fenêtre de terminal
    k3d cluster create demo --registry-create myregistry.localhost:0.0.0.0:5111

    Ou via fichier de configuration (voir la section précédente).

  2. Taguer et pousser votre image :

    Fenêtre de terminal
    docker tag nginx:alpine myregistry.localhost:5111/nginx:latest
    docker push myregistry.localhost:5111/nginx:latest
  3. Déployer depuis le registre :

    Fenêtre de terminal
    kubectl run web --image=myregistry.localhost:5111/nginx:latest
    Fenêtre de terminal
    kubectl get pod web
    NAME READY STATUS RESTARTS AGE
    web 1/1 Running 0 10s

Le registre est accessible à la fois depuis votre machine locale (via localhost:5111) et depuis les nœuds k3d (via le nom du conteneur sur le réseau Docker).

Quand utiliser image import vs registre local ?

MéthodeCas d’usage
k3d image importTests rapides, images uniques, pas de push nécessaire
Registre localWorkflow de développement continu, images partagées, CI/CD

k3d utilise un load balancer (serverlb) basé sur Nginx devant les nœuds servers. Les ports que vous mappez à la création du cluster (-p) sont routés via ce load balancer.

k3s inclut Traefik comme Ingress Controller par défaut. Pas d’add-on à activer — tout est prêt.

  1. Créer le cluster avec le port Ingress mappé :

    Fenêtre de terminal
    k3d cluster create demo -p "8888:80@loadbalancer" --agents 2

    Le mapping -p "8888:80@loadbalancer" signifie : “envoyer le trafic du port 8888 de ma machine vers le port 80 du load balancer, qui le forwarde vers Traefik dans les nœuds server”.

  2. Déployer une application :

    Fenêtre de terminal
    kubectl create deployment nginx --image=nginx:alpine
    kubectl create service clusterip nginx --tcp=80:80
  3. Créer un Ingress :

    ingress.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: nginx
    annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
    spec:
    rules:
    - http:
    paths:
    - path: /
    pathType: Prefix
    backend:
    service:
    name: nginx
    port:
    number: 80
    Fenêtre de terminal
    kubectl apply -f ingress.yaml
  4. Tester l’accès :

    Fenêtre de terminal
    curl http://localhost:8888/
    <!DOCTYPE html>
    <html>
    <head><title>Welcome to nginx!</title>
    ...

Pour mapper un port spécifique d’un nœud vers votre machine :

Fenêtre de terminal
k3d cluster create demo -p "8082:30080@agent:0" --agents 2

Créez un service de type NodePort avec le port 30080 :

nodeport-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-np
spec:
type: NodePort
ports:
- port: 80
nodePort: 30080
selector:
app: nginx
Fenêtre de terminal
kubectl apply -f nodeport-svc.yaml
curl http://localhost:8082/

k3d gère automatiquement le kubeconfig. À la création d’un cluster, le contexte est ajouté à votre fichier kubeconfig par défaut (~/.kube/config) et le contexte courant bascule sur le nouveau cluster.

Fenêtre de terminal
k3d kubeconfig get demo
Fenêtre de terminal
k3d kubeconfig write demo
/home/bob/.k3d/kubeconfig-demo.yaml
Fenêtre de terminal
k3d kubeconfig merge demo --kubeconfig-merge-default
Fenêtre de terminal
kubectl config use-context k3d-demo
ActionCommande
Créer un clusterk3d cluster create <nom>
Créer depuis un fichierk3d cluster create --config <fichier.yaml>
Lister les clustersk3d cluster list
Arrêter un clusterk3d cluster stop <nom>
Redémarrer un clusterk3d cluster start <nom>
Supprimer un clusterk3d cluster delete <nom>
Supprimer tous les clustersk3d cluster delete --all
Lister les nœudsk3d node list
Ajouter un agentk3d node create <nom> --cluster <cluster> --role agent
Ajouter un serverk3d node create <nom> --cluster <cluster> --role server
Supprimer un nœudk3d node delete <nom>
Importer une imagek3d image import <image> -c <cluster>
Créer un registrek3d registry create <nom> --port <port>
Lister les registresk3d registry list
Supprimer un registrek3d registry delete <nom>
Récupérer le kubeconfigk3d kubeconfig get <cluster>
SymptômeCause probableSolution
Port déjà utiliséUn autre service écoute sur ce portChangez le port dans -p
ErrImagePull avec image localeimagePullPolicy absentAjoutez imagePullPolicy: Never
Cluster HA, nœud ne rejoint pasCluster créé sans --cluster-initRecréez avec --servers 3
Ingress ne répond pasPort 80 non mappé au load balancerRecréez avec -p "PORT:80@loadbalancer"
registry.localhost introuvableRésolution DNS locale manquanteAjoutez dans /etc/hosts
Cluster ne redémarre pasIPs des conteneurs changentUtilisez --subnet auto
k3d cluster start échoueVolumes Docker corrompusSupprimez et recréez le cluster

Symptôme : La création du cluster échoue avec “address already in use”.

Solution :

Fenêtre de terminal
# Trouver le processus qui utilise le port
sudo lsof -i :8888
# Utiliser un autre port
k3d cluster create demo -p "9090:80@loadbalancer"

Symptôme : Votre Pod reste en ErrImagePull ou ImagePullBackOff.

Solution :

Fenêtre de terminal
# Importer l'image dans le cluster
k3d image import mon-app:v1 -c demo
# Vérifier le manifest : imagePullPolicy doit être Never ou IfNotPresent
kubectl get pod <nom> -o jsonpath='{.spec.containers[0].imagePullPolicy}'

Symptôme : Un server reste en “NotReady” ou le cluster met très longtemps à démarrer.

Diagnostic :

Fenêtre de terminal
# Vérifier les logs k3s d'un nœud
docker logs k3d-ha-demo-server-0 --tail 50
# Vérifier les ressources Docker
docker stats --no-stream

Solutions :

  1. Assurez-vous d’avoir au moins 2 CPU et 4 Go de RAM disponibles

  2. Si les IPs changent au restart, recréez avec --subnet auto :

    Fenêtre de terminal
    k3d cluster delete ha-demo
    k3d cluster create ha-demo --servers 3 --agents 2 --subnet auto

Symptôme : docker push vers le registre échoue avec “connection refused”.

Diagnostic :

Fenêtre de terminal
# Vérifier que le registre tourne
docker ps --filter "name=registry"
# Tester la connexion
curl http://localhost:5111/v2/_catalog

Solutions :

  1. Vérifiez que le port mappé est correct : k3d registry list
  2. Sur macOS/Windows, ajoutez le nom du registre dans /etc/hosts

Pour cette opération, supprimez tous les clusters et registres :

Fenêtre de terminal
k3d cluster delete --all
k3d registry delete --all

Pour supprimer aussi les volumes et réseaux Docker orphelins :

Fenêtre de terminal
docker volume prune -f
docker network prune -f
  • k3d exécute k3s dans des conteneurs Docker — le cluster Kubernetes local le plus rapide à créer (~20 secondes)
  • Chaque nœud est un conteneur Docker : server (control-plane) ou agent (worker)
  • Un load balancer Nginx (serverlb) est créé devant les servers
  • Le mode multi-server (--servers 3) active etcd distribué (HA)
  • Traefik est inclus nativement comme Ingress Controller
  • metrics-server est également intégré (pas d’add-on à activer)
  • k3d image import copie une image Docker locale dans le cluster
  • --registry-create crée un registre d’images local avec le cluster
  • Les fichiers de configuration YAML permettent des clusters reproductibles
  • Le kubeconfig est fusionné automatiquement (préfixe k3d-)
  • Utilisez --subnet auto pour éviter les problèmes d’IP au redémarrage

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.