Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
La documentation officielle de Terraform décrit tous les aspects de la configuration en détail. Il faudrait la lire attentivement pour comprendre le reste de cette section
Cette section décrit les concepts clés qui seront utilisés dans le livre.
Une ressource est un objet commeaws_vpc
, aws_db_instance
, etc. Une ressource appartient à un fournisseur, accepte des arguments, génère des attributs et possède des cycles de vie. Une ressource peut être créée, récupérée, mise à jour et supprimée.
Un module de ressources est un ensemble de ressources connectées qui exécutent mutuellement l'action commune (par exemple, le module AWS VPC Terraform crée un VPC, des sous-réseaux, une passerelle NAT, etc.). Il dépend de la configuration du fournisseur, qui peut être définie dans celui-ci, ou dans des structures de niveau supérieur (par exemple, dans le module d'infrastructure).
Un module d'infrastructure est un ensemble de modules de ressources, qui peuvent logiquement ne pas être connectés, mais dans la situation/projet/configuration actuels, ils ont le même objectif. Il définit la configuration des fournisseurs, qui est transmise aux modules de ressources en aval et aux ressources. Il est normalement limité au travail dans une entité par un séparateur logique (par exemple, AWS Region, Google Project).
Par exemple, le module terraform-aws-atlantis utilise des modules de ressources comme terraform-aws-vpc et terraform-aws-security-group pour gérer l'infrastructure requise afin d'opérationneliser Atlantis sur AWS Fargate.
Un autre exemple est le module terraform-aws-cloudquery qui emploie plusieurs modules de terraform-aws-modules ensemble afin de gérer l'infrastructure et utilisent les ressources Docker pour créer, pousser et déployer des images Docker. Tout en un ensemble.
La composition est une collection de modules d'infrastructure, qui peuvent s'étendre sur plusieurs zones logiquement séparées (par exemple, des régions AWS, plusieurs comptes AWS). La composition est utilisée pour décrire l'infrastructure complète requise pour l'ensemble de l'organisation ou du projet.
Une composition est constituée de modules d'infrastructure, qui comprennent des modules de ressources implémentant des ressources individuelles.
La source de données effectue une opération en lecture seule et dépend de la configuration du fournisseur. Elle est utilisée dans un module de ressources et un module d'infrastructure.
La source de données terraform_remote_state
agit comme une colle (lien) pour les modules et les compositions de niveau supérieur.
La source de données externe permet à un programme externe d'agir en tant que source de données, exposant des données arbitraires à utiliser ailleurs dans la configuration Terraform. En voici un exemple à partir du module terraform-aws-lambda où le nom de fichier est obtenu en appelant un script Python externe.
La source de données http envoie une requête HTTP GET à l'URL donnée et exporte des informations liées à la réponse. Ces dernières sont souvent utiles pour obtenir des informations à partir de points de terminaison où un fournisseur Terraform natif n'existe pas.
Les modules et les compositions d'infrastructure doivent conserver leur état Terraform dans un emplacement distant où il peut être récupéré par d'autres de manière contrôlable (par exemple, l'accès spécifique à l'ACL, la gestion des versions, la journalisation).
Les fournisseurs, les commission (provisioner) et quelques autres termes sont très bien décrits dans la documentation officielle et il est inutile de le répéter ici. À mon avis, ils ont peu à voir avec l'écriture de bons modules Terraform.
Alors que les ressources individuelles sont comme des atomes dans l'infrastructure, les modules de ressources sont des molécules. Un module est la plus petite unité versionnable et partageable. Il a une liste exacte d'arguments, implémente une logique de base pour qu'une telle unité remplisse la fonction requise. Par exemple, le module terraform-aws-security-group crée des ressources aws_security_group
etaws_security_group_rule
en fonction de l'entrée. Ce module de ressources en lui-même peut être utilisé avec d'autres modules pour créer le module d'infrastructure.
L'accès aux données à travers les molécules (modules de ressources et modules d'infrastructure) est effectué à l'aide des sorties et des sources de données des modules.
L'accès entre les compositions est souvent effectué à l'aide de sources de données à distance. Il existe plusieurs façons de partager des données entre les configurations.
Lorsque vous mettez les concepts décrits ci-dessus dans des pseudo-relations, cela peut ressembler à ceci :
Ce document a pour but de décrire systématiquement les bonnes pratiques dans l’utilisation de Terraform et de fournir des recommandations par rapport aux problèmatiques fréquemment rencontrées.
Terraform, un projet relativement nouveau (comme la plus part des outils Devops actuellement), a été lancé en 2014.
Terraform est un outil puissant (si ce n'est le plus puissant actuellement disponible) et le plus utilisé pour le gestion de l'infrastructure comme code. Il permet aux developpeurs de créer plusieurs codes dont le support et l'intégration seront faciles
Certaines informations décrit dans ce livre pourraient ne pas ressembler aux bonnes pratiques. J'en suis conscient, et pour aider les lecteurs à séparer ce qui établit comme bonnes pratiques et ce que je considère être d'autres méthodes équivalentes, j'utiliserai par moment des indications pour fournir un certain contexte et des icônes pour spécifier le niveau de maturité de chaque sous-section reliée aux bonnes pratiques
Ce livre a été commencé dans une ville de Madrid ensoleillée en 2018 et est disponible gratuitement ici https://www.terraform-best-practices.com/
Quelques années plus tard il a été mis à jour grâce à plusieurs récentes bonnes pratiques disponibles avec Terraform 1.0. Éventuellement ce livre devrait contenir la plupart des bonnes pratiques et recommandations indiscutables pour les utilisateurs de Terraform.
Please contact me if you want to become a sponsor.
Contactez-moi si vous voulez aider à traduire ce livre dans d'autres langues.
Je souhaite toujours obtenir des commentaires et mettre à jour ce livre au fur et à mesure que la communauté mûrit et que de nouvelles idées sont mises en œuvre et vérifiées au fil du temps. Si vous êtes intéressé par certains sujets, veuillez ouvrir un problème ou en indiquer un que vous souhaitez être traiter plus en détail. Si vous sentez que vous avez du contenu et que vous souhaitez y contribuer, rédigez un brouillon et soumettez un pull request (ne vous souciez pas d'écrire un bon texte à ce stade !)
Ce livre est maintenu par Anton Babenko avec l'aide de différents contributeurs et traducteurs. Nicanor Foping l'a traduit en français.
Ce travail est sous licence Apache 2. Voir LICENCE pour plus de détails.
Les auteurs et contributeurs de ce contenu ne peuvent garantir la validité des informations trouvées ici. Veuillez vous assurer que vous comprenez que les informations fournies ici sont fournies librement et qu'aucun type d'accord ou de contrat n'est créé entre vous et toute personne associée à ce contenu ou projet. Les auteurs et les contributeurs n'assument pas et déclinent par la présente toute responsabilité envers toute partie pour toute perte, dommage ou perturbation causé par des erreurs ou des omissions dans les informations contenues dans, associées ou liées à ce contenu, que ces erreurs ou omissions résultent de négligence, accident ou toute autre cause.
Copyright © 2018-2023 Anton Babenko.
Source: https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform
Cet exemple contient du code comme exemple de structuration des configurations Terraform pour une infrastructure de grande taille qui utilise :
2 comptes AWS
2 régions
2 environnements séparés (prod
et stage
qui ne partagent rien). Chaque environnement réside dans un compte AWS distinct et répartit les ressources entre 2 régions
Chaque environnement utilise une version différente du module d'infrastructure standard (alb
) provenant de Terraform Registry
Chaque environnement utilise la même version d'un module interne modules/network
puisqu'il provient d'un répertoire local.
Dans un grand projet comme décrit ici, les avantages de l'utilisation de Terragrunt deviennent très visibles. Voir Code Structures examples with Terragrunt.
Parfait pour les projets où l'infrastructure est logiquement séparée (comptes AWS séparés)
Bon lorsqu'il n'est pas nécessaire de modifier les ressources partagées entre les comptes AWS (un environnement = un compte AWS = un fichier d'état)
Bon quand il n'y a pas besoin d'orchestration des changements entre les environnements
Bon lorsque les ressources d'infrastructure sont différentes par environnement à dessein et ne peuvent pas être généralisées (par exemple, certaines ressources sont absentes dans un environnement ou dans certaines régions)
Au fur et à mesure que le projet grandit, il sera plus difficile de maintenir ces environnements à jour les uns avec les autres. Il faudrait envisagez d'utiliser des modules d'infrastructure (prêts à l'emploi ou internes) pour les tâches répétables.
Compliance.tf — Terraform Compliance Simplified. Make your Terraform modules compliance-ready.
—
FTP (Frequent Terraform Problems)
Terragrunt - Outil d'orchestration
tflint - Code linter
tfenv - Gestionnaire de versions
Atlantis - Automation des demandes d'extraction (Pull Request)
pre-commit-terraform - Collection de git hooks pour Terraform à utiliser avec pre-commit framework
Infracost - Estimation des coûts du cloud pour Terraform dans les demandes de pull. Fonctionne aussi avec Terragrunt, Atlantis et pre-commit-terraform
Les versions des modules de ressources et d'infrastructure doivent être spécifiées. Les fournisseurs doivent être configurés en dehors des modules, mais uniquement en composition. La version des fournisseurs et de Terraform peut également être verrouillée.
Il n'y a pas d'outil maître de gestion des dépendances, mais il existe quelques astuces pour rendre l'enfer des dépendances moins problématique. Par exemple, Dependabot peut être utilisé pour automatiser les mises à jour des dépendances. Dependabot crée des demandes d'extraction pour garder vos dépendances sécurisées et à jour. Dependabot prend en charge les configurations Terraform.
Les questions liées à la structure du code Terraform sont de loin les plus fréquentes dans la communauté. Tout le monde a également pensé à la meilleure structure de code pour le projet à un moment donné.
C'est l'une des questions pour lesquelles de nombreuses solutions existent, mais il est très difficile de donner des conseils universels, alors commençons par comprendre à quoi nous avons affaire.
Quelle est la complexité de votre projet?
Nombre de ressources associées
Nombre de fournisseurs Terraform (voir la remarque ci-dessous sur les "fournisseurs logiques")
À quelle fréquence votre infrastructure change-t-elle ?
À partir d'une fois par mois/semaine/jour
À continuellement (à chaque fois qu'il y a un nouveau commit)
Quelles sont les initiateurs de changement de code? Laissez-vous le serveur CI mettre à jour le référentiel lorsqu'un nouvel artefact est créé ?
Seuls les développeurs peuvent pousser vers le référentiel d'infrastructure?
Tout le monde peut proposer un changement à n'importe quoi en ouvrant un PR (y compris les tâches automatisées exécutées sur le serveur CI)
Quelle plate-forme de déploiement ou service de déploiement utilisez-vous ?
AWS CodeDeploy, Kubernetes ou OpenShift nécessitent une approche légèrement différente
Comment les environnements sont-ils regroupés ?
Par environnement, région, projet
Mettre tout le code dans main.tf est une bonne idée lorsque vous débutez ou que vous écrivez un exemple de code. Dans tous les autres cas, il sera préférable d'avoir plusieurs fichiers répartis logiquement comme ceci :
main.tf
- appelle les modules, les variables locals et les sources de données pour créer toutes les ressources
variables.tf
- contient les variables qui seront utilisées dans main.tf
outputs.tf
- contient les sorties des ressources créées dans main.tf
versions.tf
- contient les exigences de version pour Terraform et les fournisseurs
terraform.tfvars
ne doit être utilisé nulle part sauf composition.
Veuillez vous assurer que vous comprenez les concepts clés - resource module, infrastructure module, et composition, tels qu'ils sont utilisés dans les exemples suivants.
Il est plus facile et plus rapide de travailler avec un plus petit nombre de ressources
terraform plan
etterraform apply
effectuent tous deux des appels d'API cloud pour vérifier l'état des ressources
Si vous avez toute votre infrastructure dans une seule composition, cela peut prendre un certain temps
Le surface d'exposition est plus petit avec moins de ressources
Isoler les ressources non liées les unes des autres en les plaçant dans des compositions séparées réduit le risque en cas de problème
Démarrez votre projet en utilisant l'état distant car :
Votre ordinateur portable n'est pas une source fiable pour votre infrastructure
Gérer un fichier tfstate
file dans un git est cauchemar
Plus tard, lorsque les couches d'infrastructure commenceront à se développer dans plusieurs directions (nombre de dépendances ou de ressources), il sera plus facile de garder les choses sous contrôle
Adoptez une structure et une convention de dénomination cohérentes :
Comme tout code procédural, le code Terraform doit être écrit pour permettre d'abord aux gens de le lire. Sa cohérence aidera lorsque des changements se produiront dans une période de six mois
Il est possible de déplacer des ressources dans le fichier d'état Terraform, mais cela peut être plus difficile à faire si vous avez une structure et un nom incohérents
Gardez les modules de ressources aussi clairs que possible
Ne codez pas en dur les valeurs qui peuvent être transmises en tant que variables ou découvertes à l'aide de sources de données
Utilisez les sources de données et terraform_remote_state
spécifiquement comme colle (liaison) entre les modules d'infrastructure au sein de la composition.
Dans ce livre, des exemples de projets sont regroupés par complexité - des petites aux très grandes infrastructures. Cette séparation n'est pas stricte, vérifiez donc également les autres structures.
Avoir une petite infrastructure signifie qu'il y a un petit nombre de dépendances et peu de ressources. Au fur et à mesure que le projet se développe, la nécessité d'enchaîner l'exécution des configurations Terraform, de connecter différents modules d'infrastructure et de transmettre des valeurs au sein d'une composition devient évidente.
On dénombre au moins 5 groupes distincts de solutions d'orchestration utilisées par les développeurs :
Terraform uniquement. Très simple, les développeurs ne doivent connaître que Terraform pour faire le travail.
Terragrunt. Un pur outil d'orchestration qui peut être utilisé pour orchestrer l'ensemble de l'infrastructure ainsi que pour gérer les dépendances. Terragrunt fonctionne nativement avec des modules d'infrastructure et des compositions, ce qui réduit la duplication de code.
Scripts maison (personnel). Ils sont souvent utilisés comme point de départ vers l'orchestration et avant de découvrir Terragrunt.
Ansible ou les outils d'automatisation généraux similaires. Généralement utilisé lorsque Terraform est adopté après Ansible, ou lorsque l'UI Ansible est activement utilisée.
Crossplane et autres solutions inspirées de Kubernetes. Parfois, il est logique d'utiliser l'écosystème Kubernetes et d'employer une fonction de boucle de réconciliation pour atteindre l'état souhaité de vos configurations Terraform. Voir la vidéo Crossplane vs Terraform pour plus d'information.
Avec cela en tête, ce livre passe en revue les deux premières structures de projet ci-dessus, Terraform uniquement ou Terragrunt.
Voir des exemples de structure de code pour Terraform et Terragrunt dans le prochain chapitre.
Il existe également un atelier pour les personnes qui souhaitent mettre en pratique certaines des choses décrites dans ce guide.
Le contenu est ici - https://github.com/antonbabenko/terraform-best-practices-workshop
Ces exemples montrent un fournisseur AWS, mais la majorité des principes présentés dans les exemples peuvent être appliqués à d'autres fournisseurs de cloud public ainsi qu'à d'autres types de fournisseurs (DNS, DB, Monitoring, etc.)
Type | Description | Préparation |
---|---|---|
Type | Description | Préparation |
---|---|---|
Peu de ressources, pas de dépendances externes. Compte AWS unique. Région unique. Environnement unique
Oui
Plusieurs comptes et environnements AWS, modules d'infrastructure prêts à l'emploi utilisant Terraform.
Oui
Plusieurs régions, besoin urgent de réduire le copier-coller, modules d'infrastructure personnalisés, utilisation intensive des compositions. Utilisation de Terraform.
TeC(Travail en Cours)
Très grand
Plusieurs fournisseurs (AWS, GCP, Azure). Déploiements multi-cloud. Utilisation de Terraform.
Non
moyen
Plusieurs comptes et environnements AWS, modules d'infrastructure prêts à l'emploi utilisant Terragrunt.
No
grand
Plusieurs régions, besoin urgent de réduire le copier-coller, modules d'infrastructure personnalisés, utilisation intensive des compositions. Utilisation de Terragrunt.
No
très grand
Plusieurs fournisseurs (AWS, GCP, Azure). Déploiements multi-cloud. Utilisation de Terragrunt.
Non
locals
pour spécifier des dépendances explicites entre les ressourcesMoyen utile d'indiquer à Terraform que certaines ressources doivent être supprimées au préalable lorsqu'il n'y a pas de dépendance directe dans les configurations Terraform.
https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf
L'argument obligatoire index_document
doit être défini, si var.website
n'est pas une map vide.
L'argument optionnel error_document
peut être omis.
Il ne devrait y avoir aucune raison de ne pas suivre au moins ces conventions :)
Prenez notes que les ressources réelles dans le cloud ont souvent des restrictions dans les noms autorisés. Certaines ressources, par exemple, ne peuvent pas contenir de tirets, certaines doivent être en camel-case. Les conventions de ce livre font référence aux noms Terraform eux-mêmes.
Utilisez _
(surligné) au lieu de -
(tiret) partout (noms des ressources, noms des sources de données, noms des variables, sorties, etc.).
Préférez les lettres minuscules et les chiffres (même si UTF-8 est pris en charge).
Ne répétez pas le type de ressource dans le nom de la ressource (ni partiellement, ni complètement) :
Le nom de la ressource doit être ainsi donné s'il n'y a plus de nom descriptif et général disponible, ou si le module de ressources crée une seule ressource de ce type (par exemple, dans AWS VPC module il y a une seule ressource de type aws_nat_gateway
et plusieurs ressources de typeaws_route_table
, donc aws_nat_gateway
pourrait être nommé this
etaws_route_table
devrait avoir des noms plus descriptifs - comme private
, public
, database
).
Toujours utilisez des noms au singulier.
Utiliser - à l'intérieur des valeurs des arguments et aux endroits où la valeur sera exposée à un humain (par exemple, à l'intérieur du nom DNS de l'instance RDS).
Inclure l'argument count
/ for_each
à l'intérieur du bloc de ressource ou de source de données comme premier argument en haut et séparé par une nouvelle ligne après celui-ci.
Inclure l'argument tags,
si pris en charge par ressource, comme dernier argument réel, suivi de depends_on
etlifecycle
, si necessaire. Tous ces éléments doivent être séparés par une seule ligne vide.
Lorsque vous utilisez des conditions dans un argumentcount
/ for_each,
il est préférable d'employer les valeurs booléennes au lieu delength
ou d'autres expressions.
ressource
count
/ for_each
tags
count
Ne réinventez pas la roue dans les modules de ressources : utilisez le nom, la description et la valeur par défaut des variables telles que définies dans la section "Référence des arguments" pour la ressource avec laquelle vous travaillez.
La prise en charge de la validation dans les variables est plutôt limitée (par exemple, impossible d'accéder à d'autres variables ou de faire des recherches). Planifiez en conséquence car dans de nombreux cas, cette fonctionnalité est inutile.
Utilisez la forme plurielle dans un nom de variable lorsque type est list(...)
ou map(...)
.
Ordonner les clés dans un bloc variable comme ceci: description
, type
, default
, validation
.
Toujours inclure description
sur toutes les variables même si vous pensez que c'est évident (vous en aurez besoin à l'avenir).
Préférez l'utilisation de types simples (number
, string
, list(...)
, map(...)
, any
) plutôt qu'un type spécifique comme object()
sauf si vous avez besoin d'avoir des contraintes strictes sur chaque clé.
Utilisez des types spécifiques comme map(map(string))
si tous les éléments de la carte ont le même type (par exemple, string
) ou peuvent être convertis en celui-ci (par exemple, le type de nombre peut être converti en string
).
Utilisez type any pour désactiver la validation de type à partir d'une certaine profondeur ou lorsque plusieurs types doivent être pris en charge.
La Valeur {}
est parfois un map mais quelques fois object. Utiliser tomap(...)
pour créer une carte car il n'y a aucun moyen de créer un objet.
Faire les sorties cohérentes et compréhensibles en dehors de son champ d'application (lorsqu'un utilisateur utilise un module, le type et l'attribut de la valeur renvoyée doivent être évidents).
Le nom de la sortie doit décrire la propriété qu'il contient et être moins libre que vous ne le souhaiteriez normalement.
Une bonne structure pour le nom de la sortie ressemble à {name}_{type}_{attribute}
, où:
{name} est le nom de la ressource ou de la source de données
sans le préfixe du fournisseur. {name}
pour aws_subnet
est sous-réseau, pouraws_vpc
ce sera vpc
.
{type}
est le type de ressource
{attribute}
est l'attribut retourné par la sortie
Si la sortie renvoie une valeur avec des fonctions d'interpolation et plusieurs ressources, {name}
et {type}
il devrait être aussi générique que possible (this
comme préfixe être omis). Voir exemple.
Si la valeur renvoyée est une liste, elle doit avoir un nom au pluriel. Voir exemple.
Incluez toujours une description pour toutes les sorties, même si vous pensez que c'est évident.
Évitez de définir un argument sensible
à moins que vous ne contrôliez entièrement l'utilisation de cette sortie à tous les endroits de tous les modules
Préférez try()
(disponible depuis Terraform 0.13) à element(concat(...))
(approche héritée pour la version antérieure à 0.13)
Renvoie au plus un ID de groupe de sécurité :
Lorsque vous avez plusieurs ressources du même type, cela doit être omis dans le nom de la sortie: