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

Haute disponibilité du Control Plane Kubernetes

15 min de lecture

Un cluster Kubernetes à un seul control plane est un SPOF (Single Point of Failure). Si le nœud master tombe, plus personne ne peut déployer, scaler ou gérer les workloads — même si les applications continuent de tourner. La haute disponibilité (HA) du control plane élimine ce risque en répartissant les composants critiques sur plusieurs nœuds.

Ce guide couvre l’architecture HA pour la certification CKA et les environnements de production.

  • Pourquoi un control plane unique est un risque
  • Les deux topologies HA : stacked etcd vs external etcd
  • Comment configurer un cluster HA avec kubeadm
  • Le rôle du load balancer devant l’API Server
  • Les scénarios de failover et leur gestion

Dans un cluster standard, le control plane (un seul nœud) héberge :

ComposantRôleImpact si indisponible
kube-apiserverPoint d’entrée de toutes les requêtesAucune commande kubectl ne fonctionne
etcdStocke l’état du clusterPerte de configuration, impossible de créer des ressources
kube-schedulerPlace les Pods sur les nœudsLes nouveaux Pods restent Pending
kube-controller-managerGère les Deployments, ReplicaSets…Pas de réconciliation, pas d’autoscaling

Avec 3 control planes (minimum recommandé) :

  • Tolérance aux pannes : 1 nœud peut tomber sans impact
  • Continuité des opérations : kubectl fonctionne toujours
  • Maintenance sans interruption : upgrade un nœud à la fois
  • Résilience etcd : quorum maintenu avec 2 nœuds sur 3

Kubernetes supporte deux architectures pour la haute disponibilité :

Chaque control plane héberge à la fois les composants Kubernetes et etcd :

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Control Plane 1│ │ Control Plane 2│ │ Control Plane 3│
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ kube-apiserver │ │ kube-apiserver │ │ kube-apiserver │
│ kube-scheduler │ │ kube-scheduler │ │ kube-scheduler │
│ controller-mgr │ │ controller-mgr │ │ controller-mgr │
│ etcd │ │ etcd │ │ etcd │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└────────────────────┼────────────────────┘
┌─────────┴─────────┐
│ Load Balancer │
│ (API Server) │
└─────────┬─────────┘
┌───────────────┼───────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ Worker 1 │ │ Worker 2 │ │ Worker 3 │
└───────────┘ └───────────┘ └───────────┘

Avantages :

  • Plus simple à déployer et maintenir
  • Moins de nœuds nécessaires
  • Configuration kubeadm standard

Inconvénients :

  • Perte d’un control plane = perte d’un membre etcd
  • Couplage fort entre composants

Le cluster etcd est séparé des control planes Kubernetes :

┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ etcd 1 │ │ etcd 2 │ │ etcd 3 │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
└─────────────────┼─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Control Plane 1│ │ Control Plane 2│ │ Control Plane 3│
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ kube-apiserver │ │ kube-apiserver │ │ kube-apiserver │
│ kube-scheduler │ │ kube-scheduler │ │ kube-scheduler │
│ controller-mgr │ │ controller-mgr │ │ controller-mgr │
└─────────────────┘ └─────────────────┘ └─────────────────┘

Avantages :

  • Isolation des pannes (etcd indépendant)
  • Plus de flexibilité pour le scaling

Inconvénients :

  • Plus complexe à déployer
  • Plus de nœuds à gérer (6 minimum)
  • 3 nœuds control plane (2 CPU, 2 Go RAM — minimum de lab, pas sizing production)
  • 1 load balancer redondant devant les API Servers (HAProxy + keepalived, Nginx, ou cloud LB)
  • Connectivité réseau entre tous les nœuds
  • Ports ouverts : 6443 (API), 2379-2380 (etcd), 10250-10259 (kubelet/components)
  • kubeadm, kubelet, kubectl installés sur tous les nœuds
  • Container runtime (containerd recommandé)
  • Même version Kubernetes sur tous les nœuds

Le load balancer est indispensable pour distribuer les requêtes vers les API Servers.

Créez /etc/haproxy/haproxy.cfg sur le nœud load balancer :

global
log /dev/log local0
maxconn 2000
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
option tcplog
option dontlognull
timeout connect 5s
timeout client 50s
timeout server 50s
frontend kubernetes-api
bind *:6443
default_backend kubernetes-masters
backend kubernetes-masters
balance roundrobin
option tcp-check
server master1 192.168.1.10:6443 check fall 3 rise 2
server master2 192.168.1.11:6443 check fall 3 rise 2
server master3 192.168.1.12:6443 check fall 3 rise 2

Démarrez HAProxy :

Fenêtre de terminal
sudo systemctl enable --now haproxy

Vérification :

Fenêtre de terminal
# Testez la connexion au load balancer
nc -zv <LOAD_BALANCER_IP> 6443

Vérification avancée :

Fenêtre de terminal
# Tester que l'API Kubernetes répond vraiment
curl -k https://<LB_IP>:6443/readyz
  1. Préparez le fichier de configuration kubeadm

    Créez kubeadm-config.yaml :

    apiVersion: kubeadm.k8s.io/v1beta4
    kind: ClusterConfiguration
    kubernetesVersion: v1.35.2
    controlPlaneEndpoint: "LOAD_BALANCER_IP:6443"
    networking:
    podSubnet: "10.244.0.0/16"
    ---
    apiVersion: kubeadm.k8s.io/v1beta4
    kind: InitConfiguration
    localAPIEndpoint:
    advertiseAddress: "192.168.1.10"
    bindPort: 6443
  2. Initialisez le cluster

    Fenêtre de terminal
    sudo kubeadm init --config=kubeadm-config.yaml --upload-certs

    L’option --upload-certs chiffre et stocke les certificats dans un Secret Kubernetes pour que les autres control planes puissent les récupérer.

  3. Notez les commandes de join

    La sortie affiche deux commandes :

    • Une pour joindre d’autres control planes
    • Une pour joindre des workers
    # Pour les control planes
    kubeadm join LOAD_BALANCER_IP:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash> \
    --control-plane --certificate-key <key>
    # Pour les workers
    kubeadm join LOAD_BALANCER_IP:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash>
  4. Configurez kubectl

    Fenêtre de terminal
    mkdir -p $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
  5. Installez un CNI

    Flannel est une option simple pour commencer. Vous pouvez aussi choisir Calico, Cilium ou un autre CNI selon vos besoins :

    Fenêtre de terminal
    # Exemple avec Flannel
    kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

Sur chaque control plane additionnel (master2, master3) :

Fenêtre de terminal
sudo kubeadm join LOAD_BALANCER_IP:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key>

Vérification :

Fenêtre de terminal
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master1 Ready control-plane 10m v1.35.2
master2 Ready control-plane 5m v1.35.2
master3 Ready control-plane 2m v1.35.2
Fenêtre de terminal
kubectl get nodes -o wide
Fenêtre de terminal
# Sur un control plane
sudo etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list -w table
+------------------+---------+---------+------------------------+------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+---------+------------------------+------------------------+
| 8e9e05c52164694d | started | master1 | https://192.168.1.10:2380 | https://192.168.1.10:2379 |
| 91bc3c398fb3c146 | started | master2 | https://192.168.1.11:2380 | https://192.168.1.11:2379 |
| a3d22c71c6ed4a38 | started | master3 | https://192.168.1.12:2380 | https://192.168.1.12:2379 |
+------------------+---------+---------+------------------------+------------------------+
Fenêtre de terminal
# Endpoints modernes (componentstatuses est déprécié)
kubectl get --raw='/readyz?verbose'
kubectl get --raw='/livez?verbose'
  1. Arrêtez un control plane

    Fenêtre de terminal
    # Sur master2
    sudo systemctl stop kubelet
  2. Vérifiez que le cluster fonctionne toujours

    Fenêtre de terminal
    kubectl get nodes
    kubectl create deployment test --image=nginx
    kubectl get pods
  3. Redémarrez le nœud

    Fenêtre de terminal
    sudo systemctl start kubelet
PannesetcdAPI ServerSchedulerImpact
0/3✅ QuorumFonctionnement normal
1/3✅ Quorum (2/3)Fonctionnement normal
2/3❌ Pas de quorum⚠️ DégradéAucun changement d’état possible
3/3Cluster inutilisable

Procédez un nœud à la fois :

Fenêtre de terminal
# 1. Drain le nœud
kubectl drain master1 --ignore-daemonsets --delete-emptydir-data
# 2. Upgrade kubeadm (remplacez X.Y.Z par la version cible)
sudo apt-get update && sudo apt-get install -y kubeadm=X.Y.Z-*
# 3. Appliquez l'upgrade
sudo kubeadm upgrade apply vX.Y.Z # Premier control plane uniquement
# ou
sudo kubeadm upgrade node # Autres control planes
# 4. Upgrade kubelet et kubectl
sudo apt-get install -y kubelet=X.Y.Z-* kubectl=X.Y.Z-*
sudo systemctl daemon-reload && sudo systemctl restart kubelet
# 5. Uncordon le nœud
kubectl uncordon master1

Si un control plane est définitivement perdu :

  1. Retirez le membre etcd

    Fenêtre de terminal
    # Trouvez l'ID du membre
    sudo etcdctl member list
    # Supprimez-le
    sudo etcdctl member remove <MEMBER_ID>
  2. Supprimez le nœud du cluster

    Fenêtre de terminal
    kubectl delete node <failed-node>
  3. Configurez un nouveau nœud

    Générez un nouveau token et rejoignez :

    Fenêtre de terminal
    kubeadm token create --print-join-command
    kubeadm init phase upload-certs --upload-certs
  4. Joignez le nouveau control plane

    Fenêtre de terminal
    kubeadm join LOAD_BALANCER_IP:6443 --token <token> \
    --discovery-token-ca-cert-hash sha256:<hash> \
    --control-plane --certificate-key <key>

Le join échoue avec “certificate has expired”

Section intitulée « Le join échoue avec “certificate has expired” »

Le certificate-key expire après 2 heures. Régénérez-le :

Fenêtre de terminal
sudo kubeadm init phase upload-certs --upload-certs

Vérifiez que les certificats sont présents et valides :

Fenêtre de terminal
ls -la /etc/kubernetes/pki/etcd/
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -text -noout | grep -A2 Validity

Vérifiez :

  1. Le load balancer est accessible : nc -zv <LB_IP> 6443
  2. Au moins un API Server répond : curl -k https://<MASTER_IP>:6443/healthz
  3. Les health checks HAProxy fonctionnent
  1. 3 control planes minimum pour la haute disponibilité
  2. Load balancer obligatoire devant les API Servers
  3. Topologie stacked etcd = plus simple, recommandée pour la CKA
  4. Quorum etcd = majorité de membres nécessaire
  5. Upgrade un nœud à la fois pour maintenir la disponibilité
  6. --upload-certs facilite l’ajout de control planes
  7. Les workloads continuent même si le control plane tombe

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