Vous travaillez dans une équipe DevOps.
Votre équipe doit déployer plusieurs environnements de test pour différentes équipes :
Chaque équipe a besoin :
Problème actuel :
Objectif :
Créer une solution réutilisable permettant de déployer plusieurs conteneurs rapidement et sans erreur.
mkdir -p terraform-td2/modules/nginx cd terraform-td2
terraform-td2/
main.tf
modules/
nginx/
main.tf
variables.tf
outputs.tf
Objectif : créer un composant réutilisable
Fichier : modules/nginx/variables.tf
variable "container_name" {
description = "Nom du conteneur"
}
variable "external_port" {
description = "Port exposé"
}
Les variables permettent de rendre un module réutilisable.
Sans variables :
Avec des variables :
Fichier : modules/nginx/main.tf
resource "docker_image" "nginx" {
name = "nginx:latest"
}
resource "docker_container" "nginx" {
name = var.container_name
image = docker_image.nginx.name
ports {
internal = 80
external = var.external_port
}
}
Fichier : modules/nginx/outputs.tf
output "url" {
value = "http://localhost:${var.external_port}"
}
Les outputs permettent d’exposer des informations produites par Terraform.
Ils sont souvent utilisés pour :
Fichier : main.tf
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
}
}
}
provider "docker" {}
module "nginx1" {
source = "./modules/nginx"
container_name = "frontend"
external_port = 8080
}
module "nginx2" {
source = "./modules/nginx"
container_name = "backend"
external_port = 8081
}
Dans le dossier terraform-td2 :
terraform init terraform plan terraform apply
Vérifier :
Fichier : main.tf
Modifier la configuration pour utiliser le même port :
external_port = 8080
Relancer :
terraform apply
Questions :
terraform output
Question :
Un module Terraform fonctionne comme une fonction :
Le fichier terraform.tfstate contient l’état réel de l’infrastructure.
Terraform compare :
pour déterminer les actions à effectuer (création, modification, suppression).
Fichier : modules/nginx/variables.tf
Rendre le module plus flexible :
variable "image_name" {
description = "Image Docker"
default = "nginx:latest"
}
Fichier : modules/nginx/main.tf
Modifier :
resource "docker_image" "nginx" {
name = var.image_name
}
Test :
Créer un troisième environnement :
Afficher toutes les URLs sous forme de map :
output "urls" {
value = {
nginx1 = module.nginx1.url
nginx2 = module.nginx2.url
}
}
Une map permet d’associer explicitement une valeur à une clé (ex : environnement → URL).
Avantages :
Une liste ne garantit pas le lien entre la position et la signification.
Jusqu’à présent, vous avez défini plusieurs modules manuellement :
Ce fonctionnement pose un problème :
Objectif :
Factoriser la configuration pour décrire les environnements sous forme de données, et laisser Terraform générer les ressources.
Fichier : main.tf
locals {
environments = {
frontend = {
port = 8080
}
backend = {
port = 8081
}
data = {
port = 8082
}
}
}
Cette structure est une map d’objets :
Elle permet de décrire l’infrastructure sous forme de données, plutôt que de multiplier les blocs Terraform.
Supprimer les blocs :
module "nginx1" { ... }
module "nginx2" { ... }
Remplacer par :
module "nginx" {
for_each = local.environments
source = "./modules/nginx"
container_name = each.key
external_port = each.value.port
}
for_each permet de créer plusieurs instances d’un même bloc à partir d’une collection.
Pour chaque élément :
each.key correspond au nom (ex : frontend)each.value correspond à la configuration associéeTerraform crée une instance par entrée dans la map.
Question :
Fichier : main.tf
output "urls" {
value = {
for env, mod in module.nginx :
env => mod.url
}
}
Cet output reconstruit une map en reprenant :
Cela permet de conserver une structure cohérente entre les entrées (environments) et les sorties (urls).
terraform apply terraform output
Question :
map + for_each.
Un développeur renomme la clé frontend en front.
Vous avez déployé plusieurs conteneurs nginx avec Terraform.
Chaque conteneur est accessible via une URL différente :
Problème :
Objectif :
Configurer dynamiquement chaque conteneur avec Ansible.
Créer un dossier :
mkdir ansible cd ansible
Créer un fichier playbook.yml :
- name: Configuration des conteneurs nginx
hosts: all
gather_facts: false
tasks:
- name: Copier une page HTML personnalisée
copy:
content: |
<h1>Environnement : {{ inventory_hostname }}</h1>
<p>URL : http://localhost:{{ ansible_port }}</p>
dest: /usr/share/nginx/html/index.html
Chaque conteneur aura une page différente en fonction de son nom.
Vous utilisez ici les variables Ansible :
Ansible a besoin d’un inventory pour savoir :
Or, ces informations sont connues par Terraform.
Comment faire le lien entre les deux ?
Modifier Terraform pour générer un fichier inventory.ini.
Indice :
templatefilelocal_fileExemple attendu :
[web] frontend ansible_host=localhost ansible_port=8080 backend ansible_host=localhost ansible_port=8081 data ansible_host=localhost ansible_port=8082
Utiliser les outputs Terraform pour générer l’inventory avec un script externe.
terraform output -json > outputs.json
Transformer ce fichier en inventory.ini.
Langage au choix :
Lancer Ansible :
ansible-playbook -i inventory.ini playbook.yml
Vérifier :
Questions :
env_type (dev, prod…)Objectif :
Comprendre comment une information définie dans Terraform peut être utilisée dans Ansible.
C’est un cas réel fréquent en DevOps.
Contrairement à Terraform :
Un module Terraform devient :
Un for_each devient :
mkdir pulumi-td2 cd pulumi-td2 pulumi new typescript
Choisir :
Installer le provider Docker :
npm install @pulumi/docker
Créer un fichier nginx.ts :
import * as docker from "@pulumi/docker";
export function createNginx(name: string, port: number) {
const image = new docker.RemoteImage(name, {
name: "nginx:latest",
});
const container = new docker.Container(name, {
image: image.imageId,
ports: [
{
internal: 80,
external: port,
},
],
});
return {
url: `http://localhost:${port}`,
};
}
En Pulumi, on remplace les modules Terraform par du code réutilisable.
Modifier index.ts :
import { createNginx } from "./nginx";
const frontend = createNginx("frontend", 8080);
const backend = createNginx("backend", 8081);
export const frontendUrl = frontend.url;
export const backendUrl = backend.url;
pulumi up
Equivalent du for_each Terraform :
import { createNginx } from "./nginx";
const environments: Record<string, number> = {
frontend: 8080,
backend: 8081,
data: 8082,
};
const urls: Record<string, string> = {};
for (const [name, port] of Object.entries(environments)) {
const nginx = createNginx(name, port);
urls[name] = nginx.url;
}
export { urls };
const ports = Object.values(environments);
if (new Set(ports).size !== ports.length) {
throw new Error("Ports dupliqués détectés !");
}
Contrairement à Terraform, Pulumi permet d’ajouter des validations personnalisées avant même le déploiement.
Questions :
Pulumi permet :
Mais :
Terraform et Pulumi répondent au même besoin avec deux approches :
Dans un projet réel :