Déployer un cluster K8s on-premise avec kubeadm et Ansible
Monter un cluster Kubernetes "bare-metal" sur Proxmox, c'est le meilleur moyen de comprendre vraiment ce que fait EKS à ta place. Voici comment j'ai bootstrappé un cluster 3 nœuds avec kubeadm, le tout automatisé via Ansible.
Le contexte
Dans le cadre de mon projet fil rouge, j'avais besoin d'un cluster K8s reproductible, jetable et 100% IaC. Objectif : pouvoir le détruire et le recréer en moins de 30 minutes sans jamais toucher à la souris. L'infrastructure sous-jacente : 3 VMs Proxmox (1 control plane + 2 workers), Ubuntu Server 22.04, 4 vCPU / 8 GB RAM chacune.
L'architecture cible
Le playbook Ansible, étape par étape
Le playbook est découpé en 4 rôles réutilisables :
common: prérequis système (swap off, modules kernel, sysctl)containerd: runtime de conteneurs + config cgroupskubernetes: installkubelet,kubeadm,kubectlcluster-init:kubeadm initsur le master,joinsur les workers
1. Préparer le système (rôle common)
# roles/common/tasks/main.yml
- name: Disable swap
ansible.builtin.command: swapoff -a
when: ansible_swaptotal_mb > 0
- name: Load kernel modules
community.general.modprobe:
name: "{{ item }}"
state: present
loop:
- overlay
- br_netfilter
- name: Sysctl for K8s networking
ansible.posix.sysctl:
name: "{{ item }}"
value: "1"
state: present
reload: yes
loop:
- net.bridge.bridge-nf-call-iptables
- net.ipv4.ip_forward
/etc/fstab, kubelet refusera de démarrer après le prochain reboot. Ça m'a coûté 2h la première fois.
2. Installer containerd
Depuis K8s 1.24, Docker Engine n'est plus supporté comme runtime. J'utilise containerd directement, avec SystemdCgroup = true pour matcher le driver de kubelet.
# Key point: cgroup driver must match kubelet
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' \
/etc/containerd/config.toml
systemctl restart containerd
3. Bootstrap du control-plane
kubeadm init \
--apiserver-advertise-address=192.168.1.10 \
--pod-network-cidr=10.244.0.0/16 \
--upload-certs
La commande sort un kubeadm join à exécuter sur les workers. Je le capture via un register: Ansible puis je le passe en set_fact: avec hostvars pour que les workers puissent y accéder.
4. Installer le CNI (Calico)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/\
calico/v3.27/manifests/tigera-operator.yaml
kubectl apply -f calico-custom-resources.yaml
192.168.1.0/24. J'ai choisi 10.244.0.0/16 pour les pods pour éviter tout chevauchement. Si ça overlap, le routing part en sucette...
Résultat : kubectl get nodes
NAME STATUS ROLES AGE VERSION
control-plane Ready control-plane 4m12s v1.29.2
worker-01 Ready <none> 2m47s v1.29.2
worker-02 Ready <none> 2m45s v1.29.2
Trois nœuds Ready, Calico qui tourne, et surtout un playbook qui me permet de détruire + recréer le cluster en ~8 minutes. Next steps : Traefik en ingress, ArgoCD pour le GitOps, et Prometheus pour le monitoring (sujets des prochains articles).
"Le meilleur moyen de comprendre un abstraction cloud, c'est de la reconstruire à la main au moins une fois."
Ce que j'en retire
- kubeadm peut rapidement se prendre en main ; c'est surtout les prérequis système (swap, modules, sysctl) qui font trébucher.
- Ansible + kubeadm = combo gagnant ; la logique impérative d'
init/joinse prête bien à un playbook. - On-premise forme le réflexe "réseau" ; quand le CNI buggue, tu n'as personne à blâmer : tu lis les logs Calico.