Skip to main content

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 <<EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

Chargement immédiat des modules pour k8s : 

modprobe overlay
modprobe br_netfilter

Le module overlay permet le fonctionnement du système de fichier OverlayFS.
Le module br_netfiltrer permet d'appliquer les iptables à un bridge.

Configuration des paramètres réseaux :

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

Permet aux bridges de faire appel au FW iptables
Permet d'active le routage

Application des paramètres : 

sysctl --system

installation des prérequits

Commandes à réaliser sur tous les noeuds

Installation des packets courrants :
apt-get update 
apt-get install -y ca-certificates curl gnupg lsb-release htop jq tcpdump ethtool
Installation de containerd.io
install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg

chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/debian bookworm stable" \
  | tee /etc/apt/sources.list.d/docker.list > /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    <none>          2m3s    v1.32.12
k8s-worker-2   Ready    <none>          115s    v1.32.12
k8s-worker-3   Ready    <none>          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   <none>        80:32555/TCP   6m22s

Exemple ici : http://192.168.1.104:32555/#/dashboard

On peut utiliser n'importe quelle IP du cluster.

image.png

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 :

image.png

Suppression du pod : 

kubectl delete pod hello-longhorn

Suppression du pvc : 

kubectl delete pvc longhorn-hello-pvc