Cours 4 · Infrastructure as CodeLeçon 1 sur 2
Provisionnement déclaratif
6 h de lecture9 sections Version PDF
Infrastructure immuable ; Terraform : ressources, état, plan et application ; idempotence, modules et réutilisation, gestion de l'état partagé en équipe.
Il existe, dans presque toutes les organisations, un serveur que personne n'ose redémarrer. Il a été installé il y a quatre ans, configuré à la main, ajusté une dizaine de fois par des personnes qui ont depuis changé d'équipe. Il fonctionne. Nul ne sait exactement pourquoi, ni comment le reconstruire si le matériel lâche.
On l'appelle un serveur flocon : unique, et impossible à reproduire. Il est le symptôme d'un problème plus général, la dérive de configuration — l'écart qui se creuse entre ce qu'on croit avoir déployé et ce qui tourne réellement.
Ce chapitre supprime cette catégorie de problème en changeant l'objet qu'on manipule : on ne configure plus des machines, on décrit une infrastructure dans des fichiers versionnés.
Immuable plutôt que réparé
Deux façons de faire évoluer une machine.
La mutation : on se connecte, on met à jour un paquet, on modifie un fichier de configuration. Chaque intervention éloigne un peu plus la machine de son état d'origine, et deux serveurs installés le même jour finissent différents.
L'immuabilité : on ne modifie jamais une machine en service. On construit une nouvelle image, on déploie de nouvelles instances, on retire les anciennes. C'est exactement le conteneur du chapitre 3, à l'échelle de l'infrastructure — et c'est le même déploiement progressif qu'au chapitre 6.
La formule consacrée : on traite les serveurs comme du bétail, pas comme des animaux de compagnie. Un animal de compagnie a un nom, on le soigne quand il est malade. Un troupeau se compte : un animal malade est remplacé.
Trois bénéfices en découlent. Plus de dérive : l'état d'une machine est entièrement déterminé par l'image dont elle vient. Retour arrière trivial : redéployer l'image précédente. Et recette fidèle : le même code produit un environnement identique, ce qui est la condition posée au chapitre 6.
Déclaratif plutôt qu'impératif
C'est le second renversement, et il structure aussi le chapitre 9.
Un script impératif décrit les étapes : créer le réseau, puis la machine, puis la règle de pare-feu. Il suppose un état de départ connu, et devient faux dès que quelque chose existe déjà — il faut alors ajouter partout des « si cela n'existe pas ».
Une description déclarative énonce l'état voulu : il doit exister un réseau, deux machines et une règle. L'outil compare cette description au monde réel et calcule lui-même ce qu'il faut faire — créer, modifier, détruire, ou ne rien faire.
resource "aws_instance" "serveur_web" { ami = "ami-0c55b159" instance_type = "t3.micro" tags = { Name = "web-production" }} resource "aws_security_group" "web" { ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] }}Ce fichier ne dit pas comment créer une machine : il dit qu'il doit en exister une, de ce type, avec ce nom. Le comment appartient à l'outil.
Plan, puis application
Terraform travaille en deux temps, et c'est ce qui le rend utilisable en équipe.
terraform plan compare la description au monde réel et affiche CE QUI VA CHANGER, sans rien changer terraform apply exécute ce planLe plan est l'artefact le plus important du chapitre. Il se lit avant d'agir, il se relit en demande de fusion, et il est le seul moyen de répondre à la question qui compte : « qu'est-ce que cette modification va détruire ? »
Terraform will perform the following actions: + aws_instance.serveur_web_2 will be created ~ aws_security_group.web will be updated in-place - aws_instance.ancien_serveur will be destroyed-/+ aws_db_instance.principale must be REPLACEDLa dernière ligne est celle qu'il faut savoir repérer. Certaines modifications ne peuvent pas être appliquées sur place : changer la zone d'une machine, ou un paramètre immuable d'une base, implique de détruire puis recréer. Sur une base de production, c'est la perte des données — et le plan est le seul endroit où l'on peut s'en apercevoir avant.
D'où la règle de conduite : on lit le plan, ligne à ligne, et l'on cherche les - et les
-/+. Un apply lancé sans avoir lu son plan est l'équivalent d'un rm -rf sans regarder le
chemin.
L'état, et pourquoi c'est le point dur
Pour calculer un plan, Terraform a besoin de savoir ce qu'il a déjà créé. C'est le rôle du fichier d'état : la correspondance entre les ressources décrites dans le code et les objets réels chez le fournisseur.
description (code) ←── état ──→ monde réelTrois écarts sont donc possibles, et ils ne se traitent pas de la même façon.
Le code a changé : c'est le cas normal, le plan propose les modifications.
Le monde réel a changé sans passer par Terraform — quelqu'un a modifié une règle depuis la console web. C'est la dérive, et le plan la révèle en proposant de revenir à ce qui est décrit. La bonne réaction n'est pas d'accepter la dérive mais de comprendre pourquoi quelqu'un a eu besoin de contourner le code.
L'état est perdu ou faux : Terraform ne sait plus ce qui lui appartient. Il proposera de recréer des ressources qui existent déjà, ce qui échouera — ou pire, réussira en double. C'est le scénario catastrophe du chapitre.
D'où trois règles, non négociables. L'état ne va jamais dans Git : il contient des valeurs
sensibles en clair — mots de passe générés, clés — et deux personnes qui le versionnent
produisent des conflits inextricables. Il vit dans un stockage distant partagé. Et ce
stockage doit fournir un verrou : sans lui, deux apply simultanés écrivent chacun leur
version de l'état, et la dernière écriture efface l'autre. On se retrouve avec des ressources
créées que plus rien ne référence — invisibles, facturées, et impossibles à détruire
proprement.
Idempotence et modules
L'idempotence est la propriété qui rend tout cela sûr : appliquer deux fois produit le même
résultat qu'appliquer une fois. Puisque l'outil compare l'état voulu au réel, un second
apply sans modification ne fait rien — No changes. C'est ce qui permet de relancer sans
crainte après une interruption, et c'est ce que le chapitre 8 retrouvera avec Ansible.
Les modules évitent la duplication : un module « environnement applicatif » paramétré par le nom et la taille, instancié trois fois pour le développement, la recette et la production. C'est la même motivation que la fonction en programmation — et le même piège, un module trop paramétré devenant plus difficile à comprendre que le code qu'il factorise.
Un plan Terraform affiche « -/+ aws_db_instance.principale must be replaced ». Que signifie cette ligne ?
Deux ingénieurs lancent terraform apply en même temps sur un état stocké dans un simple fichier partagé, sans verrou. Que se passe-t-il ?
À vous
L'exercice construit un Terraform miniature, et c'est le seul moyen de voir ce que l'outil fait réellement.
Vous écrirez le calcul du plan : comparer la description, l'état et le monde réel, et en
déduire les créations, modifications, remplacements et destructions. Le jeu de données contient
un attribut immuable, pour que le -/+ apparaisse et qu'il faille le repérer.
Ensuite l'idempotence : appliquer, puis réappliquer, et vérifier qu'il ne se passe plus rien. Puis la dérive : quelqu'un modifie le monde réel sans passer par le code, et vous verrez le plan proposer de revenir en arrière.
La dernière partie simule deux applications concurrentes, avec et sans verrou. Vous compterez les ressources orphelines produites dans le premier cas.
Calculez un plan avec remplacements et destructions, vérifiez l'idempotence, puis provoquez une dérive et une course.
// ── Trois sources de vérité ─────────────────────────────────────────────── const IMMUABLES = ["zone", "moteur"]; // modifier cela impose un remplacement // Ce que le code décrit. const CODE = { "instance.web": { type: "t3.small", zone: "eu-west-1a" }, "instance.web2": { type: "t3.micro", zone: "eu-west-1b" }, "securite.web": { port: 443 }, "base.principale": { taille: 100, moteur: "postgres16" }, }; // Ce que l'outil croit avoir créé. const ETAT = { "instance.web": { type: "t3.micro", zone: "eu-west-1a" }, "securite.web": { port: 443 }, "base.principale": { taille: 100, moteur: "postgres15" }, "instance.ancienne": { type: "t3.micro", zone: "eu-west-1a" }, }; // Ce qui existe réellement chez le fournisseur. const REEL = { ...ETAT }; function plan(code, etat) { const actions = []; for (const [nom, voulu] of Object.entries(code)) { const connu = etat[nom]; if (!connu) { actions.push({ signe: "+", nom, quoi: "sera créée" }); continue; } const differents = Object.keys(voulu).filter((k) => voulu[k] !== connu[k]); if (differents.length === 0) continue; // ← à écrire : si un attribut IMMUABLE change, c'est un remplacement actions.push({ signe: "~", nom, quoi: "modifiée sur place : " + differents.join(", ") }); } // ← à écrire : ce qui est dans l'état mais plus dans le code sera détruit return actions; } function afficher(actions) { if (!actions.length) { console.log(" No changes. Infrastructure is up-to-date."); return; } for (const a of actions) { console.log(" " + a.signe.padStart(3) + " " + a.nom.padEnd(20) + a.quoi); } } // ── À VOUS ──────────────────────────────────────────────────────────────── // 1. Complétez plan() : remplacement sur attribut immuable, et destruction. // Repérez la ligne -/+ : que se passerait-il si l'on appliquait ? // 2. Appliquez, puis replanifiez : vérifiez l'idempotence. // 3. Simulez une DÉRIVE — quelqu'un modifie le monde réel depuis la console. // 4. Simulez deux applications concurrentes sans verrou, puis avec. afficher(plan(CODE, ETAT));
En travaux pratiques
Décrire l'infrastructure, sans un centime d'hébergement
Écrire, planifier et appliquer une infrastructure réelle avec Terraform — en pilotant Docker en local, ce qui donne un vrai état, une vraie dérive et un vrai verrou.
- Terraform ou OpenTofu installé
- Docker en fonctionnement
- Les images du TP 3 disponibles localement
- 1. Décrire la pile
Avec le fournisseur Docker, décrivez un réseau, un volume, un conteneur de base et un conteneur applicatif. Lancez un plan et LISEZ-LE avant d'appliquer.
- 2. Éprouver l'idempotence
Appliquez, puis replanifiez sans rien modifier. Notez ce qu'affiche le plan, et expliquez pourquoi.
- 3. Provoquer une dérive
Arrêtez un conteneur à la main avec Docker, sans passer par Terraform. Replanifiez. Que propose l'outil, et pourquoi ?
- 4. Repérer un remplacement
Modifiez un attribut modifiable sur place, puis un attribut immuable. Comparez les deux plans et repérez le symbole qui distingue une modification d'un remplacement.
- 5. Factoriser en module
Transformez la description en module paramétré par le nom de l'environnement et le nombre d'exemplaires, puis instanciez-le deux fois : recette et production.
- 6. Casser l'état, et le réparer
Faites une copie de sauvegarde du fichier d'état, puis supprimez-le. Replanifiez. Que propose Terraform ? Restaurez ensuite la sauvegarde.
- 7. La course
À deux, sur le même état partagé sans verrou, lancez une application simultanément. Comparez ensuite le contenu de l'état et la liste réelle des conteneurs.
- Un second plan sans modification affiche « no changes »
- Vous savez repérer un remplacement dans un plan et dire ce qu'il détruirait
- Le module s'instancie deux fois sans duplication de code
- Vous pouvez nommer au moins une ressource orpheline produite par la course
Ce que la suite en fait
Le chapitre 8 traite ce que celui-ci laisse de côté. Terraform crée une machine ; il ne dit pas ce qui tourne dessus. Ansible installe, configure, déploie — et l'on verra que la frontière entre les deux est moins nette qu'il n'y paraît, l'infrastructure immuable réduisant beaucoup le besoin de configuration.
Le chapitre 9 poussera le déclaratif jusqu'au bout : Kubernetes est une boucle de réconciliation permanente là où Terraform en exécute une à la demande. C'est la même idée, avec un contrôleur qui ne s'arrête jamais.
À retenir
Vous avez parcouru les 9 sections.
Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.