Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Déployer Kubernetes avec Kubespray

21 min de lecture

logo kubernetes

Kubespray déploie des clusters Kubernetes en utilisant Ansible. Ce guide construit un lab KVM reproductible servant de base à des déploiements plus robustes. Contrairement à kubeadm qui nécessite des commandes manuelles sur chaque nœud, Kubespray automatise tout depuis votre poste de travail.

CritèreKubespraykubeadmk0sk3sRKE2
TypePlaybooks AnsibleBootstrap toolDistributionDistributionDistribution
OrientationProd, multi-cloudStandard upstreamEdge, prod, CIEdge, IoT, devEnterprise, sécurité
InstallationAnsibleCommandesk0sctl (YAML)Script uniqueScript
CNI défautConfigurableAucunkube-routerFlannelCanal
HA native✅ AnsibleManuel✅ Multi-controller✅ Multi-server✅ Intégrée
Profil CISSelon configManuelManuelManuel✅ Intégré
Cas d’usageProd automatiséeBare metal, formationEdge, CI/CD, prodEdge, IoT, devConformité, prod

À la fin de ce guide, vous saurez :

  • Provisionner des VMs KVM configurées via cloud-init pour un cluster Kubernetes
  • Préparer l’environnement Kubespray avec les bonnes versions d’Ansible et dépendances
  • Créer un inventaire personnalisé définissant la topologie du cluster
  • Déployer un cluster complet avec Calico, containerd et CoreDNS en une seule commande
  • Valider que tous les composants fonctionnent correctement
  • Personnaliser les options (CNI, runtime, addons) selon vos besoins

Kubespray déploie les mêmes composants que kubeadm mais automatise la configuration. Les besoins sont donc identiques :

RôleCPURAM (min)RAM (recommandé)DisqueQuantité
Control plane2 vCPU2 Go2 Go20 Go1 (ou 3+ pour HA)
Worker2 vCPU1 Go2 Go20 Go2+
Poste Ansible1 vCPU1 Go1 Go5 GoVotre machine locale

Le minimum Kubespray pour les workers est 1 Go, mais ce guide recommande 2 Go pour éviter les problèmes lors du téléchargement parallèle des images.

Sur votre poste de travail (machine de contrôle Ansible) :

  • Python 3.10+ avec pip
  • Git pour cloner le repository Kubespray
  • SSH avec une clé configurée (les VMs doivent être accessibles sans mot de passe)

Sur les VMs cibles :

  • Ubuntu 24.04 ou Rocky Linux 9 (distribution supportée par Kubespray)
  • Python 3 installé (requis par Ansible)
  • Accès sudo sans mot de passe pour l’utilisateur SSH
  • Accès Internet pour télécharger images et binaires (sauf configuration offline dédiée)

Ce guide a été validé avec les versions suivantes :

ComposantVersion
Kubesprayv2.26.0
Kubernetesv1.30.4
Ansiblecore 2.16.x
CNICalico (plugin par défaut)

Nous allons créer un cluster avec 1 control plane et 2 workers sur le réseau libvirt par défaut :

Architecture du lab Kubespray avec Ansible et 3 VMs sur KVM

Téléchargez l’image cloud Ubuntu 24.04 si elle n’est pas déjà présente :

Fenêtre de terminal
# Vérifier si l'image existe
ls -lh /var/lib/libvirt/images/ubuntu-24.04-cloud.img
# Si absente, télécharger
sudo curl -L -o /var/lib/libvirt/images/ubuntu-24.04-cloud.img \
https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

Créez un fichier user-data pour configurer l’utilisateur SSH sur les VMs. Remplacez VOTRE_CLE_SSH_PUBLIQUE par le contenu de votre ~/.ssh/id_ed25519.pub :

user-data
#cloud-config
hostname: ${HOSTNAME}
manage_etc_hosts: true
users:
- name: kube
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, sudo
shell: /bin/bash
lock_passwd: true
ssh_authorized_keys:
- VOTRE_CLE_SSH_PUBLIQUE
packages:
- qemu-guest-agent
- python3
runcmd:
- swapoff -a
- sed -i '/swap/d' /etc/fstab
- systemctl enable qemu-guest-agent

Créez un fichier network-config pour chaque VM avec son IP statique :

network-config (exemple pour ks-cp1)
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses:
- 192.168.122.50/24
routes:
- to: default
via: 192.168.122.1
nameservers:
addresses:
- 192.168.122.1
- 8.8.8.8
  1. Générer les ISOs cloud-init pour chaque VM :

    Fenêtre de terminal
    # Pour ks-cp1
    cloud-localds ks-cp1-cloud-init.iso user-data-cp1 \
    --network-config network-config-cp1
    # Répéter pour ks-worker1 et ks-worker2 avec leurs fichiers respectifs
  2. Créer les disques avec backing file :

    Fenêtre de terminal
    for vm in ks-cp1 ks-worker1 ks-worker2; do
    sudo qemu-img create -f qcow2 \
    -b /var/lib/libvirt/images/ubuntu-24.04-cloud.img \
    -F qcow2 \
    /var/lib/libvirt/images/${vm}.qcow2 20G
    done
  3. Créer les VMs avec virt-install :

    Fenêtre de terminal
    for vm in ks-cp1 ks-worker1 ks-worker2; do
    sudo virt-install \
    --name ${vm} \
    --memory 2048 \
    --vcpus 2 \
    --disk /var/lib/libvirt/images/${vm}.qcow2 \
    --disk /var/lib/libvirt/images/${vm}-cloud-init.iso,device=cdrom \
    --os-variant ubuntu24.04 \
    --network network=default \
    --graphics none \
    --console pty,target_type=serial \
    --noautoconsole \
    --import
    done
  4. Vérifier la connectivité SSH :

    Fenêtre de terminal
    for ip in 192.168.122.50 192.168.122.51 192.168.122.52; do
    ssh -o StrictHostKeyChecking=accept-new kube@${ip} hostname
    done

Si les trois hostnames s’affichent (ks-cp1, ks-worker1, ks-worker2), vos VMs sont prêtes.

Utilisez toujours une version taggée pour la stabilité :

Fenêtre de terminal
git clone https://github.com/kubernetes-sigs/kubespray.git \
--depth 1 --branch v2.26.0
cd kubespray

Kubespray nécessite des versions spécifiques d’Ansible et de ses dépendances :

Fenêtre de terminal
# Créer un environnement virtuel isolé
python3 -m venv venv
source venv/bin/activate
# Installer les dépendances
pip install -U pip
pip install -r requirements.txt
# Vérifier Ansible
ansible --version

La sortie doit indiquer ansible [core 2.16.x] pour Kubespray v2.26.0.

Kubespray fournit un inventory sample que vous personnalisez :

Fenêtre de terminal
cp -r inventory/sample inventory/mycluster

Remplacez le contenu de inventory/mycluster/hosts.yaml par votre topologie :

inventory/mycluster/hosts.yaml
all:
hosts:
ks-cp1:
ansible_host: 192.168.122.50
ip: 192.168.122.50
access_ip: 192.168.122.50
ks-worker1:
ansible_host: 192.168.122.51
ip: 192.168.122.51
access_ip: 192.168.122.51
ks-worker2:
ansible_host: 192.168.122.52
ip: 192.168.122.52
access_ip: 192.168.122.52
children:
kube_control_plane:
hosts:
ks-cp1:
kube_node:
hosts:
ks-worker1:
ks-worker2:
etcd:
hosts:
ks-cp1:
k8s_cluster:
children:
kube_control_plane:
kube_node:
calico_rr:
hosts: {}

Avant le déploiement, vérifiez que Ansible peut se connecter à tous les nœuds :

Fenêtre de terminal
ansible all -i inventory/mycluster/hosts.yaml -m ping -u kube

Résultat attendu :

ks-cp1 | SUCCESS => { "ping": "pong" }
ks-worker1 | SUCCESS => { "ping": "pong" }
ks-worker2 | SUCCESS => { "ping": "pong" }

Avant de déployer, vous pouvez ajuster les paramètres dans inventory/mycluster/group_vars/.

Éditez inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml :

# Version Kubernetes (dans la plage supportée par votre release Kubespray)
kube_version: v1.30.4
# Nom DNS du cluster
cluster_name: cluster.local
VariableFichierValeur par défautDescription
kube_versionk8s-cluster.ymlv1.30.4Version Kubernetes
kube_network_plugink8s-cluster.ymlcalicoCNI — garder par défaut en lab
container_managerk8s-cluster.ymlcontainerdRuntime — containerd est le standard actuel
kube_proxy_modek8s-cluster.ymlipvsMode kube-proxy (iptables, ipvs)
cluster_namek8s-cluster.ymlcluster.localNom DNS du cluster
kube_pods_subnetk8s-cluster.yml10.233.64.0/18CIDR des pods
kube_service_addressesk8s-cluster.yml10.233.0.0/18CIDR des services

Le déploiement s’effectue avec le playbook cluster.yml. Comptez 10-20 minutes selon votre connexion Internet (téléchargement des images) :

Fenêtre de terminal
ansible-playbook -i inventory/mycluster/hosts.yaml cluster.yml \
-u kube -b --become-user=root

Le playbook exécute plusieurs phases :

  1. Préparation : configuration système, désactivation swap, modules kernel
  2. Téléchargement : binaires (kubectl, kubeadm, kubelet) et images conteneurs
  3. etcd : installation et configuration du cluster etcd
  4. Control plane : initialisation avec kubeadm, certificats
  5. Workers : jointure au cluster
  6. Réseau : déploiement de Calico (ou CNI choisi)
  7. Addons : CoreDNS, metrics-server (si activé)
PLAY RECAP *********************************************************************
ks-cp1 : ok=624 changed=135 unreachable=0 failed=0 skipped=1090
ks-worker1 : ok=413 changed=82 unreachable=0 failed=0 skipped=640
ks-worker2 : ok=413 changed=82 unreachable=0 failed=0 skipped=636

failed=0 sur tous les nœuds indique un déploiement réussi.

Le fichier kubeconfig se trouve sur le control plane. Récupérez-le sur votre poste :

Fenêtre de terminal
# Méthode simple avec scp (recommandée)
ssh kube@192.168.122.50 "sudo cat /etc/kubernetes/admin.conf" > ~/.kube/kubespray-config
# Remplacer l'adresse localhost par l'IP du control plane
sed -i 's/127.0.0.1/192.168.122.50/g' ~/.kube/kubespray-config
# Configurer kubectl
export KUBECONFIG=~/.kube/kubespray-config
Fenêtre de terminal
kubectl get nodes -o wide

Sortie attendue :

NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE RUNTIME
ks-cp1 Ready control-plane 19m v1.30.4 192.168.122.50 Ubuntu 24.04.3 LTS containerd://1.7.21
ks-worker1 Ready <none> 19m v1.30.4 192.168.122.51 Ubuntu 24.04.3 LTS containerd://1.7.21
ks-worker2 Ready <none> 19m v1.30.4 192.168.122.52 Ubuntu 24.04.3 LTS containerd://1.7.21

Les trois nœuds doivent être Ready.

Fenêtre de terminal
kubectl get pods -A

Tous les pods doivent être Running :

NamespacePods attendus
kube-systemcalico-node (3), calico-kube-controllers
kube-systemcoredns (2), dns-autoscaler
kube-systemkube-apiserver, kube-controller-manager, kube-scheduler
kube-systemkube-proxy (3), nodelocaldns (3)
kube-systemnginx-proxy (workers uniquement)

Validez que vous pouvez déployer une application :

Fenêtre de terminal
# Créer un déploiement nginx
kubectl create deployment nginx --image=nginx:alpine
# Exposer en NodePort
kubectl expose deployment nginx --port=80 --type=NodePort
# Attendre que le pod soit prêt
kubectl wait --for=condition=Ready pod -l app=nginx --timeout=60s
# Vérifier
kubectl get pods,svc

Résultat attendu :

NAME READY STATUS RESTARTS AGE
pod/nginx-6f564d4fd9-xxxxx 1/1 Running 0 30s
NAME TYPE CLUSTER-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 443/TCP 20m
service/nginx NodePort 10.233.33.101 80:30965/TCP 10s

Le service nginx est accessible sur http://<IP_WORKER>:30965.

Nettoyez après le test :

Fenêtre de terminal
kubectl delete deployment nginx
kubectl delete svc nginx

Le test nginx valide peu de choses. Pour vérifier la résolution DNS et la connectivité inter-pods :

Fenêtre de terminal
# Lancer un pod de test
kubectl run dnstest --image=busybox:1.36 --rm -it --restart=Never -- \
nslookup kubernetes.default.svc.cluster.local

Résultat attendu : une adresse IP dans le range 10.233.0.x (service ClusterIP).

Ansible ne peut pas se connecter aux VMs :

Fenêtre de terminal
# Vérifier la connectivité SSH
ssh -v kube@192.168.122.50
# Vérifier que la clé est autorisée
ssh kube@192.168.122.50 "cat ~/.ssh/authorized_keys"
# Vérifier que Python est installé
ansible all -i inventory/mycluster/hosts.yaml -m raw -a "which python3" -u kube

kubectl ne peut pas joindre l’API server :

Fenêtre de terminal
# Vérifier que l'adresse est correcte dans kubeconfig
grep server ~/.kube/kubespray-config
# Tester la connectivité réseau
nc -zv 192.168.122.50 6443
# Vérifier que l'API server tourne
ssh kube@192.168.122.50 "sudo crictl ps | grep kube-apiserver"

Généralement un problème de ressources ou de taints :

Fenêtre de terminal
# Décrire le pod pour voir les événements
kubectl describe pod <pod-name>
# Vérifier les ressources disponibles
kubectl describe nodes | grep -A5 "Allocated resources"
# Vérifier les taints
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints

Si le déploiement a échoué à mi-chemin, relancez simplement la même commande. Ansible est idempotent et reprendra où il s’est arrêté :

Fenêtre de terminal
ansible-playbook -i inventory/mycluster/hosts.yaml cluster.yml \
-u kube -b --become-user=root
  1. Provisionner la nouvelle VM avec cloud-init
  2. Ajouter l’entrée dans hosts.yaml sous kube_node
  3. Exécuter le playbook scale.yml :
Fenêtre de terminal
ansible-playbook -i inventory/mycluster/hosts.yaml scale.yml \
-u kube -b --become-user=root --limit=nouveau-worker

Kubespray fournit un playbook dédié pour la suppression propre :

Fenêtre de terminal
# Méthode recommandée : playbook remove-node
ansible-playbook -i inventory/mycluster/hosts.yaml remove-node.yml \
-u kube -b --become-user=root \
-e "node=ks-worker2"

Ce playbook effectue automatiquement le drain, la suppression des composants Kubernetes sur le nœud et le nettoyage. Après exécution, retirez manuellement le nœud de votre fichier hosts.yaml.

  1. Modifier kube_version dans group_vars/k8s_cluster/k8s-cluster.yml
  2. Exécuter le playbook upgrade-cluster.yml :
Fenêtre de terminal
ansible-playbook -i inventory/mycluster/hosts.yaml upgrade-cluster.yml \
-u kube -b --become-user=root

Pour supprimer complètement Kubernetes des nœuds :

Fenêtre de terminal
ansible-playbook -i inventory/mycluster/hosts.yaml reset.yml \
-u kube -b --become-user=root

Ce playbook :

  • Arrête et supprime tous les conteneurs
  • Supprime les binaires Kubernetes
  • Nettoie les règles iptables
  • Supprime les fichiers de configuration

Pour supprimer aussi les VMs :

Fenêtre de terminal
for vm in ks-cp1 ks-worker1 ks-worker2; do
sudo virsh destroy ${vm}
sudo virsh undefine ${vm} --remove-all-storage
done

Kubespray industrialise le déploiement de clusters Kubernetes :

  • Infrastructure as Code : votre cluster est défini dans des fichiers YAML versionnables
  • Reproductibilité : le même inventory produit le même cluster à chaque exécution
  • Automatisation : upgrades, scaling, suppression de nœuds via playbooks dédiés
  • Base solide : ce lab constitue un socle pour des déploiements plus robustes

Ce guide vous a permis de déployer un lab reproductible. Pour passer en production, il faut encore :

  • 3 control planes + load balancer devant l’API server
  • Backup etcd externalisé et testé régulièrement
  • Monitoring, alerting et logs centralisés
  • Sécurité réseau (NetworkPolicies, firewall configuré)
  • Plan de reprise d’activité (PRA) documenté

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.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn