Le contexte
Bon, soyons honnêtes. Au début, j'avais un gros bordel de scripts bash éparpillés partout. Genre 5-6 fichiers avec des noms comme install-docker.sh, setup-k8s-FINAL-v3.sh (oui, le v3...). À chaque fois que je devais recréer mon infra, c'était 45 minutes de galère + 10 minutes à me demander pourquoi ça marchait pas.
J'avais besoin de quelque chose de plus propre pour mon projet SAE e-commerce.
Ce que je voulais vraiment
Pas un truc de démo avec minikube. Non. Je voulais:
- 3 VMs qui tournent vraiment (1 master + 2 workers)
- Tout automatisé - je tape une commande et ça se déploie
- ArgoCD pour faire du GitOps (parce que push to deploy c'est quand même cool)
- Des logs centralisés (Loki + Grafana)
- Et surtout : pouvoir tout péter et tout recréer en 10 minutes
L'architecture (spoiler: ça marche maintenant)
┌─────────────────────────────────────────┐
│ Mon PC (Debian) │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐
│ │ Master │ │ Worker 1 │ │ Worker 2│
│ │ .56.10 │ │ .56.11 │ │ .56.12 │
│ └──────────┘ └──────────┘ └─────────┘
└─────────────────────────────────────────┘
Chaque VM a 4Go de RAM et 4 CPUs. Oui, ça bouffe des ressources. Non, ça passe pas sur un laptop pourri.
Comment c'est organisé
J'ai tout mis dans un repo bien rangé (pour une fois):
ansible-provisioning/
├── Vagrantfile # Les 3 VMs
├── playbook.yml # Le chef d'orchestre
├── manifests/ # Mes applis K8s
│ ├── apiclients/
│ ├── apicatalogue/
│ ├── databases/
│ └── ... (toutes mes APIs)
└── roles/ # Les briques Ansible
├── docker/
├── kubernetes/
├── k8s-master/
└── argocd/
Chaque rôle fait UN truc. C'est ça qui a changé ma vie.
Shell scripts → Ansible : pourquoi j'ai migré
Avant (la galère)
J'avais un script prepare-system.sh qui ressemblait à ça:
#!/bin/bash
swapoff -a
sed -i '/swap/d' /etc/fstab
modprobe br_netfilter
# ... 50 lignes de commandes
# Aucune gestion d'erreur
# Si ça plante au milieu, bonne chance
Le pire ? Si je relançais le script après un fail, tout pétait. Genre le sed essayait de supprimer une ligne qui existait plus. Classique.
Après (je respire enfin)
Maintenant j'ai un rôle Ansible system-prepare:
- name: Virer le swap
shell: swapoff -a
ignore_errors: yes
- name: Enlever le swap du fstab
lineinfile:
path: /etc/fstab
regexp: '.*swap.*'
state: absent
- name: Charger br_netfilter
modprobe:
name: br_netfilter
state: present
La différence ?
- Je peux relancer 10 fois, ça fait pas de conneries
- C'est lisible par un humain
- Si ça plante, je sais exactement où
Le Vagrantfile (ou comment lancer 3 VMs d'un coup)
Vagrant.configure("2") do |config|
config.vm.box = "debian/bullseye64"
# Config libvirt (KVM/QEMU)
config.vm.provider "libvirt" do |libvirt|
libvirt.memory = 4096
libvirt.cpus = 4
libvirt.management_network_address = "192.168.56.0/24"
end
# NFS pour partager les manifests
config.vm.synced_folder ".", "/vagrant",
type: "nfs",
nfs_version: 4
# Le master
config.vm.define "vm-master" do |vm|
vm.vm.network "private_network", ip: "192.168.56.10"
vm.vm.hostname = "master"
end
# Les 2 workers
(1..2).each do |i|
config.vm.define "vm-slave-#{i}" do |vm|
vm.vm.network "private_network", ip: "192.168.56.1#{i}"
vm.vm.hostname = "slave-#{i}"
end
end
# Ansible se lance automatiquement
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
ansible.groups = {
"master" => ["vm-master"],
"workers" => ["vm-slave-1", "vm-slave-2"]
}
end
end
Un vagrant up et boom, tout se monte tout seul.
Le playbook : l'ordre c'est important
---
# 1. Tous les nœuds en même temps
- name: Setup de base
hosts: k8s_cluster
roles:
- system-prepare # Swap off, modules kernel
- docker # Docker + containerd
- kubernetes # kubelet, kubeadm, kubectl
# 2. Le master d'abord
- name: Init master
hosts: master
roles:
- k8s-master # kubeadm init + Flannel
# 3. Les workers ensuite, un par un
- name: Join workers
hosts: workers
serial: 1 # IMPORTANT: un à la fois
roles:
- k8s-worker
# 4. Les trucs bonus sur le master
- name: Dashboard + ArgoCD + Monitoring
hosts: master
roles:
- k8s-dashboard
- argocd
- logging
- metrics-server
Le serial: 1 c'est crucial. J'avais essayé sans, les deux workers essayaient de join en même temps et ça partait en cacahuète.
Les rôles en détail
Rôle: k8s-master (le chef d'orchestre)
C'est lui qui initialise le cluster. Voici les parties importantes:
- name: Init cluster k8s
command: kubeadm init --apiserver-advertise-address=192.168.56.10 --pod-network-cidr=10.244.0.0/16
when: not k8s_initialise.stat.exists
- name: Copier config kubectl
copy:
src: /etc/kubernetes/admin.conf
dest: /home/vagrant/.kube/config
owner: vagrant
group: vagrant
- name: Installer Flannel (réseau pod)
shell: |
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
environment:
KUBECONFIG: /home/vagrant/.kube/config
- name: Générer commande join pour les workers
copy:
content: "kubeadm join 192.168.56.10:6443 --token {{ k8s_token.stdout }} --discovery-token-ca-cert-hash sha256:{{ k8s_ca_hash.stdout }}"
dest: /vagrant/join.sh
mode: '0755'
- name: Créer fichier .master-ready
copy:
content: "Master initialized"
dest: /vagrant/.master-ready
Le fichier .master-ready c'est un flag pour dire aux workers "go, vous pouvez join maintenant".
Rôle: k8s-worker (le suiveur patient)
- name: Attendre que le fichier .master-ready existe
wait_for:
path: /vagrant/.master-ready
timeout: 600
- name: Joindre le cluster
shell: bash /vagrant/join.sh
args:
creates: /etc/kubernetes/kubelet.conf
register: join_result
failed_when:
- join_result.rc != 0
- "'already exists in the cluster' not in join_result.stderr"
- name: Attendre que le node soit Ready
shell: |
for i in {1..60}; do
STATUS=$(kubectl get node $(hostname) -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')
if [ "$STATUS" = "True" ]; then
exit 0
fi
sleep 5
done
exit 1
Le worker attend gentiment que le master soit prêt avant de faire quoi que ce soit.
Les galères que j'ai rencontrées
Galère #1: NFS qui marche pas
Au début, le partage NFS entre l'hôte et les VMs plantait.
Symptôme:
mount.nfs: Connection timed out
Solution:
# Sur l'hôte
sudo apt install nfs-kernel-server
sudo systemctl start nfs-server
sudo ufw allow from 192.168.56.0/24
Le firewall bloquait les connexions NFS. Classique.
Galère #2: Kubeadm qui timeout
Le kubeadm init prenait 10 minutes et finissait par timeout.
Cause: Pas assez de RAM sur les VMs (j'avais mis 2Go).
Solution: Passer à 4Go par VM. Ça bouffe mais c'est nécessaire.
Galère #3: Les workers qui join pas
Les workers restaient en NotReady même après le join.
Cause: Flannel (le CNI) était pas encore installé sur le master.
Solution: Attendre que Flannel soit complètement déployé avant de faire join les workers:
- name: Attendre Flannel
command: kubectl wait --for=condition=ready pod -l app=flannel -n kube-flannel --timeout=300s
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
Galère #4: Ansible qui relance tout à chaque fois
Au début, chaque vagrant provision refaisait TOUT depuis zéro.
Solution: Ajouter des conditions when partout:
- name: Init cluster k8s
command: kubeadm init ...
when: not k8s_initialise.stat.exists # ← Ça sauve des vies
L'idempotence c'est vraiment la base avec Ansible.
Les commandes utiles au quotidien
# Lancer tout
cd ansible-provisioning && vagrant up
# Vérifier l'état du cluster
vagrant ssh vm-master -c 'kubectl get nodes'
# Voir les pods
vagrant ssh vm-master -c 'kubectl get pods -A'
# Refaire le provisioning (sans détruire les VMs)
vagrant provision
# Tout péter et recommencer
vagrant destroy -f && vagrant up
# SSH sur le master
vagrant ssh vm-master
# Logs d'un pod
vagrant ssh vm-master -c 'kubectl logs -n apps apicatalogue-xyz'
ArgoCD et les applications
Une fois le cluster monté, ArgoCD déploie automatiquement mes apps.
Voici comment je déclare l'API Catalogue:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: catalogue-manager-application
namespace: argocd
spec:
destination:
namespace: apps
server: https://kubernetes.default.svc
source:
path: ansible-provisioning/manifests/apicatalogue
repoURL: https://github.com/uha-sae53/Vagrant.git
targetRevision: main
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
ArgoCD surveille mon repo GitHub. Dès que je change un manifest, ça se déploie automatiquement.
Metrics Server et HPA
J'ai aussi ajouté le Metrics Server pour l'auto-scaling:
- name: Installer Metrics Server
shell: |
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
- name: Patcher pour ignorer TLS (dev seulement)
shell: |
kubectl patch deployment metrics-server -n kube-system --type='json' \
-p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]'
Avec ça, mes pods peuvent scaler automatiquement en fonction de la charge CPU/RAM.
Le résultat final
Après tout ça, voici ce que je peux faire:
# Démarrer tout de zéro
vagrant up
# ⏱️ 8 minutes plus tard...
# Vérifier que tout tourne
vagrant ssh vm-master -c 'kubectl get pods -A'
# Résultat:
# NAMESPACE NAME READY STATUS
# apps apicatalogue-xyz 1/1 Running
# apps apiclients-abc 1/1 Running
# apps apicommandes-def 1/1 Running
# apps api-panier-ghi 1/1 Running
# apps frontend-jkl 1/1 Running
# argocd argocd-server-xxx 1/1 Running
# logging grafana-yyy 1/1 Running
# logging loki-0 1/1 Running
# kube-system metrics-server-zzz 1/1 Running
Tout fonctionne, tout est automatisé.
Conclusion
Ce que j'ai appris:
- Ansible > scripts shell (vraiment, vraiment)
- L'idempotence c'est pas un luxe
- Tester chaque rôle séparément avant de tout brancher
- Les workers doivent attendre le master (le
serial: 1sauve des vies) - 4Go de RAM minimum par VM pour K8s
Le code complet est sur GitHub: https://github.com/uha-sae53/Vagrant
Des questions ? Ping moi sur Twitter ou ouvre une issue sur le repo.
Et si vous galérez avec Kubernetes, vous êtes pas seuls. J'ai passé 3 semaines là-dessus, c'est normal que ce soit compliqué au début.
Top comments (0)