2025-01-13 14:49:22 +00:00
\newpage
# Mes réalisations
2025-02-19 16:53:38 +00:00
Ce projet m'a amené à participer sur de nombreux sujets. En voici deux qui, je
l'espère, illustreront bien ce que j'ai rencontré.
2025-01-13 14:49:22 +00:00
2025-02-19 16:53:38 +00:00
Je parlerais de **livraison continue** (CI/cd) et d'**états Terraform**.
2025-01-21 09:26:35 +00:00
2025-01-21 12:43:13 +00:00
## Réalisation 1 : Livraison continue
### Contexte
2025-01-21 09:26:35 +00:00
2025-02-19 16:53:38 +00:00
Durant le court laps de temps accordé au projet, nous arrivions à la fin, nous
étions en manque de ressources humaines :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
* le travail de l'équipe n'était **pas toujours qualitatif** ,
* parfois cela **ne répondait pas au besoin énoncé** ,
* d'autres fois **une ressource prenait plus de temps que prévu** sur sa tâche.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Il en résulta **peu de moyens pour répondre à tous les besoins** du projet.
Cela raccourcissait le temps des tâches restantes.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
En pareille situation il a fallu **faire avec l'existant** afin de créér une
chaîne de publication logicielle. J'ai pris en charge cette tâche.
2025-01-21 09:26:35 +00:00
2025-01-21 12:43:13 +00:00
### Analyse
2025-01-21 09:26:35 +00:00
2025-01-29 11:07:55 +00:00
À ce moment du projet nous n'avions que peu d'éléments :
* un dépôt applicatif (nommé **upstream** ),
2025-02-19 16:53:38 +00:00
* un dépôt contenant les charts Helm qui utilisent les images publiées du
dépôt applicatif (nommé **charts** ),
2025-01-29 11:07:55 +00:00
* et un dépôt contenant l'infrastructure de production (nommé **infra** ).
2025-02-19 16:53:38 +00:00
Chaque dépôt peut utiliser l'intégration continue de Gitlab, appelée Gitlab CI
et utilisant des pipelines.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
\newpage
On souhaite **créer une chaîne d'intégration continue** (CI, Continuous
Integration) pour passer d'un dépôt à l'autre suivant **une chaîne** , comme le
montre le schéma suivant :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
{height=40%}
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Cela fait penser au chapitre précédent où nous parlions du [sujet de la
promotion logicielle](#collab). Suivant **quelle(s) règle(s)** pouvons-nous
**passer d'un dépôt à l'autre**, d'une version à l'autre ou (in)valider le
fonctionnement ?
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Après plusieurs heures de réflexions, de dessins en tous genres et du recul,
j'ai constaté que :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
* quoiqu'il arrive la **pipeline va lancer des tests** sur le code,
* et dans la **situation où l'on pose une étiquette** (appelé **tag** sur un
dépôt Git), c'est qu'on souhaite valider - indirectement - le travail effectué.
\newpage
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Ainsi dans la situation où une étiquette apparaît, la pipeline diffère
légèrement. Il y a donc globalement **2 règles à suivre** , représentées par le
schéma suivant :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
{height=50%}
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Comme le résultat positif des tests engendre la compilation puis la
publication d'une image sur un registre, une idée apparaît donc :
**informer les autres dépôts que l'image est disponible** !
2025-01-21 09:26:35 +00:00
2025-01-21 12:43:13 +00:00
### Solution
2025-01-21 09:26:35 +00:00
2025-02-19 16:53:38 +00:00
Suivant l'idée précédente qui consiste à informer les autres dépôts de la
publication récente d'une version, il a fallu trouver une solution.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Nous sommes sur Gitlab, avons des dépôts Git, et des pipelines qui se lancent
quand un commit est effectué. Dans la situation où une étiquette est posée,
pourquoi ne pas **utiliser Git lui-même à l'aide d'un commit sur le dépôt
suivant** ? Ainsi, sans outils supplémentaires, nous pouvons lier les
dépôts entre eux.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
```{=latex}
\begin{sidewaysfigure}
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
En suivant cette logique, nous obtenons le schéma représentant la pipeline de
chaque dépôt~:
\includegraphics{./media/schema_3_pipelines.png}
\caption{Schéma des 3 pipelines}
\end{sidewaysfigure}
```
\newpage
2025-01-29 11:07:55 +00:00
Ce qui donne le savant mélange suivant :
2025-01-21 09:26:35 +00:00
2025-02-19 16:53:38 +00:00
{height=73%}
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
\newpage
Ainsi nous avons, par exemple, le code suivant permettant d'informer le dépôt **charts** qu'une nouvelle étiquette a été posée sur le dépôt **upstream** une fois le backend et le frontend publiés sur le registre Docker de Gitlab :
2025-01-29 11:07:55 +00:00
```bash {.numberLines}
inform-charts:
stage: inform-next
variables:
BACKEND_CHARTFILE_PATH: charts/backend/Chart.yaml
FRONTEND_CHARTFILE_PATH: charts/frontend/Chart.yaml
script:
- |
2025-02-19 16:53:38 +00:00
if ! [ -z "$COMMIT_TAG" ]; then
2025-01-29 11:07:55 +00:00
# Configure git user
git config --global user.email "git@dossmann.net"
git config --global user.name "CI Pipeline"
# Get CHARTS repository
2025-02-19 16:53:38 +00:00
git clone https://blankoworld:${ACCESS_TOKEN}@${CHARTS_REPOSITORY}
2025-01-29 11:07:55 +00:00
cd charts
# Update files
2025-02-19 16:53:38 +00:00
sed -i "s/^version: \".*\"/version: \"${COMMIT_TAG}\"/" \
"${BACKEND_CHARTFILE_PATH}"
sed -i "s/^appVersion: \".*\"/appVersion:\"${COMMIT_TAG}-back\"/" \
"${BACKEND_CHARTFILE_PATH}"
sed -i "s/^version: \".*\"/version: \"${COMMIT_TAG}\"/"
"${FRONTEND_CHARTFILE_PATH}"
sed -i "s/^appVersion: \".*\"/appVersion:\"${COMMIT_TAG}-front\"/" \
"${FRONTEND_CHARTFILE_PATH}"
2025-01-29 11:07:55 +00:00
# Add them to git staged area
git add "${BACKEND_CHARTFILE_PATH}" "${FRONTEND_CHARTFILE_PATH}"
# Commit and push result (to launch CHARTS CI for new code)
2025-02-19 16:53:38 +00:00
git commit -m "chore(release): Update back/front image version"
git tag $COMMIT_TAG
2025-01-29 11:07:55 +00:00
git push origin main --tags
fi
needs: [push-backend,push-frontend]
```
2025-02-19 16:53:38 +00:00
**Le code a dû être modifié (raccourcissement des lignes) pour les fins de
rédaction du présent document**.
La **ligne 27 à 29** posent un tag sur le dépôt **charts** en mettant
simplement à jour les versions utilisées du backend et du frontend dans les
charts Helm sur le registre Gitlab.
2025-01-29 11:07:55 +00:00
### Résultats
2025-02-19 16:53:38 +00:00
Après plusieurs points de détails corrigés, nous avons pu **automatiser
correctement la chaîne de production logicielle** de la **phase de
développement** jusqu'à **la production** en passant **par une validation
manuelle** après déploiement en environnement de pré-production **via
l'intégration continue de Gitlab** (Gitlab CI).
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Ceci nous a permis de facilement **travailler sur 2 environnements**
distincts :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
* l'environnement de **pré-production** ,
* et l'environnement de **production** .
2025-01-21 09:26:35 +00:00
2025-01-21 12:43:13 +00:00
### Limites
2025-01-21 09:26:35 +00:00
2025-01-29 11:07:55 +00:00
Cette technique, bien qu'efficace, a ses limites :
2025-02-19 16:53:38 +00:00
* pour fonctionner sans développer plus, il a fallu **utiliser le même numéro
de version sur tous les dépôts** : on ne fait que passer le tag courant
au dépôt suivant. **Que faire si les numéros de version dérivent ?** ,
* plus on ajoute de dépôts dans la chaîne, plus il y aura de code à faire et
plus il y aura de **complexité à chaîner les dépôts** .
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Trouver une solution temporaire et efficace est - potentiellement - facile.
Mais avoir **une solution pérenne demande de l'expérience et plus de temps** .
2025-01-21 09:26:35 +00:00
2025-01-21 12:43:13 +00:00
### Amélioration(s) possibles(s)
2025-01-21 09:26:35 +00:00
2025-02-19 16:53:38 +00:00
Cette expérience a été enrichissante. Bien que ce système ait répondu à nos
besoins du moment, je pense qu'il serait envisageable de procéder autrement.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Dans un premier temps, **utiliser des dépôts indépendants** avec leur propre
pipeline qui **teste** puis **publie** sur des **dépôts utilisables** par
d'autres projets. Puis **chaque dépôt pourrait avoir un script de montée de
version** pour définir les contraintes spécifiques au projet, au dernier
numéro de version, etc.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Ainsi un dépôt d'infrastructure, par exemple, pourrait utiliser des versions
spécifiques de chacun de ses dépôts indépendants. Et ce serait à lui d'aller
regarder quelle est la dernière version d'un module ou d'une application. Et
d'agir en conséquence : lancer une batterie de tests puis intégrer la
nouvelle version disponible.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Ceci éviterait d'ajouter une quantité astronomique de code Gitlab dans le
dépôt initial (celui de l'application par exemple). Et cela **laisse la
responsabilité aux personnes suivantes** (qui utilisent le dépôt initial) plutôt
qu'aux personnes qui s'occupent du code dans le dépôt initial.
\newpage
2025-01-21 09:26:35 +00:00
2025-01-21 12:43:13 +00:00
## Réalisation 2 : États Terraform
### Contexte
2025-02-19 16:53:38 +00:00
Pendant la création du dépôt **infra** contenant - entre autre -
l'infrastructure de notre environnement de production, **nous avons subis
quelques pertes des états Terraform** posés sur le registre Gitlab.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Non seulement **les états Terraform étaient continuellement cassés** par les
collaborateurs, mais l'ignorance de ces derniers sur le fonctionnement de
Terraform nous a mis **une belle épine dans le pied** .
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Il a fallu agir. Sur mon temps libre - au grand dam de ma famille - j'ai
décidé d'y regarder de plus près.
2025-01-21 12:43:13 +00:00
### Analyse
2025-02-19 16:53:38 +00:00
Comme nous avons plusieurs environnements, l'état Terraform va décrire chaque
fois un environnement. Nous aurons besoin au minimum des états Terraform
suivants :
2025-01-29 11:07:55 +00:00
* un état **gitlab-ci** pour les pipelines de test,
* un état **staging** pour l'environnement de pré-production,
* un état **prod** pour l'environnement de production,
2025-02-19 16:53:38 +00:00
* et éventuellement **un état Terraform par développeur** .
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
C'est ce dernier point que je cherchais à résoudre : **faciliter le
travail du développeur** sur Terraform **pour éviter toute bévue** .
2025-01-29 11:07:55 +00:00
Nous constatons également :
2025-02-19 16:53:38 +00:00
* le **manque de documentation** par le développeur pour savoir comment
procéder pour travailler sur Terraform,
* que **trop d'erreurs manuelles** sont effectués lors de l'utilisation des
commandes Terraform.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
C'est ce qui **nécessite une simplification** et/ou un changement.
2025-01-21 12:43:13 +00:00
### Solution
2025-02-19 16:53:38 +00:00
Dans le Logiciel Libre, dans la plupart des dépôts, nous retrouvons un fichier
**Makefile** qui décrit les différentes étapes de compilation d'une
application, de son installation ou de la création d'une image.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Pourquoi ne pas partir sur cette solution habituelle et l'adapter à notre
cas ?
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Ainsi l'idée serait de **reprendre les commandes habituelles de Terraform** , à
savoir :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
* **init**,
* **plan**,
* **apply**,
* **destroy**,
* **fmt**,
* et **graph** .
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
J'ai imaginé mettre à disposition les mots-clés suivants avec le fichier
**Makefile** :
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
* **make init** - pour **initialiser l'état Terraform** ,
* **make plan** - pour **vérifier les changements attendus** entre l'état Terraform local et l'environnement distant,
* **make apply** - pour **appliquer les changements** ,
* **make destroy** - pour **supprimer l'ensemble des ressources distantes** ,
* **make format** - pour **formater le contenu des fichiers \*.tf** trouvés,
* **make graph** - pour **générer une image vectorielle des dépendances entre
ressources** du projet,
* et **make clean** - pour **nettoyer les fichiers Terraform** non nécessaires.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
**Ces commandes ne résolvent pas le problème en tant que tel**. C'est ce qui
est derrière qui va résoudre le souci : **des scripts qui vérifient que
tout soit OK** avant d'appliquer les commandes.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Ainsi l'**utilisation d'un fichier .env**, non disponible dans le dépôt de
code, permet de charger les informations utiles au choix d'un état Terraform
et de l'environnement distant à atteindre pour travailler.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
La solution réside dans le fait que des scripts soient lancés sous chacune des
commandes make et qu'ils vérifient l'existence et le contenu du fichier
**.env**.
2025-01-29 11:07:55 +00:00
S'ajoute à cela une **documentation dans le fichier README.md** .
2025-02-19 16:53:38 +00:00
\newpage
Exemple de script avec **make init** pour bien comprendre de quoi il est
question :
2025-01-29 11:07:55 +00:00
```bash {.numberLines}
#!/usr/bin/env bash
#
# init.sh
#
# Initialize for a Developer
ENV_FILE="${PWD}/.env"
BACKEND_HTTP_FILE="${PWD}/backend.hcl"
# Need variables in this file
source "${ENV_FILE}" \
|| echo "Fichier ${ENV_FILE} manquant." \
|| exit 1
echo "[INFO] 'USERNAME': ${TF_HTTP_USERNAME}"
if ! [[ -f "${BACKEND_HTTP_FILE}" ]]; then
echo "[ERR] ${BACKEND_HTTP_FILE} manquant!"
exit 1
fi
terraform init \
-reconfigure \
-backend-config=${BACKEND_HTTP_FILE} $@
```
2025-02-19 16:53:38 +00:00
Et le contenu du fichier **env.example** , partagé aux développeurs comme d'un
template pour fabriquer le fichier ** .env** :
2025-01-29 11:07:55 +00:00
```bash
# TOKEN DOIT avoir permissions read/write
export TF_HTTP_USERNAME="PseudoGitlab"
export TF_STATE_NAME="dev"
# Cf.https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
export TF_HTTP_PASSWORD="monPersonnalAccessTokenGitlab"
# Backend HTTP
export TF_HTTP_ADDRESS="< myurl > /state/${TF_STATE_NAME}"
export TF_HTTP_LOCK_ADDRESS="< myurl > /state/${TF_STATE_NAME}/lock"
export TF_HTTP_UNLOCK_ADDRESS="< myurl > /state/${TF_STATE_NAME}/lock"
# Accès AWS
export AWS_ACCESS_KEY_ID="AWS-access-key-id"
export AWS_SECRET_ACCESS_KEY="secret-AWS-access-key"
```
2025-02-19 16:53:38 +00:00
Pour faciliter la création de nouveaux dépôts utilisant Terraform, j'ai choisi
d'inclure ce travail dans un [dépôt dit **template** sur
Gitlab](https://gitlab.com/devu42/templates/terraform).
2025-01-29 11:07:55 +00:00
### Résultats
2025-02-19 16:53:38 +00:00
**À l'usage le travail sur Terraform s'est amélioré**. En procédant ainsi,
toute erreur se produisant sur Terraform n'affectait pas les autres
collaborateurs. **Chacun avait son état Terraform** .
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
La **documentation a permis** aux développeurs **de comprendre facilement**
quoi changer pour travailler. Et ils se sont sentis moins perdus.
2025-01-21 12:43:13 +00:00
### Limites
2025-01-29 11:07:55 +00:00
Toutefois, procéder ainsi a quelques limites :
2025-02-19 16:53:38 +00:00
* **toute modification** sur les éléments du Makefile, les scripts, la
documentation, etc. **doit être faite sur CHAQUE dépôt** . Ainsi plus on a de
dépôt, plus on consomme de temps à mettre à jour (avec des oublis possibles),
* **si le template évolue**, les dépôts ayant utilisé le template **n'ont plus
les mises à jour**. On pourrait imaginer faire un **git rebase, mais cela
casserait l'historique des dépôts** ou bien demanderait d'effectuer du travail
sur une branche Git à part.
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
**C'est très limitant**.
2025-01-29 11:07:55 +00:00
### Amélioration(s) possibles(s)
2025-02-19 16:53:38 +00:00
**Cette solution** a fait ses preuves. Elle **a été utile au moment où nous en
avions grandement besoin**. Elle permet de prendre de l'expérience à ce sujet.
2025-01-21 12:43:13 +00:00
2025-01-29 11:07:55 +00:00
Nous pourrions imaginer améliorer cela avec :
2025-01-21 12:43:13 +00:00
2025-02-19 16:53:38 +00:00
* **une forme de dépendance du fichier Makefile** et des scripts avec le dépôt
**template** utilisé (par exemple via une commande de mise à jour fournie),
* avoir une possibilité de **choisir entre la commande Terraform et OpenTofu**
par l'utilisation d'une variable d'environnement,
* et **permettre l'ajout d'arguments** aux commandes **make apply** , voire
ajouter une commande **make apply-autoapprove** .
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Il est aussi possible qu'il existe une commande de type **wrapper** (qui
utilise Terraform et ses options) pour en faciliter l'usage, tel que
**kubectx** pour Kubernetes.
2025-01-21 12:43:13 +00:00
2025-01-21 09:26:35 +00:00
## Conclusion
2025-01-29 11:07:55 +00:00
2025-02-19 16:53:38 +00:00
Deux expériences variées et différentes qui s'imbriquent pourtant dans le
processus de production logicielle et de livraison continue. Il y a beaucoup
de choses à dire sur chacun des sujets, tellement ils sont passionnants. Ils
amènent également à prendre des initiatives et tester localement des outils.
Nous parlerons d'ailleurs dans le prochain chapitre d'une situation de travail
ayant amené à faire une recherche. Nous pourrons ainsi voir plus en détail le
processus de travail suivi afin d'aboutir à ce résultat.