Introduction
Kubernetes s'est imposé comme le standard de l'orchestration de conteneurs, mais sa réputation de complexité n'est pas usurpée : entre la distribution à choisir, le CNI (réseau), le stockage et l'ingress, il est facile de se perdre. L'objectif de cet article est de fournir une procédure reproductible pour amener un cluster de zéro à un état « prêt à recevoir des applications ».
Nous avons retenu RKE2 (la distribution Kubernetes durcie de Rancher/SUSE) pour sa simplicité d'installation et sa conformité sécurité par défaut, et Cilium comme CNI pour ses performances (eBPF) et sa capacité à remplacer kube-proxy.
Le périmètre couvert ici correspond au socle de l'infrastructure :
- Préparation des machines (OS)
- Installation du master (control-plane RKE2)
- Jonction des workers
- Installation du réseau (Cilium) - l'étape qui rend le cluster réellement opérationnel
À la fin de ces quatre étapes, tous les nœuds passent en Ready et le cluster est capable d'ordonnancer des workloads. Nous indiquons ensuite les briques qui viennent naturellement compléter ce socle (stockage, certificats, GitOps…).
Architecture cible
L'infrastructure de référence repose sur trois machines minimum, avec des IPs statiques :
Rôle Nom IP
Master (control-plane + etcd) kube_master 192.0.2.10
Worker 1 kube_w1 192.0.2.11
Worker 2 kube_w2 192.0.2.12
| Rôle | Nom | IP |
|---|---|---|
| Master (control-plane + etcd) | kube_master | 192.0.2.10 |
| Worker 1 | kube_w1 | 192.0.2.11 |
| Worker 2 | kube_w2 | 192.0.2.12 |
Le modèle est bien sûr extensible : il suffit d'ajouter des workers (worker-03, worker-04…) en répétant l'étape de jonction.
Point d'attention : Deux IPs libres dans le LAN sont réservées pour les services de type LoadBalancer annoncés par Cilium : 192.0.2.240/32 (pool interne) et 192.0.2.241/32 (pool WAN).
Les adresses IP, noms d'hôtes et domaines utilisés dans cet article sont des exemples (plage de documentation 192.0.2.0/24). Remplacez-les par les valeurs propres à votre environnement.
Pré-requis
Un accès SSH par clé vers chaque nœud
- Un utilisateur avec droits sudo (ici k8sadmin)
- Un accès Internet sortant depuis les machines (téléchargement des binaires et des charts Helm)
- Helm v4 installé sur la machine opérateur (voir la documentation officielle)
Étape 1 - Préparation des machines
Avant toute installation, chaque nœud doit être préparé de façon identique : disque étendu, service iSCSI activé (prérequis pour un futur stockage type Longhorn) et accès SSH par clé configurée.
Étendre la partition principale
À la création des VM, l'espace disque disponible n'est pas toujours utilisé en totalité :
sudo apt install cloud-guest-utils -y # outils pour redimensionner la partition
sudo growpart /dev/sda 1 # /dev/sda = disque, 1 = n° de partition
sudo resize2fs /dev/sda1 # étend le système de fichiers à l'espace récupéré
Selon le type de stockage (LVM par exemple), la commande peut différer :
lvextend -l +100%FREE -r /dev/mapper/ubuntu--vg-ubuntu--lv
Activer iSCSI
Composant nécessaire au bon fonctionnement de solutions de stockage comme Longhorn :
sudo apt install open-iscsi
sudo systemctl enable iscsid
Propager la clé SSH sur les nœuds
Depuis la machine opérateur :
Syntaxe : ssh-copy-id <utilisateur>@<IP-du-noeud>
Remplacez les IP ci-dessous par celles de VOS machines.
ssh-copy-id
ssh-copy-id
ssh-copy-id
Configurer un template SSH (optionnel)
On simplifie les connexions futures via `~/.ssh/config`. Notez l'hôte `kube_tunnel`, pratique pour exposer localement l'API server (port `6443`) :
"Host" = alias que VOUS choisissez ; "HostName" = l'IP réelle de la machine.
Host kube_master
HostName 192.0.2.10 # ← IP du master
User k8sadmin # ← utilisateur sudo présent sur la machine
Port 22
Host kube_tunnel # même machine que le master, mais dédié au tunnel API (port 6443)
HostName 192.0.2.10 # ← IP du master (identique à ci-dessus)
LocalForward 6443 localhost:6443
RequestTTY no
RemoteCommand sleep infinity
User k8sadmin
Port 22
Host kube_w1
HostName 192.0.2.11 # ← IP du worker 1
User k8sadmin
Port 22
Host kube_w2
HostName 192.0.2.12 # ← IP du worker 2
User k8sadmin
Port 22
Vérification
ssh kube_master whoami # → k8sadmin
ssh kube_master systemctl is-active iscsid # → active
Étape 2 - Installation du master (RKE2)
On installe le serveur RKE2 sur le premier nœud. Choix structurant : le master démarre avec `cni: none` et `disable-kube-proxy: true`, car Cilium (étape 4) assure lui-même le réseau et le remplacement de `kube-proxy`.
Point d'attention : Tant que Cilium n'est pas installé, le master reste NotReady. C'est normal et attendu. Le couple cni: none + disable-kube-proxy: true n'est viable que parce que Cilium (kubeProxyReplacement: true) prendra le relais.
Installation du binaire
ssh kube_master
curl -sfL https://get.rke2.io --output install.sh
sudo chmod +x install.sh
sudo ./install.sh
Configuration initiale
sudo mkdir -p /etc/rancher/rke2/
sudo tee /etc/rancher/rke2/config.yaml <<EOF
cni: "none"
disable:
rke2-ingress-nginx
disable-kube-proxy: true
write-kubeconfig-mode: "0644"
tls-san: # liste des adresses/noms par lesquels l'API du cluster sera jointe
"192.0.2.10" # ← IP du master (à remplacer par la vôtre)
"127.0.0.1"
"localhost"
etcd-snapshot-schedule-cron: "0 */12 * * *"
etcd-snapshot-retention: 5
EOF
On désactive volontairement l'ingress NGINX embarqué : l'ingress sera géré plus tard via Envoy Gateway (Gateway API). Les snapshots etcd sont planifiés toutes les 12 h avec 5 générations conservées.
Activation et démarrage
sudo systemctl enable rke2-server.service
sudo systemctl start rke2-server.service
Deux arborescences sont créées :
- `/var/lib/rancher` : binaires et état du cluster (etcd inclus)
- `/etc/rancher` : configuration effective du serveur
Accès à kubectl
# Persistance du PATH
echo 'export PATH=$PATH:/var/lib/rancher/rke2/bin' >> ~/.bashrc
export PATH=$PATH:/var/lib/rancher/rke2/bin
Configuration de kubectl
mkdir -p ~/.kube
sudo cp /etc/rancher/rke2/rke2.yaml ~/.kube/config
Vérification
kubectl get nodes
Résultat :
NAME STATUS ROLES AGE VERSION
k8smaster NotReady control-plane,etcd 5m v1.35.4+rke2r1
Étape 3 - Jonction des workers
Chaque worker installe RKE2 en mode agent, se connecte au master via son URL et un token de jonction, puis démarre son service.
Récupérer le token (sur le master)
sudo cat /var/lib/rancher/rke2/server/node-token
Point d'attention : Ce token est sensible : quiconque le possède peut rejoindre le cluster. Ne pas le committer, ne pas le rejouer après rotation sans précaution.
Installer l'agent (sur le worker 1)
ssh kube_w1
sudo curl -sfL https://get.rke2.io | sudo INSTALL_RKE2_TYPE="agent" sh -
sudo systemctl enable rke2-agent.service
sudo mkdir -p /etc/rancher/rke2/
sudo nano /etc/rancher/rke2/config.yaml
Contenu du fichier de configuration :
server: https://192.0.2.10:9345 # ← IP du MASTER (port 9345 = port de jonction RKE2)
token: <token> # ← coller ici le token récupéré à l'étape a.
node-name: "worker-01" # ← nom unique de CE worker (worker-02, worker-03…)
node-label:
"role=worker"
node-ip: "192.0.2.11" # ← IP de CE worker (celle de la machine courante)
cni: none
Deux IP différentes coexistent dans ce fichier : server pointe vers le master, tandis que node-ip est l'IP de la machine worker sur laquelle vous êtes en train de travailler. Ne les confondez pas.
Puis on démarre :
sudo systemctl start rke2-agent.service
Répéter pour les autres workers
La procédure est identique sur chaque worker. Seuls node-name et node-ip changent (worker-02 / 192.0.2.12, etc.).
Vérification - depuis le master :
kubectl get nodes
Tous les nœuds apparaissent, mais restent NotReady : le réseau n'existe toujours pas. C'est l'objet de l'étape suivante.
Étape 4 - Le réseau avec Cilium (CNI)
C'est l'étape charnière. Cilium joue ici un double rôle : il fournit le CNI et remplace kube-proxy (mode eBPF). Une fois installé, tous les nœuds passent Ready.
L'installation combine : le chart Helm cilium/cilium, un pool d'IPs L2 pour les services LoadBalancer, et une policy d'annonce L2 (sans BGP).
Tant que Cilium n'est pas déployé, rien ne fonctionne dans le cluster : ni DNS, ni services, ni ingress. C'est une étape bloquante pour toutes les suivantes.
Ajouter le dépôt Helm
helm repo add cilium https://helm.cilium.io/
helm repo update
Fichier de valeurs
Créer ciliumValues.yaml. Points clés : kubeProxyReplacement: true, chiffrement WireGuard du trafic inter-nœuds, et annonces L2 activées.
kubeProxyReplacement: true
k8sServiceHost: "192.0.2.10" # ← IP du master (API server)
k8sServicePort: "6443" # port standard de l'API Kubernetes
bpf:
masquerade: true
encryption:
enabled: true
type: wireguard
hubble:
enabled: true
relay:
enabled: true
ui:
enabled: true
ingress:
enabled: false
ingressController:
enabled: false
gatewayAPI:
enabled: false
ipam:
mode: kubernetes
bandwidthManager:
enabled: true
bbr: true
l2announcements:
enabled: true
authentication:
enabled: true
mutual:
spire:
enabled: true
Installer Cilium
helm install cilium cilium/cilium -n kube-system -f ciliumValues.yaml
Pool d'IPs pour les LoadBalancer
Créer CiliumLB.yaml - un pool interne, un pool WAN, et la policy d'annonce L2 :
#Pool interne : IP(s) distribuée(s) aux services LoadBalancer accessibles depuis le LAN
apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
name: ip-pool
namespace: kube-system
spec:
blocks:
cidr: "192.0.2.240/32" # ← IP libre de VOTRE réseau (ici une seule adresse, /32)
---
#Pool WAN : IP(s) réservée(s) aux services exposés vers l'extérieur
apiVersion: cilium.io/v2
kind: CiliumLoadBalancerIPPool
metadata:
name: ip-pool-wan
spec:
blocks:
cidr: 192.0.2.241/32 # ← autre IP libre de VOTRE réseau
disabled: false
---
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: l2-policy
namespace: kube-system
spec:
loadBalancerIPs: true
nodeSelector:
matchExpressions:
key: "kubernetes.io/hostname"
operator: Exists
kubectl apply -f CiliumLB.yaml
L'IP 192.0.2.240/32 est alors annoncée sur tous les nœuds via L2 (ARP/NDP), rendant les services LoadBalancer joignables depuis le LAN — le tout sans BGP.
Vérification
Tous les nœuds → Ready
kubectl get nodes
DaemonSets Cilium (un pod par nœud)
kubectl -n kube-system get ds -l k8s-app=cilium
Pools d'IPs
kubectl -n kube-system get ciliumloadbalancerippools
Annonces L2
kubectl -n kube-system get ciliuml2announcementpolicies
À ce stade, le cluster est fonctionnel : tous les nœuds sont Ready et peuvent ordonnancer des pods.
Pour aller plus loin : d'un cluster "Ready" à un cluster "operator-ready"
Un cluster Ready sait faire tourner des pods, mais une infrastructure de production a besoin de quelques briques complémentaires. Une fois le socle en place, l'ordre recommandé est le suivant :
| # | Brique | Rôle |
|---|---|---|
| 05 | local-path (StorageClass) | Provisioner de stockage de démarrage, avant Longhorn |
| 6 | metrics-server | kubectl top et autoscaling (HPA) |
| 7 | cert-manager | Émission automatique des certificats TLS |
| 8 | Reflector | Réplication de Secret/ConfigMap entre namespaces |
| 9 | Sealed Secrets | Chiffrement des secrets versionnés dans Git |
| 10 | ArgoCD | Bascule en GitOps (App-of-Apps) |
| 11 | Envoy Gateway | Ingress moderne via Gateway API |
L'objectif final est une infrastructure pilotée en GitOps : une fois ArgoCD installé et la bascule « App-of-Apps » effectuée, l'état du cluster est décrit dans un dépôt Git et réconcilié automatiquement.
Conclusion
En quatre étapes - préparation, master, workers, réseau - on obtient un cluster Kubernetes fonctionnel, sécurisé par défaut (RKE2) et doté d'un réseau performant (Cilium/eBPF).
La clé du succès tient en une phrase : respecter l'ordre des étapes.
Le passage des nœuds en NotReady puis Ready au fil de l'installation n'est pas un bug, c'est le signal que le pipeline se déroule comme prévu.
À partir de ce socle, l'ajout des briques de stockage, de certificats et de GitOps transforme progressivement le cluster en une plateforme d'exploitation complète.
Un projet Business Apps ? Nos développeurs Business Apps sont à votre écoute ! Contactez-nous !
