Initialisation de mon tout premier cluster HA Architecture Hostname RAM CPU DISQUE IP FONCTION k8s-master-1 4 go 2 60 SSD + 100 HDD (/opt) 192.168.1.101 etcd + control-plane k8s-master-2 4 go 2 60 SSD + 100 HDD (/opt) 192.168.1.102 etcd + control-plane k8s-master-3 4 go 2 60 SSD + 100 HDD (/opt)  192.168.1.103 etcd + control-plane k8s-worker-1 16 go 4 60 SSD + 100 HDD (/opt) 192.168.1.104 k8s-worker-2 16 go 4 60 SSD + 100 HDD (/opt) 192.168.1.105 k8s-worker-3 16 go 4 60 SSD + 100 HDD (/opt) 192.168.1.106 Tous les noeuds sont sur debian 13. Plan de déploiement du cluster : Préparation du système installation des prérequits conainer.io kubelet kubeadm kubectl Préparation du cluster HA (KubeVip) Initialisation du cluster Configuration du cluster Ajout des différents noeud au cluster Préparation du système Commandes à réaliser sur tous les noeuds 1) Désactivation de la swap swapoff -a Désactivation de la swap de manière pérenne :  sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab 2) Configuration du kernel Chargement au démarage des modules pour k8s :  cat < /dev/null apt update apt install -y containerd.io Configuration de containerd :  containerd config default | sudo tee /etc/containerd/config.toml Activation de containerd :  systemctl enable containerd Activation de l'emploie de systemd comme gestionnaire de Cgroup :  sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml Redémarage de Containerd : systemctl restart containerd Installation de kubernet Installation de kubelet kubeadm et kubectl : curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | gpg --dearmor | sudo tee /etc/apt/keyrings/kubernetes-apt-keyring.gpg > /dev/null sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt update sudo apt install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl Préparation du cluster HA (KubeVip) Sur tous les noeuds master UNIQUEMENT export VIP="192.168.1.100" export INTERFACE="ens18" Ici ens18 correspond à mon interface de prod et 192.168.1.100 correspondra à l'ip qui se "baladera" sur mes noeuds master selon leur disponiblité -> elle n'est pas fixée à un seul endroit. KVVERSION=$(curl -sL https://api.github.com/repos/kube-vip/kube-vip/releases/latest | jq -r ".name") echo "Version kube-vip : $KVVERSION" sudo mkdir -p /etc/kubernetes/manifests/ # Pull de l'image kube-vip sudo ctr image pull ghcr.io/kube-vip/kube-vip:$KVVERSION # Génération du manifeste Static Pod sudo ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip \ /kube-vip manifest pod \ --interface $INTERFACE \ --address $VIP \ --controlplane \ --services \ --arp \ --leaderElection | sudo tee /etc/kubernetes/manifests/kube-vip.yaml Initialisation du cluster Sur le noeuds k8s-master-1 UNIQUEMENT Application "à la main" de l'ip vip le temps qu'elle soit gérée par K8S :  sudo ip addr add 192.168.1.100/32 dev ens18 Initialisation du cluster : sudo kubeadm init \ --control-plane-endpoint "192.168.1.100:6443" \ --upload-certs \ --pod-network-cidr=10.10.0.0/16 sudo kubeadm init phase upload-certs --upload-certs Configurer kubectl pour l'utilisateur courant : mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config  Vérification du bon déploiement du cluster "basique" :  kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE etcd-k8s-master-1 1/1 Running 0 4m57s kube-apiserver-k8s-master-1 1/1 Running 0 4m57s kube-controller-manager-k8s-master-1 1/1 Running 0 4m57s kube-scheduler-k8s-master-1 1/1 Running 0 4m57s kube-vip-k8s-master-1 1/1 Running 0 4m57s Déploiement du réseau Calico  kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml Attendre que l'opérateur ait fini : kubectl rollout status deployment tigera-operator -n tigera-operator Déployer les ressources Calico : kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml Définir le sous réseau de K8S :  kubectl patch installation default --type=merge -p '{"spec": {"calicoNetwork": {"ipPools": [{"cidr": "10.10.0.0/16", "encapsulation": "VXLANCrossSubnet", "natOutgoing": "Enabled", "nodeSelector": "all()"}]}}}' Suvi des états :  watch kubectl get pods -n calico-system Configuration du cluster Désactivation de l'ip VIP "manuelle" : sudo ip addr del 192.168.1.100/32 dev ens18 Ajout de l'ip vp dans le configmap de kubeadmin : kubectl edit configmap kubeadm-config -n kube-system apiVersion: v1 data: ClusterConfiguration: | apiServer: {} apiVersion: kubeadm.k8s.io/v1beta4 caCertificateValidityPeriod: 87600h0m0s certificateValidityPeriod: 8760h0m0s certificatesDir: /etc/kubernetes/pki clusterName: kubernetes controllerManager: {} --> controlPlaneEndpoint: "192.168.1.100:6443" Ici on ajoute uniquement "controlPlaneEndpoint. kubeadm init phase upload-certs --upload-certs Application du label control-plane à k8s-master-1 kubectl label node k8s-master-1 node-role.kubernetes.io/control-plane= Génération de la commande permettant d'ajouter des noeuds masters : sudo kubeadm token create --print-join-command --certificate-key $(sudo kubeadm init phase upload-certs --upload-certs | tail -1) Génération de la commande permettant d'ajouter des noeuds worker : kubeadm token create --print-join-command Ajout des différents noeuds master au cluster Commandes à exécuter sur les noeuds k8s-master-2 et k8s-master-3 kubeadm join 192.168.1.100:6443 --token ld3z42.3n2zglgdtoyd2to2 --discovery-token-ca-cert-hash sha256:03d97c70e97b300bb1088fa63f1005d52dcf45d813e6ec535897f2151c8a61c2 --control-plane --certificate-key c9413180b64c612ee58713da91a4d9b77e95bee06b424aa500ebb4b16fb7769d Ajout des différents noeuds worker au cluster Commandes à exécuter sur les noeuds k8s-worker-1, k8s-worker-2 et k8s-worker-3 // Envoyer la commande générée par kubeadm token create --print-join-command Résultat :  kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master-1 Ready control-plane 81m v1.32.12 k8s-master-2 Ready control-plane 6m13s v1.32.12 k8s-master-3 Ready control-plane 4m8s v1.32.12 k8s-worker-1 Ready 2m3s v1.32.12 k8s-worker-2 Ready 115s v1.32.12 k8s-worker-3 Ready 105s v1.32.12 Félicitation ! Vous venez de déployer votre tout premier cluster Kubernettes ! Déployer une solution de stockage Installation des prérequits sur TOUS LES NOEUDS :   sudo apt-get update sudo apt-get install -y open-iscsi nfs-common sudo systemctl enable --now iscsid Installation de Helm sur le master 1 : curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 bash get_helm.sh Préparation et déploiement de Longhorn : helm repo add longhorn https://charts.longhorn.io helm repo update Dans le fichier : /root/longhorn-values.yaml : defaultSettings: defaultDataPath: "/opt/longhorn" Déploiement de longhorn :  helm install longhorn longhorn/longhorn \ --namespace longhorn-system \ --create-namespace \ -f /root/longhorn-values.yaml Résultat :  LAST DEPLOYED: Wed Apr 15 22:49:57 2026 NAMESPACE: longhorn-system STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: Longhorn is now installed on the cluster! Please wait a few minutes for other Longhorn components such as CSI deployments, Engine Images, and Instance Managers to be initialized. Visit our documentation at https://longhorn.io/docs/ Vérifications :  kubectl get pods -n longhorn-system NAME READY STATUS RESTARTS AGE csi-attacher-5bcb65bf95-44fv7 1/1 Running 1 (66s ago) 2m27s csi-attacher-5bcb65bf95-jvqxj 1/1 Running 0 2m27s csi-attacher-5bcb65bf95-pgf8h 1/1 Running 0 2m27s csi-provisioner-5d498c6944-d65sl 1/1 Running 0 2m27s csi-provisioner-5d498c6944-dfl96 1/1 Running 0 2m27s csi-provisioner-5d498c6944-wvmvs 1/1 Running 0 2m27s csi-resizer-6c6474c996-9k94v 1/1 Running 0 2m26s csi-resizer-6c6474c996-mjtsj 1/1 Running 0 2m27s csi-resizer-6c6474c996-t94bs 1/1 Running 0 2m26s csi-snapshotter-5cc5fb45d9-pxz5r 1/1 Running 0 2m26s csi-snapshotter-5cc5fb45d9-swr6x 1/1 Running 0 2m26s csi-snapshotter-5cc5fb45d9-wsdh6 1/1 Running 0 2m26s engine-image-ei-75a03ec3-8r6p4 1/1 Running 0 3m23s engine-image-ei-75a03ec3-nmbdp 1/1 Running 0 3m23s engine-image-ei-75a03ec3-tl4x2 1/1 Running 0 3m23s engine-image-ei-75a03ec3-vcq2q 1/1 Running 0 3m23s instance-manager-8904e58b7c0d881b70d813ddd4fe86f4 1/1 Running 0 2m54s instance-manager-9176dd8e42515fe6146f445c7a1bc3ad 1/1 Running 0 2m53s instance-manager-9805597396c5db2820496e3f125bc6bc 1/1 Running 0 2m53s instance-manager-eaeed3ca06747e29c0f3aa8b9557c5c8 1/1 Running 0 2m53s longhorn-csi-plugin-7vtx8 3/3 Running 0 2m26s longhorn-csi-plugin-brx6d 3/3 Running 0 2m26s longhorn-csi-plugin-d4fxc 3/3 Running 0 2m26s longhorn-csi-plugin-tsrrg 3/3 Running 0 2m26s longhorn-driver-deployer-746f54969f-9gmlb 1/1 Running 0 4m5s longhorn-manager-b7q7k 2/2 Running 0 4m5s longhorn-manager-fk6f4 2/2 Running 0 4m5s longhorn-manager-xghbf 2/2 Running 0 4m5s longhorn-manager-xt4xq 2/2 Running 0 4m5s longhorn-ui-5df99fc477-7zt5s 1/1 Running 0 4m5s longhorn-ui-5df99fc477-tzwdp 1/1 Running 0 4m5s Accéder à l'interface graphique : kubectl patch svc longhorn-frontend -n longhorn-system -p '{"spec": {"type": "NodePort"}}' Afficher le port : kubectl get svc longhorn-frontend -n longhorn-system On va utiliser le port : NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE longhorn-frontend NodePort 10.102.85.80 80:32555/TCP 6m22s Exemple ici : http://192.168.1.104:32555/#/dashboard On peut utiliser n'importe quelle IP du cluster. Tester le stockage :  Déclaration du PVC : hello-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: longhorn-hello-pvc spec: accessModes: - ReadWriteOnce storageClassName: longhorn # On utilise le driver Longhorn resources: requests: storage: 1Gi # 1 Go c'est largement assez pour un test Création d'un conteneur utilisant le PVC : hello-app.yaml apiVersion: v1 kind: Pod metadata: name: hello-longhorn spec: containers: - name: hello-world image: busybox # On écrit la date dans /data/coucou.txt en boucle command: ["sh", "-c", "while true; do date >> /data/coucou.txt; echo 'Donnée écrite !'; sleep 5; done"] volumeMounts: - name: storage-volume mountPath: /data volumes: - name: storage-volume persistentVolumeClaim: claimName: longhorn-hello-pvc Application des changements : kubectl apply -f hello-pvc.yaml kubectl apply -f hello-app.yaml Côté interface graphique : Suppression du pod :  kubectl delete pod hello-longhorn Suppression du pvc :  kubectl delete pvc longhorn-hello-pvc