Introduction

Il y a quelques mois je parlais de ma formation Bootcamp pour devenir Ingénieur DevOps. J’y ai utilisé - à nouveau - Gitlab CI (Continuous Integration). Vous savez, cet outil permettant d’exécuter des actions après avoir envoyé votre code/dépôt sur la plateforme Gitlab ?

C’est très utile, par exemple pour lancer les tests sur votre code, vérifier que le code s’exécute dans un environnement précis ou tout simplement vérifier que le code compile. Et si votre dépôt n’est pas du code, par exemple un site web, vous pouvez tester les liens morts ; voire publier votre site ! Les possibilités sont - finalement - nombreuses !

En revanche, un tel outil est parfois déstabilisant. Et des cheveux, j’en ai arrachés ! J’aurais apprécié tomber sur un article explicatif. Avec des astuces. Peut-être que le présent article m’aurait convenu, qui sait !

J’aurai par exemple aimé savoir comment publier facilement un site web, tester localement le fichier .gitlab-ci.yml avant de l’envoyer sur Gitlab, connaître plus d’astuces pour les mots clés Gitlab, savoir utiliser Docker dans Gitlab CI et ce que sont les composants réutilisables.

Ce sont ainsi tous les sujets que nous allons aborder aujourd’hui.

Un visage de Raton Laveur

Photo trouvée sur le profil de didier.camus sur Flickr sous Licence Œuvre du domaine public.

Publier rapidement un site statique

Gitlab met à disposition des utilisateurs un espace permettant d’héberger des sites webs statiques. Incroyable n’est-ce pas ? Cet espace est Gitlab Pages.

Comment cela fonctionne ? Il suffit de créer un dépôt Gitlab, d’ajouter un fichier .gitlab-ci.yml contenant la section suivante :

image: busybox

pages:
  stage: deploy
  script:
    - echo "The site will be deployed to $CI_PAGES_URL"
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Code récupéré du projet https://gitlab.com/pages/plain-html.

Cela signifie qu’au terme de la CI/CD (dans l’étape nommée deploy), le contenu du dossier public sera publié sur Gitlab Pages. À quelle adresse ? Imaginons que vous partez d’un dépôt dont l’adresse est : https://gitlab.com/blankoworld/monprojet/, l’adresse URL d’accès à votre site publié sera : https://blankoworld.gitlab.io/monprojet/. Pratique non ?

Astuce : Si votre dépôt est privé, vous pouvez quand même rendre votre site public. Il suffit d’aller dans Settings > General, Section « Visibility, project features, permissions » et dans le sous-titre « Pages », activez la fonctionnalité. Et dans le menu déroulant à côté, choisissez « Everyone ». N’oubliez pas de sauvegarder cette configuration. Le code sera privé, le site web statique public !

Pour plus d’exemple, veuillez vous rendre sur la page Gitlab présentant la liste de projets utilisant Gitlab Pages.

Tester avant d’envoyer sur le dépôt ?

Le truc vraiment embêtant avec les fichiers .gitlab-ci.yml, c’est qu’on ne peut pas les tester sur notre machine (localement). Pour avancer il faut :

  • modifier le fichier .gitlab-ci.yml,
  • le valider (faire un commit),
  • l’envoyer sur Gitlab,
  • patienter que la pipeline se lance (et parfois se termine),
  • bénéficier du résultat ou, dans la plupart des cas, recommencer à l’étape 1…

À une époque on pouvait utiliser une commande gitlab-runner exec en local. Mais celle-ci a été supprimée : https://gitlab.com/gitlab-org/gitlab-runner/-/commit/f8508c924f80b104ec7353353e6ad0edb5daae66 .

Suivant ce que nous faisons dans la CI/CD de Gitlab, il est possible d’utiliser un outil bien pratique nommé gitlab-ci-local : gitlab-ci-local.

Vous trouverez quelques détails de gitlab-ci-local sur mon recueil d’astuces.

Généralités

Voici quelques pistes intéressantes sur le sujet Gitlab :

  • Une documentation de départ est : https://docs.gitlab.com/topics/build_your_application/. Elle permet d’en savoir plus sur la syntaxe de gitlab-ci.yaml, les variables pré-définies ou encore le fonctionnement d’un pipeline dans Gitlab CI,
  • Toujours utiles : les actions que je nomme comme « globales ». Par exemple les mots clés image, variables ou before_script peuvent être utilisés en début du fichier pour définir des actions ou des valeurs qui seront utilisées dans toutes les étapes du pipeline. Ce qui peut être un avantage, ou un inconvénient. Exemple : image: ubuntu:latest va définir que toutes les étapes se lanceront dans un docker partant de l’image Ubuntu,
  • Entrées/sorties : un artefact = une sortie. C’est à dire des fichiers utilisables/disponibles pour les étapes suivantes (donc en entrée pour les autres étapes) en utilisant le mot clé dependencies (Cf. https://docs.gitlab.com/ci/jobs/job_artifacts/#fetching-artifacts). Sauf si dependencies = [] est utilisé.
  • Utiliser des variables globales en début du fichier (par exemple pour les numéros de versions) fait gagner du temps sur le long terme. Exemple :
variables:
  DOCKER_VERSION: "27.3.1"
  UBUNTU_VERSION: "24.04"
  • Concernant le mot clé script :
    • si on veut écrire plusieurs lignes, utiliser la syntaxe suivante :
    script:
      - echo "première étape des scripts"
      - |
        echo "première ligne"
        echo "seconde ligne"
        echo "etc."    
    
    • limiter à 10 lignes maximum,
    • si on utilise la syntaxe pour écrire plusieurs lignes, le debug est plus difficile
  • Pour organiser les étapes, on peut donner à chacune d’elle une étape pré-requise en utilisant le mot clé « needs », cela permet de chaîner les étapes dans un certain ordre. Exemple : si on veut que l’étape « deploy » se fasse après « compile », alors à la fin de l’étape « deploy » on met needs: [compile].

Le Cas Docker

Création d’une image Docker… dans un conteneur Docker

Dans la situation où vous voudriez fabriquer des images Docker, vous allez être confrontés à plusieurs problématiques :

  • il faut utiliser des commandes docker pour créer des images,
  • mais la plupart des Gitlab Runner exécutent les étapes de Gitlab CI dans un Docker,
  • donc on se retrouve à vouloir exécuter des commandes docker dans un environnement Docker.

Vous voyez le problème ?

Pour contourner cette situation il suffit d’utiliser Docker-in-Docker.

En somme le début du fichier .gitlab-ci.yml ressemblera à :

variables:
  DOCKER_VERSION: "27.4.1"

image: docker:$DOCKER_VERSION

services:
  - docker:$DOCKER_VERSION-dind

S’identifier

Pour utiliser les dépôts de conteneurs de Gitlab, rien de plus simple il suffit d’écrire le code suivant dans le fichier .gitlab-ci.yml :

before_script:
  - docker info
  - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin

Nul besoin de renseigner les variables. Elles sont renseignées par Gitlab CI.

Construire l’image

Pour construire l’image cela va ressembler à :

variables:
  CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG

build-image:
  stage: build
  script:
    - docker pull "$CONTAINER_TEST_IMAGE" || true
    - docker build --build-arg BUILDKIT_INLINE_CACHE=1 -t $CONTAINER_TEST_IMAGE .
    - docker push $CONTAINER_TEST_IMAGE

On part du principe que le fichier Dockerfile est situé à la racine de votre projet Gitlab.

CONTAINER_TEST_IMAGE est une variable globale construite à partir des variables pré-définies de Gitlab CI.

Pousser l’image sur le dépôt

Dans cette situation nous sommes contents d’avoir déjà ajouté l’authentification aux dépôts de conteneurs Gitlab.

Cette fois on fait une étape pour récupérer l’image précédente et lui donner un nouveau nom :

push-image:
  stage: push
  script:
    - export IMAGE_TAG="$(date +'%Y%m%d')-${CI_COMMIT_SHORT_SHA}"
    - docker pull $CONTAINER_TEST_IMAGE
    - docker tag $CONTAINER_TEST_IMAGE $CI_REGISTRY_IMAGE:$IMAGE_TAG
    - docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

L’idée est :

  • de créer un nom de tag pour l’image (variable IMAGE_TAG),
  • récupérer l’image précédente - puisque les étapes d’un fichier .gitlab-ci.yml s’exécutent dans des conteneurs isolés les uns des autres,
  • apposer un nouveau tag sur l’image récupérée,
  • publier le résultat en poussant l’image ainsi étiquetée.

Ceci ne s’exécute que si la branche est nommée main. Donc chaque commit sur cette branche générera une image Docker et un tag.

Ordre d’exécution des étapes

Afin que les deux étapes précédentes s’exécutent dans un certain ordre, ne pas oublier les lignes suivantes dans le fichier .gitlab-ci.yml :

stages:
  - build
  - push

Comme si ça ne suffisait pas…

En outre, voici d’autres éléments à rajouter dans son fichier .gitlab-ci.yml :

variables:
  # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"

Ces lignes permettent de renseigner un autre accès à l’API Docker pour lancer les commandes docker.

Les composants réutilisables

Gitlab CI offre la possibilité d’utiliser des composants réutilisables pour agrémenter les étapes de son fichier .gitlab-ci.yml. Ceci évite de réinventer la roue (en codant soi-même).

Plusieurs composants sont disponibles sur la page Gitlab components.

Exemple d’utilisation d’un composant :

include:
  - component: gitlab.com/components/container-scanning/container-scanning@5.1.0

C’est très utile dans certaines situations comme pour tester des fichiers Terraform (avec le composant OpenTofu), le composant Go, le composant Rust ou même utiliser les fonctionnalités Autodevops de Gitlab.

Conclusion

Au premier abord Gitlab CI paraît complexe et maigre en fonctionnalités. Cependant la lecture de la documentation (très fournie !) donne rapidement un aperçu bien plus avantageux de cet outil. On y découvre les composants réutilisables, les mots clés image, variables, before_script, les artefacts (sorties), les variables globales, le déploiement de sites statiques et j’en passe !

Certaines difficultés telles que Docker viennent s’immiscer dans nos essais. Mais une fois les obstacles franchis c’est un outil très satisfaisant. Je l’apprécie.

Je serais ravi de tomber plus souvent sur des articles décrivant ses fonctionnalités. J’espère participer à une diffusion plus grande de la connaissance Gitlab CI et, qui sait, peut-être partagerais-je prochainement d’autres astuces ?