Terraform, mutualisation des ressources hors production
Jusqu’à présent, j’étais très fier de mes infrastructures par environnement construites à l’aide de Terraform. Tout était paramétrable, on passait de l’intégration à la recette en changeant simplement une valeur ou deux, etc.
Jusqu'à récemment où je me suis fait la remarque : “pourquoi construire un moteur de base de données pour l’intégration et un pour la recette alors que chacun est très peu utilisé ?”
Prenons une architecture simple :
Un load balancer avec une IP publique, un firewall pour filtrer le trafic entrant, un storage pour délivrer les fichiers statiques (html, css, js), une exécution de container pour l’api et un moteur de base de données.
Mutualisée, cette architecture devient :
Quatre principaux cas de figure se présentent :
Je ne parle pas des deux premiers cas qui n’ont pas de difficulté particulière.
Une façon de résoudre le troisième cas est de construire l’environnement en deux étapes. Lors d’un premier script Terraform, on construit les ressources partagées (le moteur de base de données). Dans un second temps, on ajoute des éléments à cette ressource (les instances de base de données).
Le dernier cas m’a posé beaucoup plus de problèmes.
La solution à laquelle je suis arrivée est d’utiliser les primitives count(), dynamic, for_each et index() de Terraform. Concrètement, j’ai défini deux variables env_context et env_list. env_context indique le contexte dans lequel vont se situer les environnements (par exemple sandbox, production). env_list est une liste de noms d’environnements (par exemple [“int”, “rec”] pour la liste des environnements d’intégration et de recette).
Recommandé par LinkedIn
Dans n’importe quelle ressource créée via Terraform, ajouter count = length(var.env_list) dans la déclaration va créer autant de ressources que d’éléments dans la liste, pour la création des storages par exemple. Par la suite, on peut référencer les ressources comme des éléments de tableau (google_storage_bucket.cloud_storage[0]).
resource "google_storage_bucket" "cloud_storage"
count = length(var.env_list)
}
Les primitives dynamic et for_each vont permettre d’itérer au sein d’une ressource. Par exemple sur la configuration du load balancer.
resource "google_compute_url_map" "url_map" {
dynamic "host_rule" {
for_each = var.env_list
content {
hosts = ...
path_matcher = "frontend-${host_rule.value}"
}
}
}
Lors de la création, l’ordre du tableau est respecté dans toutes les ressources. L’attribut count.index permet de retrouver le pas de l’itération, il permet de référencer les autres ressources pour l’environnement courant.
Dans les cas les plus complexes, la primitive index() va permettre de retrouver l’index à partir d’un tableau et d’un élément.
Et l’environnement de production dans tout ça ?
Tous les scripts Terraform sont applicables pour la production. Il ne s’agit que d’un cas particulier pour lequel le contexte est production (env_context=production) la liste des environnements se résume à un seul élément (env_list=[“prod”]).
En conclusion, la solution optimise l’utilisation des ressources qui peuvent être partagées (bdd, load balancer, …). Le gain est aussi financier.
La solution préserve aussi le cloisonnement entre les environnements puisqu’ils ont chacun leur instance de BDD, leur login de connexion, leur storage, …
Un grand merci à Sandra PARLANT, Tony JARRIAULT et Corentin DEHAY pour leurs remarques perspicaces.
Solutions Architect @Sogeti part of CapGemini
2 ansSylvain MACHARD : c'est toujours un plaisir de partager les retours d'expériences