Cela fait plusieurs mois (années ?) que je me dis qu’utiliser DinD (Docker in Docker) pour builder des images ce n’est vraiment pas idéal. Mais cela fait aussi un moment que je n’ai pas eu l’occasion de (re)construire une CI. Couplé à une flemmingite aiguë en ce qui concerne la reprise des projets persos…
Mais il est temps ! Ajourd’hui jetons un coup d’oeil sur buildah et construisons une pipeline Gitlab-CI simple pour s’affranchir de la dépendance à Docker dans nos jobs.
Objectif
C’est la première fois que je me penche sur les alternatives à DinD pour le build. Le but n’est pas de devenir expert mais de remplacer ce fonctionnement par un autre que j’estime plus sain.
L’objectif est donc de créer une pipeline pour construire une image docker puis de publier celle-ci.
Nous utiliserons les éléments suivants :
- Gitlab.com : et ses outils de CI/CD intégrés
- Runner partagés : des serveurs mis à disposition par Gitlab.com pour éxécuter nos jobs
Attention au quota
- .gitlab-ci.yml : le fichier définissant le comportement de la CI Gitlab
- Dockerfile : le fichier contenant les instructions de création pour une image
- Buildah : alternative à Docker pour construire nos images à partir du Dockerfile dans le contexte CI
- quay.io/buildah/stable:v1.21.0 : la dernière version en date de l’image Buildah
- GitLab Container Registry : pour publier nos images
Les étapes seront les suivantes :
- Définition d’un Dockerfile de test à builder automatiquement
- Définition des jobs CI dans le fichier .gitlab-ci.yml pour réaliser le build et la publication sur le registry Gitlab
- Un récap du code complet
Y’a plus qu’à…
Avant de commencer
Nous aurons besoin de quelques prérequis pour atteindre cet objectif :
- Un compte et un repository vierge sur Gitlab.com
N’hésitez pas à jeter un oeil sur les guides si nécessaire Gitlab basics, Gitlab CI quick start
- Un Dockerfile d’exemple
- Nous nous appuierons sur l’image officielle de Buildah pour le contexte d’éxécution de nos tâches automatisées
Le Dockerfile de test
Pour notre exemple choisissons un serveur Nginx servant un fichier statique.
Commençons par cloner notre projet, préalablement créé sur gitlab.com. Dans mon cas :
1 | git clone git@gitlab.com:memorandom/buildah-gitlab-ci.git |
Créons ensuite un pseudo site web qui sera exposé par Nginx :
1 | # créons le dossier qui contiendra un fichier html basique |
Puis le fichier Dockerfile qui permettra de construire l’image de notre serveur :
1 | FROM nginx |
Commitons les changements. Notre exemple est prêt.
Test en local
Avant d’implémenter la CI testons le processus en local. Démarrons un conteneur avec Buildah :
1 | docker run --security-opt seccomp:unconfined -it -v $PWD:/buildah quay.io/buildah/stable:v1.21.0 |
Il peut être nécessaire d’adapter la configuration Docker pour que Buildah fonctionne correctement (buildah:issue:1901). Heureusement les runners mis à disposition par Gitlab.com ont l’air compatible 🎉.
Une fois dans le conteneur, nous avons accès au contenu de notre projet grâce au volume Docker. Exécutons les commandes suivantes :
1 | cd /buildah # Le dossier dans lequel est monté notre projet |
Nous utilisons buildah bud
(pour Build Using Dockerfile) pour construire l’image. Les paramètres sont simplement le chemin vers le fichier Dockerfile et le nom de la nouvelle image.
La commande buildah push
nous permet d’exporter l’image dans un fichier d’archive. Et celle de pull
permet le re-import d’une image depuis le même type d’archive.
Ces deux dernières commandes nous serviront à isoler notre job de build et notre job de push. L’export sera stocké dans un artifact au moment du build et sera re-importé avant le push vers le registry. Les deux jobs pourront ainsi s’éxécuter indépendamment et l’archive pourra être téléchargée pour consultation si nécessaire.
La Pipeline
Entrons dans le vif du sujet avec le détail des définitions de la CI. Créons un fichier .gitlab-ci.yml à la racine de notre projet dans lequel nous allons définir nos deux jobs.
Options communes
Le fichier débute par des options communes :
1 | variables: # les variables, notamment de configuration pour Buildah |
L’export du TAG joue sur les variables pré-définies exposées par Gitlab.com. Si la pipeline est trigger par la création d’un tag, la variable CI_COMMIT_TAG est automatiquement ajoutée par Gitlab. Nous utilisons donc cette valeur pour le TAG si elle existe. Sinon la valeur par défaut latest prime.
Le dossier servant à stocker l’artifact (variable ARTIFACT_DIR) doit être relatif au dossier de build, sans quoi Gitlab.com ne pourra pas créer l’artifact. (issue:15530)
Le build
Définissons ensuite la clef build
qui configure le job du même nom :
1 | build: |
Plutôt explicite. Notons l’utilisation de la variable pré-défénie CI_REGISTRY_IMAGE. Celle-ci contient l’adresse complète de l’image pour le projet courant. Par exemple dans le cas de mon dépôt git nommé memorandom/buildah-gitlab-ci, la valeur associée est registry.gitlab.com/memorandom/buildah-gitlab-ci
.
Rappelons que la variable TAG provient de la définition default.before_script
explicitée précédemment.
L’artifact
Toujours dans le job de build rajoutons le bloc indiquant à Gitlab.com qu’il doit stocker l’export de l’image :
1 | build: |
C’est ce qui nous permet de séparer nos deux jobs. L’artifact est automatiquement géré par Gitlab.com. Il sera copié au même endroit dans les jobs suivants avant que la logique de ces derniers ne soit éxécutée.
Pour être plus explicite nous mentionnerons la dépendances lors de la phase de push, mais ce n’est pas obligatoire.
Le push
Finalement ajoutons la tâche permettant de publier l’image :
1 | push: |
La différence principale (au-delà des commandes de scripts) réside dans l’ajout de la clef only
. Nous spécifions par ce biais que le job n’est lancé que pour la branche main ou pour la création d’un tag. L’intérêt de découpler les deux jobs est de pouvoir s’assurer que le build est fonctionnel en permanence, mais de ne publier que dans certains cas précis.
Nous nous appuyons également de nouveau sur les variables pré-défénies de Gitlab qui exposent toutes les informations nécessaires pour l’authentification auprès du registry (CI_REGISTRY_USER, CI_REGISTRY_PASSWORD & CI_REGISTRY).
Commitons les changements et poussons sur Gitlab. En consultant l’UI nous pouvons suivre l’éxécution de la pipeline. Une fois celle-ci terminée nous pouvons également constater l’apparition d’une nouvelle image dans le registry ! 💪
Vérification en local
Par acquis de conscience, récupérons et lançons l’image depuis notre poste pour s’assurer que tout est fonctionnel. Dans mon cas :
1 | docker login registry.gitlab.com |
A l’aide de curl, ou en visitant http://127.0.0.1:8080, nous retrouvons bien le pseudo site web créé initialement.
Pour l’accès au registry Gitlab depuis l’extérieur vous pouvez créer un token personnel.
Le code complet
Nous en avons terminé. N’hésitez pas à me faire vos retours 😄. En attendant vous trouverez ci-après le code complet de la pipeline.
A la prochaine 👋.
.gitlab-ci.yml
1 | variables: |