Travailler en équipe sous Terraform
!
Je suis membre de l’équipe Azure [aka CSA] au sein d’ESKER qui est composée de 6 membres. Nous avons mis en œuvre Terraform
pour réaliser le déploiement de la solution ESKER sur un nouveau data-center
Azure. Dans cet article nous allons nous interroger sur le travail en équipe avec Terraform
. Commençons tout de suite par une phrase qui pourrait être énoncée trivialement par un développeur :
“Facile, tu pousses tout dans
git
!?”
En effet, il faut mettre le code dans un gestionnaire de sources mais il faut aussi partager le fameux tfstate
pour réaliser les commandes Terraform
. Au final, tout ceci n'est pas si évident dans la pratique, et voici donc les douleurs et les solutions que nous avons rencontrées les tous premiers jours en phase de prototypage.
Partager le tfstate
!
Le tfstate
est la base de données qui permet à Terraform
de savoir s'il doit détruire, créer ou modifier les ressources.
La peine tfstate
Par défaut terraform
sauve le tfstate
dans le dossier de travail en local. Et donc si vous travaillez chacun dans votre coin, il y aura autant de tfstate
que de développeurs. Et il n'y a aucun moyen de les fusionner plus tard ! Il n'est donc vraiment pas possible de travailler en équipe de cette façon.
S'ajoute que le tfstate
doit être soigné comme la prunelle de ses yeux ! Le perdre vous obligerait à retrouver tous les ID des ressources et les faire correspondre avec les ID de Terraform,
ce qui représente une énorme dose de boulot !
TERRAFORM a tout prévu !
Pour partager le tfstate
efficacement, il faut déclarer un lieu de stockage centralisé disponible pour tous appelé backend
. Les commandes Terraform
verrouillent l'accès au tfstate
et permettent donc de travailler sereinement en équipe ! Par exemple, nous utilisons les StorageAccount
, Azure
étant aussi notre provider
de ressources. Le choix du backend
n'est pas vraiment une difficulté, et il se fait en fonction de votre environnement. Il faut juste savoir que terraform
supporte quelques backends
incompatibles avec la fonctionnalité de lock
. Pour revenir à Azure, voici un exemple de déclaration :
terraform {
backend "azurerm" {
storage_account_name = "mystorageAccount"
container_name = "myContainer"
key = "myState.tfstate"
}
}
Pensez donc à faire un système de backup, ainsi qu’à la sécurité : il y a certains de vos secrets dans ce fichier !
Et le Code ?
Nous pourrions dire :
“Facile, tu pousses tout dans
git
!?”
Presque mais non ! Encore une fois le tfstate
fait parler de lui !
Les problèmes commencent
Si vous réalisez un seul workspace
- autrement dit l'ensemble de vos fichiers .tf
dans un même dossier, votre tfstate
va être de plus en plus gros et finir par ralentir les commandes de plan
et apply
! S'ajoute que faire des opérations de maintenance du tfstate
(renommer, importer des ressources...) avec une personne qui en parallèle veut pousser sa modification provoque des blocages non souhaitables. Et on ne parle que du cas favorable dans lequel les gens se parlent ! Bref, pas terrible !
Diviser pour régner
Notre conclusion est qu’il faut découper en plusieurs workspaces
- sous entendu avoir plusieurs bases de code et donc autant de tfstate
. Nous avons fait le choix de découper fonctionnellement des workspaces
par rôle : persistance, frontend, backend... Le tout est complété par un workspace
technique transverse pour le réseau.
Reste à savoir comment stocker tout ceci et donc enfin parler de repository
, sous entendu git
, mercurial
et compagnie ! La documentation Terraform Entreprise
fait plusieurs propositions - elle en parle, parlons-en :
1. Un seul repository
et un dossier par workspace :
- PRO, je clone un seul repo sur ma machine
- CON, la plateforme d’intégration et livraison continue aka CI/CD empile les
plan
pour l'ensemble desworkspaces
, à chaquecommit
! L'enfer c'est l'autre
2. Un seul repository
mais une branche par workspace :
- PRO, je clone un seul
repository
sur ma machine - CON, la maintenance du code sur plusieurs
workspaces
devient une vraie peine
3. Un repository
par workspace :
- PRO, la CI/CD devient plus lisible, chaque
workspace
a son propre cycle de vie - CON, devoir cloner plusieurs
repositories
pour travailler en local
Nous avons expérimenté les deux premières en phase de prototypage. Et nous en sommes actuellement à un repository
par workspace
pour une meilleure intégration à la CI/CD. Pour compenser la peine, nous avons mis en place un script pour initier tous les repositories
pour les machines de développement.
En découpant le code, vous pourriez avoir besoin d'informations qui sont publiées dans un autre tfstate
. Vous pouvez utiliser la ressource remote_state
par exemple :
# Déclaration data "terraform_remote_state" "my_state" {
backend = "azurerm"
config {
storage_account_name = "mystorageAccount"
container_name = "myContainer"
key = "myState.tfstate"
}
}# Utilisation locals {
subnet_id= "data.terraform_remote_state.my_state.my_subnet_id"
}
Et maintenant
Vous pensez que c’est joué ? Et bien pas du tout ! A ce stade, le développeur aura des surprises dans le plan
avec des ressources ajoutées dans le dernier commit
non récupérées sur sa machine ! Sans compter que pour lancer les commandes très vite, il vous faudra maintenir un fichier de configuration appelé tfvars
. Donc à cette étape c'est vivable mais il faut parler entres collègues :).
Ceci rappelle la vie sans les plateformes de CI/CD et renforce leur légitimité.
En effet, je vous propose d’utiliser une plateforme de CI/CD pour résoudre tout ceci. Vous pouvez regarder du côté de Terraform Entreprise
qui propose une solution clef en main. Nous avons choisi la plateforme de CI/CD AzureDevOps
et je vous expliquerai pourquoi et comment dans un futur article !
A retrouver dans la documentation officielle terraform
, les notions évoquées :
- configuration du
backend
: https://www.terraform.io/docs/backends/types/index.html - configuration du
remote_state
: https://www.terraform.io/docs/providers/terraform/d/remote_state.html - fournisseur de ressources
provider
: https://www.terraform.io/docs/providers/ - fichier
tfvars
: https://www.terraform.io/docs/configuration-0-11/variables.html#variable-files