2-05 Bonnes pratiques d'organisation des fichiers et dossiers d'un projet
Objectifs
- Comprendre comment bien structurer ses projets dans Terraform avec Terragrunt
Architecture modulaire
Point sur les parties statiques et dynamiques de l'infrastructure
Certaines parties de l'infrastructure vont évoluer plus rapidement et nécessiter des mises à jour plus fréquentes que d'autres.
Pour prendre le cas le plus sensible, une base de données de production n'est pas censé bouger, hormis quand on orchestre une migration pour changer de version.
Ces parties de l'infrastructure peuvent considérées comme "statiques".
À l'inverse, le code d'une application peut évoluer plusieurs fois par jour, et dans une infrastructure élastique les noeuds de calculs sont par nature transients.
On va appeler "dynamiques" ces parties de l'infrastructure.
Il y a a plusieurs avantages à séparer dans l'IAC les parties statiques et dynamiques.
- Assurance : éviter le risque de détruire par erreur une base de données
- Rapidité : éviter de perdre du temps dans de la planification et du déploiement
- Sobriété : éviter les surconsommations d'énergie / réseau / disque
Conséquence 1 : séparer le code des différentes parties
On va segmenter le projet en modules différents.
Dans notre exemple, le déploiement de la base de données et la gestion des serveurs web sont dans deux modules séparés.
Conséquence 2 : utiliser des méthodes de déploiement légères pour les parties dynamiques
On va utiliser des outils adaptés à de la CI/CD pour les déploiements réguliers.
La bonne solution dépend des compétences techniques de l'équipe.
Aujourd'hui on conseillerait d'utiliser Kubernetes et des images de conteneurs comme méthode de déploiement du code.
Mais K8s n'est pas simple, et dans des cas plus raisonnables on peut utiliser ansible en coordination avec des utilitaires de déploiement continu.
Ansible va utiliser un inventaire dynamique, basé sur les API du cloud provider voire même en utilisant l'état Terraform.
Les utilitaires de déploiement vont récupérer à la demande les nouvelles versions du code, qui peut être déployé via
- des conteneurs : docker-compose
- des
git pull
: capistrano et ses alternatives
Point sur l'architecture modulaire, les workspaces et les fichiers d'états
Notre vision de Terraform a évolué : il y a des limitations d'usage dans ces techniques un peu complexes, et on voit qu'il faut bien structurer son code à terme.
Nous allons voir que sur cette base le logiciel terragrunt
va nous permettre de résoudre plusieurs problèmes avec quelques principes de base.
- Don't Repeat Yourself (DRY) : éviter la répétition du code
- Usage de conventions structurelles : travailler uniquement avec des modules
- Standardisation des environnements : gestions des états d'infrastructure
Terragrunt
La ligne de commande terragrunt
À la base, Terragrunt est une "surcouche" qui va piloter terraform pour vous.
Les versions de Terragrunt sont à aligner avec celles de Terraform.
Pour lancer Terragrunt, on utilisera les mêmes actions que Terraform
$ terragrunt init
$ terragrunt plan
$ terragrunt apply
$ terragrunt destroy
Mais là où Terragrunt va présenter une différence, c'est qu'il peut opérer sur plusieurs recettes à la fois.
$ terragrunt run-all apply
$ terragrunt run-all destroy
Terragrunt : Terraform sous stéroïdes
Avec Terragrunt, on passe d'une structure redondante :
└── live
├── prod
│ ├── app
│ │ └── main.tf + others
│ ├── mysql
│ │ └── main.tf + others
│ └── vpc
│ └── main.tf + others
├── qa
│ ├── app
│ │ └── main.tf + others
│ ├── mysql
│ │ └── main.tf + others
│ └── vpc
│ └── main.tf + others
└── stage
├── app
│ └── main.tf + others
├── mysql
│ └── main.tf + others
└── vpc
└── main.tf + others
à ça
└── live
├─ terragrunt.hcl
├── prod
│ ├── app
│ │ └── terragrunt.hcl
│ ├── mysql
│ │ └── terragrunt.hcl
│ └── vpc
│ └── terragrunt.hcl
├── qa
│ ├── app
│ │ └── terragrunt.hcl
│ ├── mysql
│ │ └── terragrunt.hcl
│ └── vpc
│ └── terragrunt.hcl
└── stage
├── app
│ └── terragrunt.hcl
├── mysql
│ └── terragrunt.hcl
└── vpc
└── terragrunt.hcl
Gestion des dépendances
Chaque fichier terragrunt.hcl
contient les informations nécessaires pour gérer l'infrastructure via Terraform.
terraform {
# Deploy version v0.0.3 in stage
source = "git::git@github.com:foo/modules.git//app?ref=v0.0.3"
}
inputs = {
instance_count = 3
instance_type = "t2.micro"
}
Gestion des backends de fichiers d'état
On a vu une limitation des backends : impossible d'utiliser des variables quand on déclare un backend de remote state.
Et il fallait créer le bucket S3 pour stocker notre remote state séparément.
Avec Terragrunt, on a une syntaxe qui va prendre ça en charge.
remote_state {
backend = "s3"
generate = {
path = "backend.tf"
if_exists = "overwrite"
}
config = {
bucket = "my-terraform-state"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "my-lock-table"
}
}
Gestion de l'architecture DRY
Pour éviter de se répéter entre configurations d'environnement, Terragrunt utilise une directive include
.
└── live
├── terragrunt.hcl
├── _env
│ ├── app.hcl
│ ├── mysql.hcl
│ └── vpc.hcl
├── prod
│ ├── app
│ │ └── terragrunt.hcl
│ ├── mysql
│ │ └── terragrunt.hcl
│ └── vpc
│ └── terragrunt.hcl
## File: _env:app.hcl
terraform {
source = "github.com/<org>/modules.git//app?ref=v0.1.0"
}
dependency "vpc" {
config_path = "../vpc"
}
dependency "mysql" {
config_path = "../mysql"
}
inputs = {
basename = "example-app"
vpc_id = dependency.vpc.outputs.vpc_id
subnet_ids = dependency.vpc.outputs.subnet_ids
mysql_endpoint = dependency.mysql.outputs.endpoint
}
## File: prod/app/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
include "env" {
path = "${get_terragrunt_dir()}/../../_env/app.hcl"
}
inputs = {
env = "prod"
}
Bonnes pratiques pour le module racine
Il est conseillé d'intégrer certains fichiers sont dans le module racine :
- versions.tf
terraform {
required_version = ">= 0.14"
required_providers {
aws = "= 3.28"
}
}
- providers.tf
provider "google" {
project = var.project_id
region = var.region
}
- README.md
Ces fichiers définissent la documentation et configuration de base de votre recette.
Cependant rien n'interdit un module d'avoir sa propre documentation ou de surcharger un provider (par exemple pour utiliser une version différente).
Rappel des objectifs
- Comprendre comment bien structurer ses projets dans Terraform avec Terragrunt