
Terraformer une infra Scaleway avec du Continuous Delivery
Infrastructure as code
L'infrastructure as code est une pratique qui consiste à déclarer sous forme de fichiers de code les éléments de son infrastructure (ressource de calcul, stockage, règles de flux etc.). Les avantages de cette méthode sont multiples :
- Tout changement est traçable (via un système de gestion de version comme git)
- Cela peut permettre de maintenir un état cohérent de l'infrastructure en s'assurant que la configuration ne change pas avec le temps
- Le retour vers une infrastructure fonctionnelle en cas de problème est facilité
- Dans une certaine mesure, le code fait aussi office de documentation de l'infrastructure
- Il est plus facile de partager un code source d'infrastructure qu'un ensemble de documents (pas nécessairement à jour) décrivant comment a été construite une infrastructure, un environnement
Cette pratique est de plus en plus courante dans le monde qui m'entoure. C'est une bonne chose et j'essaie de la répandre autour de moi également.
Dans la suite de cet article, Terraform sera notre outil, notre langage pour faire de l'infrastructure as code.
Terraform
Pour commencer, voici un petit extrait de Wikipedia :
Terraform est un environnement logiciel d'« infrastructure as code » publié en open-source par la société HashiCorp. Cet outil permet d'automatiser la construction des ressources d'une infrastructure de centre de données comme un réseau, des machines virtuelles, un groupe de sécurité ou une base de données.
En complément de cette première définition, ajoutons que Terraform est un outil descriptif. On y décrit ce qu'on le veut comme ressources, comme paramètres. En revanche, à aucun moment on n'écrit comment les choses doivent être créées.
Une des grandes forces de Terraform est qu'il fonctionne avec beaucoup de fournisseurs (dans l'outil, il est question de provider) connus : Amazon AWS , Google GCP, Microsoft Azure, IBM, VMware, OVH ou encore Scaleway. C'est ce dernier qui va nous intéresser dans la suite de cet article.
Pour la petite histoire, Terraform est écrit en Go. Pour décrire notre infrastructure as code, nous utilisons un langage de configuration propre à HashiCorp (éditeur de la solution) : HCL (Hashicorp Configuration Language).
Dernier point important à souligner, Terraform est idempotant. Une première application du code Terraform effectue les modifications nécessaires sur l'infrastructure. Une seconde indique qu'il n'y a rien à faire, l'infrastructure est déjà dans l'état cible.
Arrêtons ici l'introduction et passons plutôt dans le vif du sujet.
Attention, cet article n'est pas une introduction à Terraform. Pour mieux saisir ce qui est exposé par la suite, il est préférable d'avoir des connaissances basiques sur l'utilisation et le fonctionnement de Terraform. Voici un lien vers le tutoriel officiel Hashicorp : https://learn.hashicorp.com/terraform
Terraform en Continuous Delivery
Comme évoqué dans l'introduction de cet article, nous voulons versionner le code Terraform. Pour cela, nous allons utiliser GitLab et gitlab-ci pour la partie automatisation.
Le Continuous Delivery, aussi abrégé CD, est une pratique qui consiste à automatiser un certain nombre d'actions autour d'un répertoire, d'un projet de code. Chaque fois qu'un nouveau code est déployé, des actions se lancent automatiquement : build si besoin, des tests (unitaires, fonctionnels, d'intégration etc. ). Tout ce qu'il faut pour préparer et valider que l'application est prête à être déployée. Cette dernière action reste manuelle. Dans ce mode de fonctionnement, le but recherché est d'automatiser tout sauf la mise en production que l'on garde en déclenchement manuel afin de continuer à la maîtriser.
Il existe aussi le Continuous Deployment, qui vise à tout automatiser, y compris l'étape de déploiement.
Ici, c'est bien le Continuous Delivery qui nous intéresse. Il nous faut rester maître des applications du code Terraform sur notre infrastructure.
Un autre avantage de la mise en place d'un tel système avec GitLab est la simplification du poste utilisateur. Il n'est plus nécessaire d'installer terraform
sur son poste de travail. Tout est déclenché et exécuté depuis le runner gitlab-ci. Cette machine utilise des conteneurs pour exécuter les tâches qui lui sont attribués. En cas de besoin ponctuel pour investiguer, comprendre ou analyser un problème, il est possible de faire la même chose sur son poste : lancer des commandes terraform depuis un conteneur. Ceci offre la possibilité de se concentrer un peu plus sur le code et non sur le lancement des commandes terraform.
Un brin de technique pour la mise en place
Terraform stocke l'état des infrastructures et sa configuration. Le sujet a été abordé précédemment : les commandes sont exécutées depuis des conteneurs. Il faut donc trouver un endroit pour stocker et partager ce fichier avec les conteneurs successifs. Plusieurs solutions sont possibles et ici nous avons fait le choix arbitraire de stocker le fichier sur le service stockage objet de Scaleway. Le service se base sur le protocole S3 d'Amazon. Dans la suite de cet article, nous aborderons la mise en place de la configuration pour ce fichier d'état.
Pour l'intégration GitLab, il n'est pas utile de réinventer la roue, un modèle existe et nous allons nous en inspirer fortement. C'est parti !
Exemple concret
Dans l'exemple suivant, nous travaillons sur la déclaration d'une instance security group. Il s'agit de règles de flux, entrants et sortants pour un ou plusieurs instances.
Sans plus attendre, voici l'arborescence de notre mini projet.
.
+-- .gitlab-ci.yml
+-- backend.tf
+-- provider.tf
+-- security-group.tf
Pour le fichier .gitlab-ci.yml
, un modèle est fourni par GitLab, il ne reste plus qu'à le personnaliser un peu et l'utiliser. Nous y reviendrons. Voici le fichier fourni :
# This file is a template, and might need editing before it works on your project.
# Official image for Hashicorp's Terraform. It uses light image which is Alpine
# based as it is much lighter.
#
# Entrypoint is also needed as image by default set `terraform` binary as an
# entrypoint.
image:
name: registry.gitlab.com/gitlab-org/gitlab-build-images:terraform
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
# Default output file for Terraform plan
variables:
PLAN: plan.tfplan
JSON_PLAN_FILE: tfplan.json
cache:
paths:
- .terraform
before_script:
- alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
- terraform --version
- terraform init
stages:
- validate
- build
- test
- deploy
validate:
stage: validate
script:
- terraform validate
plan:
stage: build
script:
- terraform plan -out=$PLAN
- "terraform show --json $PLAN | convert_report > $JSON_PLAN_FILE"
artifacts:
paths:
- $PLAN
reports:
terraform: $JSON_PLAN_FILE
# Separate apply job for manual launching Terraform as it can be destructive
# action.
apply:
stage: deploy
environment:
name: production
script:
- terraform apply -input=false $PLAN
dependencies:
- plan
when: manual
only:
- master
Le fichier backend.tf
contient les informations nécessaires pour accéder au tfstate. Pour rappel, il s'agit du fichier d'état Terraform qui contient des informations sur l'état actuel des infrastructures déclarées dans le code etc. Dans la démarche de CD que nous menons, il faut le stocker à part. En effet, chaque lancement peut tomber sur un exécuteur différent. Il faut donc que cet état soit conservé dans un endroit accessible à tous. Dans notre cas, nous avons fait le choix du stockage objet Scaleway. Il est considéré comme du stockage S3 par terraform :
terraform {
backend "s3" {
bucket = "mybucket"
key = "terraform.tfstate"
region = "fr-par"
endpoint = "https://s3.fr-par.scw.cloud"
access_key = "__BACKEND_ACCESS_KEY__"
secret_key = "__BACKEND_SECRET_KEY__"
skip_credentials_validation = true
skip_region_validation = true
}
}
Il est important de noter que l'access_key
et la secret_key
ne sont pas données directement dans le code. Il s'agit d'information sensible que nous remplaçons à la volée afin qu'elle n'apparaissent pas dans le code source. C'est dans le fichier .gitlab-ci.yml
que ce travail est effectué grâce à l'outil sed :
(...)
before_script:
- alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
- sed -i "s~__BACKEND_ACCESS_KEY__~${BACKEND_ACCESS_KEY}~" backend.tf
- sed -i "s~__BACKEND_SECRET_KEY__~${BACKEND_SECRET_KEY}~" backend.tf
- terraform --version
- terraform init
(...)
Comme expliqué en introduction, Terraform est un outil très puissant. Il permet de faire beaucoup avec un grand nombre de providers. Il est donc impératif de lui signaler quels sont ceux que nous souhaitons utiliser. C'est le rôle du fichier provider.tf
.
terraform {
required_providers {
scaleway = {
source = "scaleway/scaleway"
version = "~> 1.17"
}
}
}
provider "scaleway" {
organization_id = "00000000-0000-0000-0000-000000000000"
zone = "fr-par-1"
region = "fr-par"
}
C'est donc dans ce fichier qu'est déclaré le provider Scaleway et les informations nécessaires à son bon fonctionnement. Ici le projet est assez simple donc nous avons fait le choix d'écrire en dur l'organization_id
. En revanche, à l'image de ce qui a été fait pour le backend, les informations de l'API (access_key et secret_key) ne sont pas visibles ici. Ces informations sensibles sont stockées dans des variables qui sont injectées à la volée lors de l'exécution du pipeline.
Le dernier fichier (security-group.tf
) est celui qui contient la déclaration de la ressource qui nous intéresse : les règles de flux, ou security group chez Scaleway.
resource "scaleway_instance_security_group" "my_instance_security_group" {
inbound_default_policy = "drop"
outbound_default_policy = "accept"
inbound_rule {
action = "accept"
ip = "1.2.3.4"
}
inbound_rule {
action = "accept"
port = 80
}
inbound_rule {
action = "accept"
port = 443
}
}
Les propriétés par défaut sont déclarées tout en haut. Dans l'exemple, 3 règles sont déclarées dont une avec une adresse ip et tous les ports. Sur les autres, ce sont des ports qui sont précisés mais plus d'adresse, c'est tout internet qui est autorisé.
Dès qu'on pousse du code, le pipeline se déclenche sur GitLab avec 3 étapes:
- Validate : Lance la commande
terraform validate
pour faire une vérifcation de la bonne syntaxe du projet. - Plan :
terraform plan
permet de simuler un lancement et de montrer tout ce qui serait réalisé (création, modification et destruction) par Terraform. - Apply : Dernière étape. Celle-ci est manuelle. Une fois que le plan est validé, il est possible de la déclencher et cela exécute la commande
terraform apply
.
Ajout de code
Afin de poursuivre dans le concret, ajoutons une ressource Object Storage dans la base de code et étudions ce qu'il se passe.
La première étape consiste à créer une issue sur le projet.
Une fois l'issue créée, l'étape logique suivante est de proposer une Merge Request ou MR. Une nouvelle branche git permet de proposer notre code.
Petite parenthèse : dans le monde de l'open source, pour proposer une contribution à un projet, il faudrait créer une copie (ou fork) du projet, travailler dessus et proposer les modifications via une Merge Request. Dans l'exemple de cet article, une simplification a été faite. Il n'y a pas de copie du projet car nous avons les droits dessus.
C'est dans cette branche que nous allons rajouter un nouveau fichier storage.tf
. Le fichier contient les lignes suivantes :
resource "scaleway_object_bucket" "test_bucket" {
name = "scw-area51-bucket"
acl = "private"
tags = {
key = "test"
}
}
Une nouvelle ressource scaleway_object_bucket
est déclarée. Il est nécessaire de lui fournir un nom, les autres propriétés sont optionnelles.
Le code est poussé sur le projet, dans sa branche distincte. Voici l'aperçu de la MR. Plusieurs onglets sont visibles en haut avec Overview, Commits, Pipelines et Changes. Le premier présente une vue d'ensemble de la proposition de code. L'onglet Changes est très intéressant.
Comme le montre la capture précédente, il est possible de visualiser rapidement tous les changements proposés. Dans l'exemple, un nouveau fichier est ajouté, avec les lignes de déclaration de l'Object Storage.
Lorsque le code a été poussé sur le projet, un pipeline s'est automatiquement déclenché.
La première étape est un validate qui vérifie la bonne syntaxe des fichiers terraform du projet. Dans notre exemple, la tâche est un succès, le pipeline peut donc continuer son exécution avec l'étape suivante.
L'étape montrée ci-dessus est un plan. Une liste détaillée des actions à réaliser est affiché. Cette liste est complétée par un résumé des actions : nombre d'objet à créer, modifier ou détruire.
Ce résumé est très pratique. Lors d'un ajout d'Object Storage, comme dans l'exemple, il n'y a pas de raison pour que terraform veuille détruire des ressources. Sur l'aperçu de la MR, un bloc est visible affichant ce résumé et un lien permet d'aller directement voir la tâche plan
Le plan n'a pas remonté d'erreur et indiqué ce qu'il voudrait faire sur notre infrastructure.
Le code proposé peut être mergé dans la branche principale du projet. Ceci déclenche automatiquement un nouveau pipeline :
Le pipeline a lancé les tâches validate et plan. Le plan Terraform est exactement le même que celui détaillé plus haut. Une dernière et nouvelle étape est visible sur le pipeline. Celle-ci ne s'est pas lancée automatiquement. En effet, comme expliqué précédemment, nous sommes ici dans une démarche de Continuous Delivery. Nous souhaitons conserver une maîtrise totale sur le déploiement en production.
Ici, tout a été validé en amont donc nous pouvons appliquer les modifications.
Tout s'est bien passé et notre nouvel Object Storage a été déployé !
Conclusion
Voilà c'est la fin de cet article un peu long. Nous avons vu dans les grandes lignes comment fonctionne un tel projet et également comment y contribuer pour ajouter du contenu.
C'est une bonne base mais il reste néanmoins des points d'amélioration. En particulier, la gestion des secrets dans GitLab a ses limites. Il serait mieux d'utiliser un coffre-fort comme Vault. Mais c'est une autre histoire...
En l'état, le projet reste fonctionnel. Il permet de créer et gérer des infrastructures avec Terraform. Et vous, comment faites vous ?
{{ 'Comments (%count%)' | trans {count:count} }}
{{ 'Comments are closed.' | trans }}