Terraform en découverte, le state et la dérive
Pourquoi ce TP, après Ansible ?
Ansible vous a montré configurer ; Terraform montre provisionner avec sa pièce originale, le state et la détection de dérive (ch. 13). On ne va pas reconstruire Listify avec : on va isoler ces deux mécanismes sur un exemple minimal (un conteneur Nginx), pour les voir fonctionner. C'est un TP de concepts, court et dense.
Étape 0 : installer Terraform et activer le socket Podman (30 min)
# Terraform (dépôt HashiCorp, comme Vagrant au TP 7) OU OpenTofu (ch. 13)
sudo apt install -y terraform # ou : suivez le guide pour opentofu (commande 'tofu')
terraform version
# Le socket Podman, compatible API Docker, en mode utilisateur (rootless)
systemctl --user enable --now podman.socket
systemctl --user status podman.socket # active (listening)
echo "$XDG_RUNTIME_DIR/podman/podman.sock" # notez ce chemin, il ira dans le provider
Étape 1 : la configuration (30 min)
Dans un répertoire de travail, hors du dépôt Listify (c'est un exercice séparé) :
mkdir ~/tp10-terraform && cd ~/tp10-terraform
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
provider "docker" {
# Adaptez au chemin affiché à l'étape 0 (souvent /run/user/1000/podman/podman.sock)
host = "unix:///run/user/1000/podman/podman.sock"
}
resource "docker_image" "web" {
name = "docker.io/library/nginx:1.27-alpine"
}
resource "docker_container" "web" {
name = "tf-demo"
image = docker_image.web.image_id
ports {
internal = 80
external = 8088
}
}
Étape 2 : le cycle init / plan / apply (45 min)
terraform init # télécharge le provider docker ; crée .terraform/ et le lock
Regardez ce qu'init a créé (.terraform/, .terraform.lock.hcl), puis le geste central du chapitre 13, plan avant apply :
terraform plan
Lisez le plan attentivement (il va au runbook) : 2 to add, 0 to change, 0 to destroy, avec le détail de l'image et du conteneur, et les (known after apply) pour les valeurs que seul le fournisseur connaîtra (l'ID de l'image). Rien n'a encore été fait : c'est une prévision. Puis :
terraform apply # ré-affiche le plan, demande confirmation : tapez 'yes'
podman ps # le conteneur tf-demo tourne
curl -s localhost:8088 | head -5 # la page nginx
Ouvrez maintenant le state (sans jamais l'éditer à la main) :
terraform show # l'état lisible
ls -l terraform.tfstate # le fichier ; NE VA PAS dans Git (ch. 13, §3.3)
Point de contrôle n° 1 : le graphe et l'ordre
Vous n'avez écrit aucun ordre, pourtant l'image a été créée avant le conteneur. Retrouvez, dans la sortie de plan ou avec terraform graph, la dépendance qui l'a imposé (la ligne image = docker_image.web.image_id). Reliez au chapitre 13, §2.1 : l'ordre est calculé depuis les références.
Étape 3 : la détection de dérive (l'expérience clé, 45 min)
Le cœur du TP. On modifie la réalité dans le dos de Terraform, puis on regarde comment il réagit.
# Détruire le conteneur À LA MAIN (comme un collègue pressé, ou un incident)
podman rm -f tf-demo
podman ps -a | grep tf-demo || echo "tf-demo n'existe plus"
# Terraform ne le sait pas encore. Demandons-lui de comparer :
terraform plan
Lisez la sortie : Terraform a rafraîchi son state face à la réalité, constaté que le conteneur déclaré n'existe plus, et propose 1 to add pour le recréer. C'est la détection de dérive (ch. 13, §3.2) : l'écart entre la configuration (le conteneur doit exister) et la réalité (il n'existe plus) est détecté et corrigible.
terraform apply # 'yes' : le conteneur renaît, conforme à la configuration
podman ps # tf-demo est de retour
Faites la variante « modification » : changez le port externe à 8089 dans main.tf, terraform plan (il montre un -/+, remplacement : le conteneur doit être détruit puis recréé, car le port n'est pas modifiable à chaud), puis apply. Observez le ~/-/+ : Terraform distingue ce qui se modifie en place de ce qui exige un remplacement.
Point de contrôle n° 2 : les trois termes
Remplissez le tableau du chapitre 13 (§3.2) avec CE que vous venez de voir : dans le cas « podman rm -f », quelle était la configuration, quel était le state avant refresh, quelle était la réalité, et qu'a proposé le plan ? Ce tableau est une question d'examen quasi certaine.
Étape 4 : destruction et nettoyage (15 min)
terraform destroy # 'yes' : détruit tout ce qui est dans le state (ordre inverse)
podman ps -a | grep tf-demo || echo "propre"
Notez le principe : destroy détruit ce que le state connaît, dans l'ordre inverse de création (conteneur puis image). Ce que Terraform n'a pas créé, il n'y touche pas : le state est sa frontière de propriété (ch. 13, §3.1).
Étape 5 : la synthèse comparative (15 min)
Rédigez au runbook, en un paragraphe : qu'est-ce que Terraform fait que Ansible ne fait pas (le state, la détection qu'une ressource déclarée a disparu, la destruction du non-déclaré) ? Et qu'est-ce qu'Ansible fait que ce Terraform ne fait pas (configurer l'intérieur d'une machine : paquets, fichiers, services) ? Concluez par la phrase du chapitre 13 : Terraform provisionne, Ansible configure, et notre TP 9 (Vagrant + Ansible) est la maquette locale de ce couple.
Point de contrôle final
-
terraform applycrée le conteneur ;curl localhost:8088répond - Le rôle du plan (prévision avant action) compris et consigné
- Dérive provoquée (
podman rm -f) et détectée parterraform plan: la capture au runbook - La distinction
~(modif en place) /-/+(remplacement) observée -
terraform destroynettoie ; synthèse Terraform vs Ansible rédigée -
terraform.tfstatenon commité (vérifié dans.gitignoresi vous versionnez cet exercice)
Pour aller plus loin (bonus)
- Le graphe :
terraform graph | dot -Tsvg > graph.svg(paquetgraphviz) ; visualisez le DAG de deux ressources. Gardez cette image : au S3, les DAG d'Airflow lui ressembleront comme des frères. - Une variable et une sortie : ajoutez une
variable "external_port"et un blocoutput "url";terraform apply -var external_port=9090puisterraform output. Vous retrouvez la séparation code/données de tout le semestre. - Deux conteneurs et un réseau : déclarez un
docker_networket deux conteneurs dessus ; observez comment le graphe ordonne le réseau avant les conteneurs. Vous provisionnez une mini-infra. - Simuler le cloud sans cloud : explorez le provider
local(fichiers) ou lisez la configuration d'un module AWS de la documentation ; identifiez ce qui change (types de ressources) et ce qui ne change pas (init/plan/apply, state, graphe). C'est tout l'intérêt du modèle.
Questions de compréhension (à préparer pour le TD et l'examen)
- Pourquoi Terraform a-t-il besoin d'un state alors qu'Ansible n'en a pas ? Répondez par la nature de leur interlocuteur (une API vaste vs un OS qu'on relit intégralement).
- Vous supprimez une ressource de
main.tf(vous cessez de la déclarer) puisapply. Que se passe-t-il, et pourquoi est-ce parfois une mauvaise surprise en production ? Quelle pratique (revue duplan) l'évite ? - Le state contient des secrets en clair et ne va pas dans Git. Où le met-on quand on travaille à plusieurs, et quel nouveau problème (accès concurrent) cela crée-t-il ? (Cherchez « remote backend » et « state locking ».)
- Terraform et le
deploy.shdu TP 9 gèrent tous deux un « ordre ». Comparez : l'ordre de Terraform est calculé (graphe de dépendances), celui du script est écrit (deux lignes). Lequel passe mieux à l'échelle de 200 ressources, et pourquoi ?