Skip to content
Learni
View all tutorials
Raspberry Pi

Comment déployer un cluster Kubernetes sur Raspberry Pi en 2026

Introduction

En 2026, les Raspberry Pi 5 avec leur SoC ARM64 BCM2712 (2.4 GHz quad-core) sont idéaux pour des clusters Kubernetes en homelab ou edge computing. Ce tutoriel advanced vous guide pour déployer un cluster haute disponibilité (HA) à 3 nœuds maîtres + 2 workers, utilisant kubeadm, containerd comme CRI et MetalLB pour load balancing. Pourquoi c'est crucial ? Kubernetes sur ARM réduit les coûts (RPi5 ~100€/nœud vs serveurs x86) tout en gérant 100+ pods avec monitoring Prometheus. On évite les pièges ARM comme les images non-multiarch. À la fin, vous déployez une app Nginx scalable. Temps estimé : 2h. Scalable à 10+ nœuds. Préparez 5 RPi5, Ethernet stable (WiFi instable pour K8s).

Prérequis

  • 5x Raspberry Pi 5 (8GB RAM min., microSD 64GB classe A2)
  • Raspberry Pi OS Lite 64-bit (bookworm, mise à jour 2026.01+)
  • Réseau Gigabit Ethernet privé (192.168.1.0/24), SSH activé
  • Clés SSH sans mot de passe entre nœuds (ansible-ready)
  • sudo sans mot de passe pour pi user
  • Kubernetes v1.32+ (ARM64 compatible)
  • Outils : curl, jq, apt-transport-https

Mise à jour et installation des dépendances communes

prepare-nodes.sh
#!/bin/bash
set -euo pipefail

# Exécuter sur TOUS les nœuds (masters + workers)
apt update && apt upgrade -y
apt install -y curl apt-transport-https ca-certificates gnupg lsb-release jq

# Désactiver swap (critique pour kubelet)
swapoff -a
sed -i '/ swap / s/^/#/' /etc/fstab

# Charger modules kernel pour networking CNI
cat <<EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter

# Config sysctl pour Kubernetes
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system

reboot

Ce script prépare tous les nœuds en désactivant le swap (kubelet refuse sinon), charge les modules pour CNI (comme Flannel), et configure sysctl pour le forwarding IP. Exécutez-le via Ansible ou manuellement sur chaque RPi. Le reboot final assure la persistance. Piège : oublier sysctl --system cause des échecs de pod networking.

Installation du runtime conteneur (containerd)

Containerd est le CRI recommandé pour K8s sur ARM en 2026 (léger, native CRI). On l'installe depuis les repos officiels pour éviter les versions obsolètes d'Apt.

Installation et config de containerd

install-containerd.sh
#!/bin/bash
set -euo pipefail

# Sur TOUS les nœuds
apt install -y containerd.io=1.7.*

# Config containerd pour CRI (sandbox_image = pause:3.10)
mkdir -p /etc/containerd
containerd config default | sed 's/SystemdCgroup = false/SystemdCgroup = true/' | tee /etc/containerd/config.toml

# Pull image pause ARM64
ctr image pull registry.k8s.io/pause:3.10

systemctl restart containerd
systemctl enable containerd

# Vérif
ctr version | head -1
grep -q 'SystemdCgroup = true' /etc/containerd/config.toml && echo 'Config OK'

Ce script installe containerd 1.7 (stable 2026), active cgroup v2 systemd (requis K8s 1.32+), et précharge pause:3.10 ARM64. sed corrige le défaut cgroup. Vérifiez avec ctr version. Piège ARM : images x86 échouent ; toujours spécifier ARM64. Containerd > Docker pour perf sur RPi (moins RAM).

Installation des composants Kubernetes

Seuls les masters exécutent kubeadm init. Workers : kubelet/kubectl seulement. Utilisez la version exacte pour éviter incompatibilités.

Installation kubeadm, kubelet et kubectl

install-kube.sh
#!/bin/bash
set -euo pipefail

KUBEVER="v1.32.2"

# Ajout repo Kubernetes (ARM64 auto)
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBEVER/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBEVER/deb/ /' | tee /etc/apt/sources.list.d/kubernetes.list

apt update
apt install -y kubelet=$KUBEVER kubeadm=$KUBEVER kubectl=$KUBEVER
apt-mark hold kubelet kubeadm kubectl

systemctl enable --now kubelet
kubectl version --client

Installe K8s v1.32.2 depuis pkgs.k8s.io (officiel post-deprecation apt.kubernetes.io). hold empêche upgrades auto. Client version confirme ARM64 build. Exécutez sur tous nœuds. Piège : vieilles clés GPG causent 404 ; toujours régénérer.

Initialisation du nœud maître (kubeadm init HA)

init-master.sh
#!/bin/bash
set -euo pipefail

POD_CIDR="10.244.0.0/16"  # Pour Flannel
CONTROL_PLANE_ENDPOINT="192.168.1.100:6443"  # Load balancer virtuel (VIP)

# Reset si besoin
kubeadm reset -f

# Init avec CRI=containerd, HA stackrox (cert-manager like)
kubeadm init \
  --pod-network-cidr $POD_CIDR \
  --control-plane-endpoint $CONTROL_PLANE_ENDPOINT \
  --cri-socket unix:///run/containerd/containerd.sock \
  --upload-certs \
  --certificate-key \
  $(kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1) \
  --kubernetes-version v1.32.2

# Post-init
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

# Taint removal pour worker-like master
kubectl taint nodes --all node-role.kubernetes.io/control-plane-

kubectl get nodes
kubeadm token create --print-join-command

Exécuter UNIQUEMENT sur PREMIER MASTER (192.168.1.100). --upload-certs pour HA (autres masters join certs). Sauvegardez le kubeadm join output pour workers. Taint removal permet pods sur masters. Piège : mauvais --control-plane-endpoint brise HA ; utilisez VIP (keepalived plus loin).

Ajout des nœuds workers et masters secondaires

Copiez le kubeadm join --token ... --discovery-token-ca-cert-hash ... --control-plane --certificate-key ... du master. Exécutez sur workers : kubeadm join .... Sur seconds masters : ajoutez --control-plane.

Script générique pour join nœuds

join-node.sh
#!/bin/bash
set -euo pipefail

# Remplacer par output de kubeadm token create --print-join-command
JOIN_CMD="kubeadm join 192.168.1.100:6443 --token abcdef.1234567890abcdef \
    --discovery-token-ca-cert-hash sha256:1234..abcd \
    --cri-socket unix:///run/containerd/containerd.sock"

# Pour worker : $JOIN_CMD
# Pour master HA : $JOIN_CMD --control-plane --certificate-key XYZ...

kubeadm reset -f
$JOIN_CMD

# Sur master principal, vérifier
kubectl get nodes -o wide

Adaptez $JOIN_CMD depuis master. Pour 2nd/3e master, ajoutez --control-plane --certificate-key (du init). kubectl get nodes montre Ready après ~2min. Piège : token expire 24h ; recréez avec kubeadm token create. Tous nœuds Ready = cluster up.

Déploiement CNI et load balancer

Flannel pour CNI simple (Calico plus lourd sur RPi). MetalLB pour LB externe (NodePorts sinon).

Installation Flannel CNI et MetalLB

deploy-cni-metallb.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: flannel
  namespace: kube-system
data:
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": { "Type": "vxlan" }
    }
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {"type": "flannel"},
        {"type": "portmap"},
        {"type": "bandwidth"},
        {"type": "firewall"}
      ]
    }
---
# MetalLB (LB pour services)
apiVersion: v1
kind: Namespace
metadata:
  name: metallb-system
---
apiVersion: v1
kind: Secret
metadata:
  namespace: metallb-system
  name: memberlist
stringData:
  secretkey: "VeryRandomString12345678=="
---
# Appliquer : kubectl apply -f deploy-cni-metallb.yaml

kubectl apply -f ce-fichier depuis master. Flannel DaemonSet assigne IPs pods (vxlan tunnel efficace ARM). MetalLB utilise pool IP 192.168.1.200-250 (configurez votre réseau). Vérif : kubectl get pods -n metallb-system. Piège : mauvais pool IP cause 'Pending' services.

Déploiement app exemple scalable (Nginx + HPA)

nginx-hpa.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.27-alpine
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 200m
            memory: 256Mi
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deploy
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

kubectl apply -f nginx-hpa.yaml. Déploie Nginx avec HPA sur CPU 70% (scale 3-10 pods). Service LoadBalancer expose via MetalLB (IP ~192.168.1.200). Ressources limitent à RPi perf. Test : curl , stress kubectl run load -i --rm --image=busybox --restart=Never -- wget -qO- . Piège : sans limits, OOMKills crash RPi.

Bonnes pratiques

  • VIP HA : Installez keepalived sur masters (apt install keepalived, VRRP pour 192.168.1.100).
  • Monitoring : Déployez Prometheus + Grafana (kube-prom-stack helm chart ARM).
  • Backup : velero pour etcd snapshots (kubeadm certs renew mensuel).
  • Sécurité : RBAC strict, NetworkPolicy, PodSecurityStandards admission.
  • Perf RPi : Overclock CPU à 2.8GHz, heatsinks actifs, USB SSD boot.

Erreurs courantes à éviter

  • Swap activé : Kubelet refuse start (journalctl -u kubelet montre erreur).
  • Images x86 : Pods CrashLoopBackOff ; forcez imagePullPolicy: Always + multiarch.
  • CNI manquant : Pods Pending forever ; vérifiez kubectl get pods -n kube-system.
  • Port 6443 firewall : Joins échouent ; ufw allow 6443/tcp ou iptables-off.

Pour aller plus loin

  • Docs officielles : Kubernetes ARM
  • Helm pour apps : curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
  • Longhorn CSI pour stockage persistant.
  • Découvrez nos formations Learni DevOps pour Kubernetes certifiées CKAD.