Tu veux découvrir Terraform sans te prendre la tête avec un cloud provider ? Bonne nouvelle : on peut tout apprendre en local avec Docker. C'est exactement ce qu'on va faire dans cet article.
Installer Terraform
Direction le site officiel pour télécharger le binaire qui correspond à ton système.
Pour vérifier que tout est bon :
1$ terraform --version
2Terraform v1.9.x
3on darwin_arm64La syntaxe en 2 minutes
Avant de se lancer, voici les bases de la syntaxe Terraform.
Assigner une valeur
1image_id = "1234"Définir une ressource
1resource "container" "example" {
2 name = "1234"
3 network_interface {
4 # ...
5 }
6}Les types de variables
1"1234" // String
21234 // Number
3true / false // Boolean
4[0, 5, 2, 4] // List
5{ "key" = "value" } // MapRien de sorcier. On passe à la pratique.
Configurer le provider Docker
On va créer deux fichiers :
1|-- main.tf
2|-- provider.tfDans provider.tf, on dit à Terraform d'utiliser le provider Docker :
1terraform {
2 required_providers {
3 docker = {
4 source = "kreuzwerker/docker"
5 version = "~> 3.0"
6 }
7 }
8}
9
10provider "docker" {
11 host = "unix:///var/run/docker.sock"
12}On initialise le projet (ça télécharge le provider) :
1$ terraform initLancer un premier container
Dans main.tf, on définit une image et un container :
1resource "docker_image" "ubuntu" {
2 name = "ubuntu:precise"
3}
4
5resource "docker_container" "ubuntu" {
6 name = "foo"
7 image = docker_image.ubuntu.latest
8}C'est assez lisible : on pull une image Ubuntu, puis on crée un container basé dessus.
Pour appliquer :
1$ terraform applyTerraform te montre ce qu'il va faire. Tape yes pour confirmer.
Astuce :
terraform plante montre les changements sans rien appliquer. Pratique pour vérifier avant de lancer.
Le fichier state
Terraform crée un fichier terraform.tfstate qui garde en mémoire l'état de ton infra. C'est grâce à lui qu'il sait ce qu'il doit créer, modifier ou supprimer.
Règle d'or : ne touche jamais à ce fichier manuellement. Si tu bosses en équipe, stocke-le sur un backend distant.
Un exemple plus concret : PostgreSQL + Adminer
On va monter un vrai setup avec PostgreSQL et Adminer (une interface web pour gérer la DB). Et on va le faire proprement avec des modules.
Nouvelle structure :
1|-- main.tf
2|-- provider.tf
3|-- modules
4| |-- postgres
5| | |-- provider.tf
6| | |-- postgres.tf
7| | |-- adminer.tfLe module postgres
Dans main.tf, on appelle notre module :
1module "postgres" {
2 source = "./modules/postgres"
3}Pense à relancer terraform init quand tu ajoutes un module.
postgres.tf
1resource "docker_image" "postgres" {
2 name = "postgres"
3 keep_locally = true
4}
5
6resource "docker_container" "postgres" {
7 name = "postgres"
8 image = docker_image.postgres.latest
9 shm_size = 4000
10 ports {
11 external = 5432
12 internal = 5432
13 }
14 volumes {
15 container_path = "/var/lib/postgresql/data"
16 host_path = abspath("tmp/postgres")
17 }
18 env = [
19 "POSTGRES_USER=user",
20 "POSTGRES_PASSWORD=password"
21 ]
22}Si tu connais Docker, tu retrouves tes marques :
- shm_size : mémoire partagée (obligatoire pour Postgres en local)
- ports : mapping de ports classique
- volumes : persistance des données
- env : variables d'environnement
adminer.tf
1resource "docker_image" "adminer" {
2 name = "adminer"
3 keep_locally = true
4}
5
6resource "docker_container" "adminer" {
7 name = "adminer"
8 image = docker_image.adminer.latest
9 ports {
10 external = 8080
11 internal = 8080
12 }
13 env = [
14 "ADMINER_DEFAULT_SERVER=${docker_container.postgres.name}"
15 ]
16 depends_on = [
17 docker_container.postgres
18 ]
19}Le truc cool ici : on référence le container Postgres avec docker_container.postgres.name. Terraform gère les dépendances automatiquement, mais depends_on permet d'être explicite.
Les outputs
Tu peux exposer des valeurs depuis un module. Crée un fichier outputs.tf :
1output "postgres_container_name" {
2 value = docker_container.postgres.name
3}Cette valeur sera accessible via module.postgres.postgres_container_name.
Aller plus loin : les variables
Pour rendre ton module configurable, utilise des variables.
Dans main.tf, on crée un network et on le passe au module :
1resource "docker_network" "default" {
2 name = "local"
3}
4
5module "postgres" {
6 source = "./modules/postgres"
7 network_name = docker_network.default.name
8}Dans le module, crée variables.tf :
1variable "network_name" {
2 type = string
3}Et utilise-la dans postgres.tf :
1resource "docker_container" "postgres" {
2 name = "${var.network_name}-postgres"
3 # ... reste de la config ...
4 networks_advanced {
5 name = var.network_name
6 }
7}La syntaxe ${var.network_name} permet d'insérer la variable dans une string.
Tu maîtrises maintenant les bases de Terraform : providers, ressources, modules, variables et outputs. Tout ça sans avoir dépensé un centime en cloud.
Prêt à passer aux choses sérieuses ? J'ai écrit un article sur le déploiement d'infrastructure Scaleway avec Terraform qui applique ces concepts sur un vrai cloud provider.