MET : Ansible automation (3/6)

| Digital

Configurer un serveur manuellement est acceptable. En configurer dix est fastidieux. En configurer cent est impossible. Ansible résout ce problème avec élégance.

Dans l’ article précédent, nous avons vu comment Terraform crée nos serveurs. Mais un serveur fraîchement créé est « nu » : pas de Kubernetes, pas de configuration, rien. C’est là qu’Ansible intervient.

Le problème : la configuration manuelle

Après la création d’un serveur, la liste des tâches est longue :

  • Mettre à jour le système
  • Installer les dépendances
  • Configurer le pare-feu
  • Désactiver le swap (requis pour Kubernetes)
  • Charger les modules du noyau
  • Installer Kubernetes
  • Configurer le réseau

Faire cela manuellement pose plusieurs problèmes :

  • Temps : 2-3 heures par serveur, minimum
  • Erreurs : Un oubli, une faute de frappe, et cela ne fonctionne plus
  • Dérive : Avec le temps, les serveurs divergent (configurations différentes)
  • Documentation : « Comment avons-nous configuré cela déjà ? »

La solution : Ansible

Ansible est un outil d’automatisation open source qui permet de configurer des serveurs de manière déclarative et idempotente.

Idempotent : Vous pouvez exécuter le même playbook 100 fois, le résultat sera toujours le même. Si quelque chose est déjà configuré, Ansible ne le refait pas.

Avantages clés :

  • Sans agent : Ansible se connecte via SSH, rien à installer sur les serveurs
  • Lisible : Les playbooks sont en YAML, compréhensibles par tous
  • Idempotent : Exécution sûre et reproductible
  • Modulaire : Des milliers de modules disponibles

L’inventaire : définir vos serveurs

Tout commence par l’inventaire, qui liste les serveurs et leurs caractéristiques :

# inventory/hosts.yml
all:
 children:
 k8s_cluster:
 children:
 control_plane:
 hosts:
 k8s-master:
 ansible_host: 128.140.xxx.xxx
 private_ip: 10.0.1.10
 workers:
 hosts:
 k8s-worker-1:
 ansible_host: 91.99.xxx.xxx
 private_ip: 10.0.1.11

  vars:
 ansible_user: root
 ansible_ssh_private_key_file: ~/.ssh/id_rsa
 k3s_version: "v1.28.5+k3s1"
 cluster_cidr: "10.42.0.0/16"
 service_cidr: "10.43.0.0/16"

Cet inventaire définit :

  • Groupes : control_plane, workers, k8s_cluster
  • Variables par hôte : Adresses IP publiques et privées
  • Variables globales : Version K3s, configuration réseau

Playbook 1 : Préparation des serveurs

Notre premier playbook prépare les serveurs pour Kubernetes :

# playbooks/prepare-servers.yml
---
- name: Préparation des serveurs
 hosts: k8s_cluster
 become: true # Exécuter en tant que root

  tasks:
 - name: Mettre à jour les paquets
 apt:
 update_cache: yes
 cache_valid_time: 3600

  - name: Installer les dépendances
 apt:
 name:
 - curl
 - wget
 - git
 - htop
 - open-iscsi # Requis pour Longhorn
 - nfs-common # Requis pour le stockage NFS
 - cryptsetup # Chiffrement
 - jq # Analyse JSON
 state: present

  - name: Désactiver le swap
 shell: swapoff -a
 when: ansible_swaptotal_mb > 0

  - name: Désactiver le swap de manière permanente
 replace:
 path: /etc/fstab
 regexp: '^([^#].*?sswaps+sws+.*)$'
 replace: '# \1'

Chaque tâche est explicite :

  • name : Description lisible de ce que fait la tâche
  • apt : Module Ansible pour la gestion des paquets Debian/Ubuntu
  • when : Condition d’exécution (ici, uniquement si le swap existe)

Configuration du noyau

Kubernetes nécessite certains modules du noyau et paramètres système :

  - name: Configurer les modules du noyau
 copy:
 dest: /etc/modules-load.d/k8s.conf
 content: |
 overlay
 br_netfilter
 ip_vs
 ip_vs_rr
 ip_vs_wrr
 ip_vs_sh
 nf_conntrack

  - name: Charger les modules
 modprobe:
 name: "{{ item }}"
 state: present
 loop:
 - overlay
 - br_netfilter
 - ip_vs

  - name: Configurer sysctl
 sysctl:
 name: "{{ item.key }}"
 value: "{{ item.value }}"
 state: present
 reload: yes
 loop:
 - { key: 'net.bridge.bridge-nf-call-iptables', value: '1' }
 - { key: 'net.bridge.bridge-nf-call-ip6tables', value: '1' }
 - { key: 'net.ipv4.ip_forward', value: '1' }
 - { key: 'fs.inotify.max_user_watches', value: '524288' }

Notez l’utilisation de loop pour répéter une tâche avec différentes valeurs. C’est plus propre que de dupliquer le code.

Playbook 2 : Installation de Kubernetes (K3s)

Une fois les serveurs préparés, nous installons K3s :

# playbooks/install-k3s.yml
---
- name: Installation du plan de contrôle K3s
 hosts: control_plane
 become: true

  tasks:
 - name: Télécharger le script K3s
 get_url:
 url: https://get.k3s.io
 dest: /tmp/k3s-install.sh
 mode: '0755'

  - name: Installer le serveur K3s
 shell: |
 INSTALL_K3S_VERSION={{ k3s_version }} \
 K3S_TOKEN={{ k3s_token }} \
 INSTALL_K3S_EXEC="server \
 --cluster-init \
 --disable traefik \
 --write-kubeconfig-mode 644 \
 --tls-san {{ ansible_host }} \
 --node-ip {{ private_ip }} \
 --advertise-address {{ private_ip }} \
 --cluster-cidr {{ cluster_cidr }} \
 --service-cidr {{ service_cidr}}" \
 sh /tmp/k3s-install.sh
 args:
 creates: /usr/local/bin/k3s # Exécuter uniquement si k3s n'existe pas

  - name: Récupérer le kubeconfig
 fetch:
 src: /etc/rancher/k3s/k3s.yaml
 dest: ../kubeconfig
 flat: yes

Points importants :

  • creates : Rend la tâche idempotente (s’exécute uniquement si le fichier n’existe pas)
  • fetch : Récupère le kubeconfig sur notre machine locale
  • –disable traefik : Nous installerons notre propre Traefik configuré

Installation des workers

- name: Installation des workers K3s
 hosts: workers
 become: true

 tasks:
 - name: Installer l'agent K3s
 shell: |
 INSTALL_K3S_VERSION={{ k3s_version }} \
 K3S_URL=https://{{ hostvars['k8s-master']['private_ip'] }}:6443 \
 K3S_TOKEN={{ hostvars['k8s-master']['k3s_join_token'] }} \
 INSTALL_K3S_EXEC="agent \
 --node-ip {{ private_ip }}" \
 sh /tmp/k3s-install.sh
 args:
 creates: /usr/local/bin/k3s

Les workers rejoignent automatiquement le cluster en utilisant le jeton du plan de contrôle.

Playbook 3 : Services de base

Après K3s, nous installons les services essentiels via Helm :

# playbooks/install-core-services.yml
---
- name: Installation des services de base
 hosts: control_plane
 become: true

  tasks:
 - name: Installer Helm
 shell: |
 curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
 args:
 creates: /usr/local/bin/helm

  - name: Ajouter les dépôts Helm
 shell: |
 helm repo add traefik https://traefik.github.io/charts
 helm repo add longhorn https://charts.longhorn.io
 helm repo add jetstack https://charts.jetstack.io
 helm repo add argo https://argoproj.github.io/argo-helm
 helm repo update
 environment:
 KUBECONFIG: /etc/rancher/k3s/k3s.yaml

  - name: Installer Traefik
 shell: |
 helm upgrade --install traefik traefik/traefik \
 --namespace infra-system \
 --create-namespace \
 --set ports.web.redirectTo.port=websecure \
 --wait --timeout 5m
 environment:
 KUBECONFIG: /etc/rancher/k3s/k3s.yaml

  - name: Installer Cert-Manager
 shell: |
 helm upgrade --install cert-manager jetstack/cert-manager \
 --namespace cert-manager \
 --create-namespace \
 --set installCRDs=true \
 --wait --timeout 5m
 environment:
 KUBECONFIG: /etc/rancher/k3s/k3s.yaml

  - name: Installer Longhorn
 shell: |
 helm upgrade --install longhorn longhorn/longhorn \
 --namespace longhorn-system \
 --create-namespace \
 --set defaultSettings.defaultReplicaCount=2 \
 --wait --timeout 10m
 environment:
 KUBECONFIG: /etc/rancher/k3s/k3s.yaml

Exécution des playbooks

Pour exécuter un playbook :

# Tester la connexion aux serveurs
ansible all -i inventory/hosts.yml -m ping

# Exécuter un playbook
ansible-playbook -i inventory/hosts.yml playbooks/prepare-servers.yml

# Mode simulation (voir ce qui serait fait sans exécuter)
ansible-playbook -i inventory/hosts.yml playbooks/install-k3s.yml --check

Dans notre projet, nous avons simplifié avec des commandes Make :

# Tester la connexion
make ansible-ping

# Installer K3s
make ansible-install-k3s

# Installer les services de base
make ansible-core-services

Nos bonnes pratiques Ansible

📁 Organisation

ansible/
├── inventory/
│ ├── hosts.yml # Inventaire des serveurs
│ └── group_vars/ # Variables par groupe
├── playbooks/
│ ├── prepare-servers.yml
│ ├── install-k3s.yml
│ └── install-core-services.yml
├── templates/ # Fichiers de configuration Jinja2
└── ansible.cfg # Configuration Ansible

🔐 Sécurité

  • Ansible Vault : Chiffrer les variables sensibles
  • Clés SSH : Jamais de mots de passe en clair
  • become : Utiliser sudo plutôt que root direct

✅ Idempotence

  • Utiliser creates ou removes pour les commandes shell
  • Préférer les modules Ansible aux commandes shell lorsque c’est possible
  • Tester avec –check avant d’appliquer

Le résultat

Avec nos playbooks Ansible :

Avant (manuel)Après (Ansible)
2-3 heures par serveur15 minutes pour l’ensemble du cluster
Documentation séparée (souvent obsolète)Le code EST la documentation
Configuration différente entre les serveursConfiguration identique garantie
« Ça marchait avant… »100 % reproductible

Et ensuite ?

Nous avons maintenant :

  • ✅ Des serveurs créés par Terraform
  • ✅ Un cluster Kubernetes configuré par Ansible
  • ✅ Des services de base installés (Traefik, Longhorn, Cert-Manager)

Dans le prochain article, nous verrons comment Kubernetes et Rancher nous permettent de gérer nos applications avec une interface intuitive et des fonctionnalités puissantes.

🚀 Vous passez trop de temps à configurer vos serveurs ?

Nous pouvons vous aider à automatiser votre infrastructure avec Ansible. Gagnez du temps, réduisez les erreurs, dormez tranquille.

Discutons de votre projet →