Terraform: Up & Running
Chapitre 1 / 8 · 27 min de lecture

Pourquoi Terraform

Ce qu'est l'infrastructure as code, comment fonctionne Terraform, et comment il se compare aux autres outils (Chef, Puppet, Ansible, CloudFormation, Pulumi).

Un logiciel n'est pas terminé quand le code fonctionne sur votre machine. Il ne l'est pas davantage quand les tests passent, ni quand quelqu'un vous donne son feu vert lors d'une revue de code. Un logiciel n'est terminé que lorsqu'il est livré à l'utilisateur — c'est-à-dire qu'il tourne sur des serveurs de production, qu'il résiste aux pannes et aux pics de trafic, et qu'il est protégé des attaquants. Avant de plonger dans les détails de Terraform, il vaut la peine de prendre du recul pour voir où il s'inscrit dans cette histoire plus vaste qu'est la livraison de logiciel. Ce premier chapitre situe l'infrastructure as code (IaC) dans le paysage du DevOps, explique le fonctionnement de Terraform et le compare en profondeur aux autres outils du marché.

L'essor du DevOps

Il n'y a pas si longtemps, monter une entreprise logicielle imposait de gérer beaucoup de matériel : installer des baies et des racks, les charger de serveurs, brancher les câbles, prévoir le refroidissement et des alimentations redondantes. Il était logique d'avoir une équipe dédiée à l'écriture du logiciel — les développeurs (« Devs ») — et une autre dédiée à la gestion de ce matériel — l'exploitation (« Ops »). L'équipe Dev construisait une application et la « jetait par-dessus le mur » à l'équipe Ops, à qui revenait la charge de la déployer et de la faire tourner. L'essentiel se faisait à la main : installation manuelle, commandes tapées une à une sur les serveurs.

Ce modèle tient un temps, puis se fissure à mesure que l'entreprise grandit. Comme les déploiements sont manuels, ils deviennent lents, pénibles et imprévisibles à mesure que le nombre de serveurs augmente. L'équipe Ops commet des erreurs, et l'on se retrouve avec des serveurs flocons de neige (snowflake servers), chacun affichant une configuration subtilement différente des autres — un problème connu sous le nom de dérive de configuration (configuration drift). Les bugs se multiplient, les développeurs haussent les épaules en répétant « ça marche sur ma machine », les pannes deviennent fréquentes. Épuisée par les bipeurs qui sonnent à 3 heures du matin après chaque livraison, l'équipe Ops espace les releases : une fois par semaine, puis une fois par mois, puis tous les six mois. Les fusions de branches virent au cauchemar, les silos se forment, les équipes se rejettent la faute, et l'entreprise se grippe.

Aujourd'hui, un basculement profond s'opère. Au lieu de gérer leurs propres centres de données, beaucoup d'entreprises migrent vers le cloud — AWS, Microsoft Azure, Google Cloud Platform (GCP). Plutôt que d'investir dans le matériel, les équipes Ops passent désormais leur temps sur du logiciel, avec des outils comme Chef, Puppet, Terraform et Docker. Les administrateurs système n'enfilent plus des câbles réseau : ils écrivent du code. Dev et Ops travaillent l'un et l'autre sur du logiciel, et la frontière entre les deux s'estompe. C'est de là que naît le mouvement DevOps.

Le DevOps n'est ni le nom d'une équipe, ni un intitulé de poste, ni une technologie particulière : c'est un ensemble de processus, d'idées et de techniques. Sa définition varie selon les personnes, mais l'auteur retient celle-ci : l'objectif du DevOps est de rendre la livraison de logiciel radicalement plus efficace. Au lieu de cauchemars de fusion sur plusieurs jours, on intègre le code en continu et on le maintient toujours dans un état déployable. Au lieu de déployer une fois par mois, on déploie des dizaines de fois par jour, voire après chaque commit. Et au lieu de pannes incessantes, on bâtit des systèmes résilients et auto-réparateurs.

Les résultats des entreprises qui ont mené cette transformation sont saisissants : Nordstrom a doublé le nombre de fonctionnalités livrées par mois, réduit ses défauts de 50 % et ses délais de mise en production de 60 % ; la division firmware des imprimantes LaserJet de HP a fait passer le temps consacré au développement de nouvelles fonctionnalités de 5 % à 40 % ; Etsy est passé de déploiements rares et stressants à 25 ou 50 déploiements par jour. Le mouvement DevOps repose sur quatre valeurs fondamentales — culture, automatisation, mesure et partage (l'acronyme CAMS). Ce chapitre se concentre sur l'une d'elles : l'automatisation. L'idée est d'automatiser autant que possible le processus de livraison, c'est-à-dire de gérer son infrastructure non pas en cliquant sur une page web ou en exécutant des commandes shell à la main, mais à travers du code. C'est précisément ce qu'on appelle l'infrastructure as code.

Qu'est-ce que l'infrastructure as code ?

L'idée derrière l'infrastructure as code (IaC) est que l'on écrit et exécute du code pour définir, déployer, mettre à jour et détruire son infrastructure. C'est un changement de mentalité majeur : on traite tous les aspects de l'exploitation comme du logiciel, y compris ceux qui représentent du matériel. L'une des intuitions clés du DevOps est que l'on peut gérer presque tout par du code — serveurs, bases de données, réseaux, fichiers journaux, configuration applicative, documentation, tests automatisés, processus de déploiement. On distingue cinq grandes catégories d'outils d'IaC : les scripts ad hoc, les outils de gestion de configuration, les outils de templating de serveurs, les outils d'orchestration et les outils de provisionnement.

Les scripts ad hoc

L'approche la plus immédiate pour automatiser quoi que ce soit est d'écrire un script ad hoc : on prend la tâche que l'on faisait à la main, on la décompose en étapes discrètes, et on code chacune dans son langage de script favori (Bash, Ruby, Python). Voici par exemple un script setup-webserver.sh qui configure un serveur web en installant ses dépendances, en récupérant du code depuis un dépôt Git et en démarrant Apache.

# Met à jour le cache d'apt-get
sudo apt-get update

# Installe PHP et Apache
sudo apt-get install -y php apache2

# Copie le code depuis le dépôt
sudo git clone https://github.com/brikis98/php-app.git /var/www/html/app

# Démarre Apache
sudo service apache2 start

Le formidable avantage des scripts ad hoc est que l'on peut utiliser des langages de programmation généralistes et populaires, et écrire le code comme on l'entend. Leur terrible inconvénient est… exactement le même : on peut utiliser des langages généralistes et écrire le code comme on l'entend. Là où les outils conçus pour l'IaC fournissent des API concises pour des tâches compliquées et imposent une structure, un langage généraliste oblige à écrire un code entièrement personnalisé, et chaque développeur adopte son propre style. Ce n'est pas un problème pour un script de huit lignes qui installe Apache, mais cela devient vite un plat de spaghettis ingérable dès qu'il s'agit de gérer des dizaines de serveurs, de bases de données et d'équilibreurs de charge. Les scripts ad hoc sont parfaits pour de petites tâches ponctuelles, mais pour gérer toute son infrastructure, mieux vaut un outil pensé pour cela.

Les outils de gestion de configuration

Chef, Puppet, Ansible et SaltStack sont des outils de gestion de configuration (configuration management) : ils sont conçus pour installer et gérer du logiciel sur des serveurs existants. Voici un rôle Ansible web-server.yml qui configure le même serveur Apache que le script précédent.

- name: Update the apt-get cache
  apt:
    update_cache: yes

- name: Install PHP
  apt:
    name: php

- name: Install Apache
  apt:
    name: apache2

- name: Copy the code from the repository
  git: repo=https://github.com/brikis98/php-app.git dest=/var/www/html/app

- name: Start Apache
  service: name=apache2 state=started enabled=yes

Le code ressemble au script Bash, mais un outil comme Ansible apporte trois avantages décisifs. D'abord, des conventions de codage : une structure cohérente et prévisible (documentation, organisation des fichiers, paramètres clairement nommés, gestion des secrets), là où chaque développeur range ses scripts ad hoc à sa façon. Ensuite, l'idempotence : un code idempotent est un code qui produit le bon résultat quel que soit le nombre de fois où on l'exécute. Écrire un script ad hoc qui fonctionne une fois est facile ; en écrire un qui fonctionne correctement même relancé en boucle est bien plus difficile (il faut vérifier à chaque étape qu'un dossier n'existe pas déjà, qu'une ligne de configuration n'est pas déjà présente, qu'une application ne tourne pas déjà). La plupart des fonctions Ansible sont idempotentes par défaut. Enfin, la distribution : là où un script ad hoc tourne sur une machine locale, Ansible est pensé pour gérer un grand nombre de serveurs distants. On déclare les adresses IP dans un fichier hosts, on écrit un playbook, et on exécute ansible-playbook playbook.yml pour configurer les cinq serveurs en parallèle. En réglant le paramètre serial, on obtient même un déploiement progressif (rolling deployment) par lots.

Les outils de templating de serveurs

Une alternative à la gestion de configuration, en plein essor, est le templating de serveurs (server templating) avec Docker, Packer et Vagrant. Au lieu de lancer des serveurs et de les configurer en exécutant le même code sur chacun, l'idée est de créer une image de serveur capturant un instantané (« snapshot ») entièrement autonome : le système d'exploitation, les logiciels, les fichiers et tous les détails pertinents. On utilise ensuite un autre outil d'IaC pour installer cette image sur tous ses serveurs.

Il existe deux grandes catégories d'images. Une machine virtuelle (virtual machine, VM) émule un système informatique complet, matériel compris : un hyperviseur (VMware, VirtualBox, Parallels) virtualise le processeur, la mémoire, le disque et le réseau. Chaque image de VM est totalement isolée de la machine hôte et des autres VM, et se comporte exactement de la même façon dans tous les environnements ; en contrepartie, virtualiser tout ce matériel et faire tourner un OS complet par VM engendre une lourde surcharge en CPU, en mémoire et en temps de démarrage. On définit des images de VM avec Packer et Vagrant. Un conteneur (container), lui, émule seulement l'espace utilisateur d'un OS : un moteur de conteneurs (Docker, CoreOS rkt, cri-o) crée des processus, une mémoire et un réseau isolés. Tous les conteneurs d'un même serveur partagent le noyau et le matériel de l'hôte, ce qui réduit l'isolation et la sécurité par rapport à une VM, mais leur permet de démarrer en quelques millisecondes avec une surcharge quasi nulle. On définit des images de conteneurs avec Docker.

Voici un template Packer web-server.json qui crée une Amazon Machine Image (AMI), une image de VM exécutable sur AWS, configurant le même serveur Apache que précédemment.

{
  "builders": [{
    "ami_name": "packer-example",
    "instance_type": "t2.micro",
    "region": "us-east-2",
    "type": "amazon-ebs",
    "source_ami": "ami-0c55b159cbfafe1f0",
    "ssh_username": "ubuntu"
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sudo apt-get update",
      "sudo apt-get install -y php apache2",
      "sudo git clone https://github.com/brikis98/php-app.git /var/www/html/app"
    ],
    "environment_vars": [
      "DEBIAN_FRONTEND=noninteractive"
    ]
  }]
}

La seule différence avec les exemples précédents est que ce template ne démarre pas Apache : les images servent à installer le logiciel, et ce n'est qu'au moment où l'on exécute l'image — en la déployant sur un serveur — qu'on lance réellement le logiciel. Les outils ont des usages distincts : Packer crée des images destinées directement aux serveurs de production (une AMI dans un compte AWS), Vagrant des images pour les postes de développement, Docker des images d'applications individuelles. Un motif courant consiste à utiliser Packer pour créer une AMI dotée du moteur Docker, à la déployer sur un cluster de serveurs, puis à y déployer des conteneurs Docker.

Note

Le templating de serveurs est la pierre angulaire de l'infrastructure immuable (immutable infrastructure). L'idée, inspirée de la programmation fonctionnelle, est que l'on ne modifie jamais un serveur une fois déployé : pour le mettre à jour, on crée une nouvelle image à partir du template et on la déploie sur un nouveau serveur. Comme les serveurs ne changent jamais, il devient beaucoup plus facile de raisonner sur ce qui est réellement déployé.

Les outils d'orchestration

Le templating crée des VM et des conteneurs, mais comment les gérer une fois créés ? Pour la plupart des cas réels, il faut savoir : déployer les VM et conteneurs en exploitant efficacement le matériel ; déployer des mises à jour sur une flotte existante via des stratégies comme le rolling deployment, le bleu-vert (blue-green) ou le canari (canary) ; surveiller la santé des instances et remplacer automatiquement celles qui sont défaillantes (auto-réparation, auto healing) ; ajuster le nombre d'instances en fonction de la charge (mise à l'échelle automatique, auto scaling) ; répartir le trafic (équilibrage de charge, load balancing) ; et permettre aux instances de se découvrir et de communiquer (découverte de services, service discovery).

C'est le domaine des outils d'orchestration (orchestration tools) : Kubernetes, Marathon/Mesos, Amazon ECS, Docker Swarm, Nomad. Kubernetes, par exemple, permet de définir en code la gestion de ses conteneurs Docker. On déploie d'abord un cluster Kubernetes — un groupe de serveurs que Kubernetes gère et utilise pour exécuter les conteneurs — puis on décrit dans un fichier YAML un Deployment : un ou plusieurs conteneurs regroupés dans un Pod, le nombre de répliques à maintenir, et la stratégie de mise à jour. Kubernetes choisit alors automatiquement les serveurs optimaux, surveille en permanence le cluster pour garantir le nombre voulu de répliques, et remplace tout Pod défaillant. On applique le tout avec kubectl apply -f example-app.yml.

Les outils de provisionnement

Là où la gestion de configuration, le templating et l'orchestration définissent le code qui tourne sur chaque serveur, les outils de provisionnement (provisioning) — Terraform, CloudFormation, OpenStack Heat — sont responsables de la création des serveurs eux-mêmes. Et pas seulement des serveurs : aussi des bases de données, des caches, des équilibreurs de charge, des files d'attente, du monitoring, des sous-réseaux, des règles de pare-feu, des certificats SSL — presque tous les aspects de l'infrastructure. Voici un serveur web déployé avec Terraform.

resource "aws_instance" "app" {
  instance_type     = "t2.micro"
  availability_zone = "us-east-2a"
  ami               = "ami-0c55b159cbfafe1f0"

  user_data = <<-EOF
              #!/bin/bash
              sudo service apache2 start
              EOF
}

Deux paramètres méritent l'attention. Le paramètre ami indique l'identifiant de l'AMI à déployer sur le serveur — on pourrait y mettre l'AMI construite avec le template Packer de la section précédente, qui contient déjà PHP, Apache et le code source. Le paramètre user_data est un script Bash exécuté au démarrage du serveur, ici pour lancer Apache. Autrement dit, ce code montre le provisionnement et le templating de serveurs travaillant ensemble — un motif courant de l'infrastructure immuable.

Les bénéfices de l'infrastructure as code

Pourquoi se donner cette peine ? Pourquoi apprendre une floppée de langages et d'outils, et s'encombrer d'encore plus de code à maintenir ? Parce que le code est puissant. En échange de l'investissement initial pour convertir ses pratiques manuelles en code, on obtient des gains spectaculaires sur sa capacité à livrer du logiciel. Selon le State of DevOps Report 2016, les organisations qui pratiquent l'IaC déploient 200 fois plus souvent, se remettent d'un incident 24 fois plus vite, et ont des délais de mise en production 2 555 fois plus courts.

Lorsque l'infrastructure est définie en code, on peut lui appliquer toute la palette des pratiques du génie logiciel.

  • Libre-service (self-service) : la plupart des équipes qui déploient à la main dépendent d'une poignée d'administrateurs — souvent un seul — qui détiennent les formules magiques et les seuls accès à la production. C'est un goulot d'étranglement majeur. Avec l'IaC, le déploiement est automatisé et chaque développeur peut lancer le sien quand il le faut.
  • Vitesse et sécurité (speed and safety) : un processus automatisé est bien plus rapide qu'un humain, et plus sûr, car plus cohérent, plus répétable et insensible à l'erreur manuelle.
  • Documentation : au lieu de rester enfermé dans la tête d'un seul administrateur, l'état de l'infrastructure est consigné dans des fichiers que tout le monde peut lire. L'IaC fait office de documentation vivante, même quand l'administrateur est en vacances.
  • Gestion de version (version control) : on stocke ses fichiers IaC en gestion de version, et tout l'historique de l'infrastructure se retrouve dans le journal des commits. Devant un problème, le premier réflexe devient de chercher ce qui a changé, et le second de revenir à une version antérieure connue comme saine.
  • Validation : pour chaque changement, on peut faire une revue de code, lancer une suite de tests automatisés et passer le code dans des outils d'analyse statique — autant de pratiques qui réduisent fortement le risque de défauts.
  • Réutilisation (reuse) : on empaquette son infrastructure dans des modules réutilisables, pour bâtir sur des briques connues, documentées et éprouvées plutôt que de tout refaire à chaque déploiement.
  • Bonheur (happiness) : déployer et gérer l'infrastructure à la main est répétitif, fastidieux, sans créativité ni reconnaissance — on peut déployer parfaitement pendant des mois sans que personne ne le remarque, jusqu'au jour où l'on se trompe. L'IaC laisse les ordinateurs faire ce qu'ils font de mieux (l'automatisation) et les développeurs ce qu'ils font de mieux (coder).

Comment fonctionne Terraform

Voici une vue de haut niveau, quelque peu simplifiée, du fonctionnement de Terraform. C'est un outil open source créé par HashiCorp, écrit en Go. Ce code Go se compile en un unique binaire (un par système d'exploitation supporté), nommé sans surprise terraform. On peut l'utiliser pour déployer de l'infrastructure depuis son portable, un serveur de build ou presque n'importe quelle machine, sans avoir à faire tourner d'infrastructure supplémentaire. La raison : sous le capot, le binaire terraform effectue des appels d'API en votre nom vers un ou plusieurs providers — AWS, Azure, Google Cloud, DigitalOcean, OpenStack, et bien d'autres. Terraform tire ainsi parti de l'infrastructure que ces providers font déjà tourner pour leurs serveurs d'API, et des mécanismes d'authentification que vous utilisez déjà avec eux (par exemple vos clés d'API AWS).

Comment Terraform sait-il quels appels effectuer ? Vous écrivez des configurations Terraform : des fichiers texte qui spécifient l'infrastructure à créer. Ce sont elles, le « code » de l'« infrastructure as code ».

resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

resource "google_dns_record_set" "a" {
  name         = "demo.google-example.com"
  managed_zone = "example-zone"
  type         = "A"
  ttl          = 300
  rrdatas      = [aws_instance.example.public_ip]
}

Même sans jamais avoir vu de code Terraform, ce fragment se lit aisément : il demande à Terraform d'effectuer des appels d'API vers AWS pour déployer un serveur, puis vers Google Cloud pour créer une entrée DNS pointant vers l'adresse IP de ce serveur. En une syntaxe unique et simple, Terraform permet de déployer des ressources interconnectées à travers plusieurs providers cloud. On définit toute son infrastructure dans ces fichiers, on les place en gestion de version, et on lance des commandes comme terraform apply : le binaire analyse le code, le traduit en une série d'appels d'API vers les providers concernés, et les exécute aussi efficacement que possible. Quand un coéquipier doit modifier l'infrastructure, il ne touche plus aux serveurs à la main : il modifie les fichiers de configuration, valide par des tests automatisés et une revue de code, commite, puis lance terraform apply.

   Configurations Terraform (HCL)


       binaire terraform   ──parse──►  appels d'API

   ┌──────────┼──────────────┐
   ▼          ▼              ▼
  AWS       Azure       Google Cloud  ... (providers)
   │          │              │
   ▼          ▼              ▼
 serveurs, bases de données, load balancers, DNS...

À retenir

La portabilité transparente entre providers est un faux problème. On demande souvent si l'on peut écrire une infrastructure pour AWS puis la redéployer telle quelle sur Azure ou Google Cloud en quelques clics. La réalité est que les providers n'offrent pas les mêmes types d'infrastructure : les serveurs, équilibreurs et bases de données d'AWS diffèrent profondément de ceux d'Azure ou de GCP (fonctionnalités, configuration, sécurité, scalabilité, observabilité). L'approche de Terraform est de vous laisser écrire du code spécifique à chaque provider, en exploitant ses particularités, tout en utilisant le même langage, le même outillage et les mêmes pratiques d'IaC pour tous.

Terraform face aux autres outils d'IaC

L'IaC est merveilleuse ; choisir son outil l'est moins. Beaucoup d'outils se recouvrent, beaucoup sont open source, beaucoup offrent un support commercial. La plupart des comparaisons se contentent d'énumérer les propriétés générales de chacun en laissant croire que l'on réussirait aussi bien avec n'importe lequel — ce qui est techniquement vrai mais inutile, comme dire à un débutant qu'il peut bâtir un site web avec PHP, C ou de l'assembleur. Voici donc une comparaison détaillée des outils les plus populaires — Terraform, Chef, Puppet, Ansible, SaltStack, CloudFormation, OpenStack Heat — selon plusieurs axes de compromis.

Gestion de configuration ou provisionnement

Chef, Puppet, Ansible et SaltStack sont des outils de gestion de configuration ; CloudFormation, Terraform et Heat sont des outils de provisionnement. La distinction n'est pas tout à fait nette — un outil de gestion de configuration peut faire un peu de provisionnement (on peut déployer un serveur avec Ansible) et inversement (on peut exécuter des scripts de configuration sur chaque serveur provisionné par Terraform) —, mais on a tout intérêt à choisir l'outil le mieux adapté à son cas. En particulier, si vous utilisez le templating de serveurs (Docker, Packer), l'essentiel de vos besoins de gestion de configuration est déjà couvert : une fois l'image construite, il ne reste plus qu'à provisionner l'infrastructure qui l'exécute, et un outil de provisionnement sera alors le meilleur choix. Sinon, une bonne alternative est de combiner les deux : Terraform pour provisionner les serveurs, Chef pour les configurer.

Infrastructure mutable ou immuable

Les outils de gestion de configuration adoptent par défaut un paradigme d'infrastructure mutable (mutable infrastructure). Si vous demandez à Chef d'installer une nouvelle version d'OpenSSL, il exécute la mise à jour sur place, sur les serveurs existants. Au fil des mises à jour, chaque serveur accumule un historique unique de changements et finit par différer subtilement des autres — la même dérive de configuration que dans la gestion manuelle. Ces bugs sont difficiles à attraper, même avec des tests automatisés : un changement peut fonctionner sur un serveur de test mais se comporter différemment en production, parce que celle-ci a accumulé des mois de modifications absentes de l'environnement de test.

Avec un outil de provisionnement déployant des images Docker ou Packer, la plupart des « changements » sont en réalité le déploiement d'un serveur entièrement neuf. Pour passer à une nouvelle version d'OpenSSL, on crée une nouvelle image avec Packer, on la déploie sur des serveurs frais, puis on détruit les anciens. Comme chaque déploiement repose sur des images immuables sur des serveurs neufs, cette approche réduit le risque de dérive, permet de savoir exactement quel logiciel tourne sur chaque serveur, et de redéployer à tout moment n'importe quelle version antérieure. Elle a aussi ses revers : reconstruire une image et redéployer tous ses serveurs pour un changement trivial peut prendre du temps, et l'immuabilité ne dure que jusqu'au démarrage de l'image — une fois en route, le serveur commence à écrire sur son disque et dérive un peu (effet atténué si l'on déploie fréquemment).

Langage procédural ou déclaratif

Chef et Ansible encouragent un style procédural : on écrit un code qui décrit, étape par étape, comment atteindre un état final. Terraform, CloudFormation, SaltStack, Puppet et Heat encouragent un style déclaratif : on décrit l'état final désiré, et l'outil se charge de déterminer comment y parvenir. Imaginons que l'on veuille déployer dix serveurs (des instances EC2 dans le jargon AWS).

# Ansible : approche procédurale
- ec2:
    count: 10
    image: ami-0c55b159cbfafe1f0
    instance_type: t2.micro
# Terraform : approche déclarative
resource "aws_instance" "example" {
  count         = 10
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

En surface, les deux se ressemblent et produisent d'abord le même résultat. Tout l'intérêt apparaît au moment de changer. Si le trafic augmente et que l'on veut passer à quinze serveurs, le code Ansible n'est plus d'aucune aide : modifier count à 15 et relancer déploierait 15 nouveaux serveurs, soit 25 au total. Il faut donc savoir ce qui est déjà déployé et écrire un nouveau script qui n'ajoute que les cinq manquants. Avec Terraform, comme on ne fait que déclarer l'état voulu, l'outil connaît l'état qu'il a créé par le passé : il suffit de revenir à la même configuration et de passer count de 10 à 15. Avant d'appliquer, on peut prévisualiser les changements avec la commande plan.

$ terraform plan

# aws_instance.example[11] will be created
+ resource "aws_instance" "example" {
    + ami           = "ami-0c55b159cbfafe1f0"
    + instance_type = "t2.micro"
    + (...)
  }
# ... [12], [13], [14]

Plan: 5 to add, 0 to change, 0 to destroy.

De même, pour déployer une autre version de l'application, on change simplement le paramètre ami dans la même configuration. Cela révèle deux problèmes majeurs des outils procéduraux. D'abord, le code procédural ne capture pas pleinement l'état de l'infrastructure : lire les trois templates Ansible ne suffit pas à savoir ce qui est déployé, il faut aussi connaître l'ordre dans lequel ils ont été appliqués — donc l'historique complet de chaque changement. Ensuite, le code procédural limite la réutilisabilité : il faut tenir compte à la main de l'état courant, qui change constamment, si bien qu'un code écrit la semaine dernière peut déjà être inutilisable. Les bases de code procédurales tendent à grossir et à se compliquer. Avec l'approche déclarative de Terraform, le code représente toujours le dernier état de l'infrastructure : d'un coup d'œil, on sait ce qui est déployé et comment, sans se soucier de l'historique ni du timing, ce qui facilite grandement la réutilisation.

Astuce

Le déclaratif a aussi des limites : sans langage de programmation complet, le pouvoir d'expression est restreint, et certaines opérations (déploiement sans interruption, logique conditionnelle, boucles) sont délicates à exprimer. Terraform compense avec des primitives puissantes — variables d'entrée et de sortie, modules, create_before_destroy, count, syntaxe ternaire, fonctions intégrées — qui rendent possible un code propre, configurable et modulaire même en déclaratif.

Avec ou sans master, avec ou sans agent

Par défaut, Chef, Puppet et SaltStack exigent un serveur master chargé de stocker l'état de l'infrastructure et de distribuer les mises à jour : un client envoie ses commandes au master, qui les pousse vers les autres serveurs (ou que ces derniers récupèrent périodiquement). Un master offre un point central de gestion, parfois doté d'une interface web, et peut tourner en continu pour faire respecter la configuration et corriger les changements manuels. Mais il a de sérieux inconvénients : une infrastructure supplémentaire à déployer (voire un cluster pour la haute disponibilité), de la maintenance (mises à jour, sauvegardes, supervision), et une sécurité dégradée (ports et systèmes d'authentification supplémentaires, donc surface d'attaque accrue). Ansible, CloudFormation, Heat et Terraform sont sans master par défaut : Terraform communique via les API du provider cloud — ces serveurs d'API jouent le rôle de master, mais sans infrastructure ni authentification à gérer en plus (vos clés d'API suffisent) — et Ansible se connecte directement à chaque serveur par SSH.

De même, Chef, Puppet et SaltStack exigent l'installation d'un agent (Chef Client, Puppet Agent, Salt Minion) sur chaque serveur géré. Cela pose le problème de l'amorçage (bootstrapping : comment provisionner les serveurs et installer l'agent au départ ?), de la maintenance de cet agent, et de la sécurité (ouverture de ports entrants ou sortants, authentification de l'agent). Toutes ces pièces mobiles introduisent de nombreux modes de défaillance : devant un bug à 3 heures du matin, il faut déterminer s'il vient du code applicatif, du code IaC, du client de gestion de configuration, du master, ou de leurs communications. Ansible, CloudFormation, Heat et Terraform ne requièrent aucun agent supplémentaire : les agents nécessaires sont déjà installés et gérés par le provider cloud sur ses propres serveurs. En tant qu'utilisateur de Terraform, vous émettez simplement des commandes et les agents du provider les exécutent pour vous.

  Chef / Puppet / SaltStack          Terraform
  (master + agent)                   (masterless, agentless)

  votre poste                        votre poste
      │ commande                         │ commande
      ▼                                  ▼
  serveur MASTER  ──pousse──►        API du provider cloud (AWS...)
      │                                  │
      ▼                                  ▼
  agents sur chaque serveur          agents DÉJÀ gérés par le provider
  (à installer, maintenir, sécuriser)  (rien à installer côté client)

Grande ou petite communauté, mature ou de pointe

Choisir une technologie, c'est aussi choisir une communauté : nombre de contributeurs, de plugins et d'intégrations, facilité à trouver de l'aide en ligne ou à recruter. Le tableau suivant résume des données recueillies en mai 2019.

OutilSourceCloudContributeursÉtoilesBibliothèquesQuestions SO
ChefOpenTous5625 7943 8325 982
PuppetOpenTous5155 2996 1103 585
AnsibleOpenTous4 38637 16120 67711 746
SaltStackOpenTous2 2379 9013181 062
CloudFormationFerméAWS seul??3773 315
HeatOpenTous361349088
TerraformOpenTous1 26116 8371 4622 730

Deux tendances ressortent. D'abord, tous ces outils sont open source et multi-cloud, sauf CloudFormation, qui est fermé et fonctionne uniquement avec AWS. Ensuite, Ansible domine en popularité, suivi de près par Salt et Terraform. Surtout, comparé à la première édition (septembre 2016), Terraform et Ansible connaissent une croissance explosive : pour Terraform, le nombre de bibliothèques a bondi de plus de 3 500 %, les questions sur StackOverflow de près de 2 000 %, et les offres d'emploi de plus de 8 000 %.

Côté maturité, le tableau est plus nuancé. Puppet (2005) et Chef (2009) sont les plus anciens ; Terraform (2014) est de loin le plus jeune. À l'époque de l'écriture, il était encore pré-1.0.0 : aucune garantie d'API stable ou rétrocompatible, et des bugs relativement fréquents (quoique mineurs). C'est sa plus grande faiblesse — le prix à payer pour un outil neuf et de pointe, devenu pourtant extrêmement populaire en très peu de temps.

Combiner plusieurs outils

Malgré toutes ces comparaisons, la réalité est que l'on aura généralement besoin de plusieurs outils ensemble, chacun ayant ses forces et ses faiblesses. Trois combinaisons fonctionnent particulièrement bien :

  • Provisionnement + gestion de configuration (Terraform + Ansible) : Terraform déploie toute l'infrastructure sous-jacente (réseau VPC, sous-réseaux, tables de routage, bases de données, équilibreurs, serveurs), Ansible déploie ensuite les applications par-dessus. Facile à démarrer (les deux sont des applications client uniquement), mais Ansible implique beaucoup de code procédural et de serveurs mutables, dont la maintenance se complique avec la croissance.
  • Provisionnement + templating de serveurs (Terraform + Packer) : Packer empaquette les applications en images de VM, Terraform déploie les serveurs portant ces images ainsi que le reste de l'infrastructure. Approche immuable, donc plus facile à maintenir, mais les VM sont lentes à construire et les stratégies de déploiement de Terraform sont limitées.
  • Provisionnement + templating + orchestration (Terraform + Packer + Docker + Kubernetes) : Packer crée une image de VM avec Docker et Kubernetes, Terraform déploie un cluster de serveurs portant cette image et le reste de l'infrastructure, et le cluster forme un environnement Kubernetes pour exécuter les applications conteneurisées. On bénéficie de toutes les fonctionnalités intégrées de Kubernetes (stratégies de déploiement, auto-réparation, auto-scaling), au prix d'une complexité accrue et de plusieurs couches d'abstraction à apprendre et à déboguer.

Le verdict

Piège courant

Le tableau de synthèse ci-dessous montre l'usage par défaut ou le plus courant de chaque outil. Ces outils restent flexibles : on peut utiliser Chef sans master, ou Salt pour faire de l'infrastructure immuable. Ne lisez donc pas ces lignes comme des absolus, mais comme le mode idiomatique de chacun.

OutilTypeInfrastructureLangageAgentMasterCommunautéMaturité
ChefConfig MgmtMutableProcéduralOuiOuiGrandeHaute
PuppetConfig MgmtMutableDéclaratifOuiOuiGrandeHaute
AnsibleConfig MgmtMutableProcéduralNonNonÉnormeMoyenne
SaltStackConfig MgmtMutableDéclaratifOuiOuiGrandeMoyenne
CloudFormationProvisioningImmuableDéclaratifNonNonPetiteMoyenne
HeatProvisioningImmuableDéclaratifNonNonPetiteFaible
TerraformProvisioningImmuableDéclaratifNonNonÉnormeFaible

Ce que l'auteur et son entreprise, Gruntwork, recherchaient : un outil de provisionnement open source, agnostique du cloud, supportant l'infrastructure immuable, un langage déclaratif, une architecture sans master ni agent, doté d'une grande communauté et d'une base de code mature. Terraform, bien qu'imparfait (sa jeunesse reste son talon d'Achille), est celui qui se rapproche le plus de tous ces critères.

Note

Depuis la publication de l'ouvrage, le paysage a évolué : Terraform a atteint sa version 1.0 stable, et Pulumi s'est imposé comme une alternative de provisionnement notable. Pulumi reprend le modèle déclaratif et l'état (state) de Terraform, mais permet d'écrire ses configurations dans de véritables langages de programmation (TypeScript, Python, Go) plutôt que dans un langage dédié comme HCL — un compromis intéressant entre l'expressivité d'un langage généraliste et la rigueur du modèle déclaratif.

À retenir

  • L'infrastructure as code (IaC) consiste à écrire et exécuter du code pour définir, déployer, mettre à jour et détruire son infrastructure ; elle se décline en cinq catégories : scripts ad hoc, gestion de configuration (Chef, Puppet, Ansible, Salt), templating de serveurs (Docker, Packer), orchestration (Kubernetes) et provisionnement (Terraform, CloudFormation, Heat).
  • Ses bénéfices sont concrets : libre-service, vitesse et sécurité, documentation vivante, gestion de version, validation par revue et tests, réutilisation via des modules, et bonheur des équipes — les organisations qui la pratiquent déploient 200 fois plus souvent.
  • Terraform est un binaire Go unique, open source, sans master ni agent : il traduit des configurations en HCL en appels d'API vers des providers (AWS, Azure, GCP…), en s'appuyant sur l'infrastructure et l'authentification que ces providers fournissent déjà.
  • La portabilité transparente entre clouds est un faux problème : les providers n'offrent pas les mêmes ressources ; Terraform vous laisse écrire du code spécifique à chacun, avec le même langage et les mêmes pratiques.
  • Le grand partage est mutable contre immuable et procédural contre déclaratif : Terraform privilégie l'infrastructure immuable et le déclaratif, où le code représente toujours le dernier état désiré et où terraform plan prévisualise les changements — au prix d'un pouvoir d'expression plus limité, compensé par des primitives (variables, modules, count).
  • On combine souvent les outils : Terraform + Packer + Docker + Kubernetes est un motif courant où chacun joue sur sa force (provisionnement, templating, orchestration).
  • Gruntwork a choisi Terraform pour son profil — provisionnement open source, agnostique du cloud, immuable, déclaratif, sans master ni agent, grande communauté — sa jeunesse étant sa principale faiblesse ; depuis, Terraform a atteint la 1.0 et Pulumi a émergé comme alternative écrite en langages généralistes.