Aller au contenu
CI/CD & Automatisation medium

Lab 01 — Mon premier pipeline GitLab CI/CD

15 min de lecture

logo gitlab

Votre équipe a une API Python, des tests, un Dockerfile — mais aucun pipeline CI/CD. Les développeurs lancent pytest à la main (quand ils y pensent), personne ne lint le code, et le docker build ne tourne que sur la machine de celui qui déploie. Résultat : des bugs détectés en production, du code inconsistant, et un Dockerfile cassé que personne ne remarque pendant des semaines. Dans ce premier lab, vous allez écrire un .gitlab-ci.yml qui automatise ces trois vérifications dans un pipeline GitLab.

  • Écrire un .gitlab-ci.yml avec des stages, des jobs et une image Docker
  • Lancer du lint, des tests et un build Docker dans un pipeline automatisé
  • Naviguer dans l’interface GitLab pour voir le pipeline, les jobs et les logs
  • Comprendre ce qui se passe quand GitLab exécute un job (runner, conteneur, script)

En entreprise, la plupart des projets commencent sans CI/CD. Quelqu’un crée un dépôt Git, l’équipe pousse du code, et les vérifications se font « à la main » — quand elles se font. Le jour où un bug passe en production parce que personne n’a lancé les tests, on se dit qu’il faudrait automatiser. Ce lab vous met exactement dans cette situation.

Situations réelles où ce lab vous aide :

  • Vous rejoignez un projet qui n’a aucun pipeline et on vous demande d’en créer un
  • Votre équipe perd du temps à lancer pytest et ruff manuellement avant chaque merge
  • Le docker build casse régulièrement mais personne ne s’en rend compte avant la mise en production
  • Vous voulez comprendre ce qu’il y a derrière le bouton « Pipelines » de GitLab avant d’aller plus loin
  1. Forkez le dépôt (une seule fois)

    Un fork, c’est une copie complète d’un projet GitLab sur votre propre compte. Vous obtenez votre propre version du dépôt, avec vos propres pipelines — sans risque de modifier le projet original.

    Rendez-vous sur gitlab.com/Bob74/pipeline-craft et cliquez sur Fork en haut à droite. Gardez les paramètres par défaut et confirmez. Vous obtenez votre copie personnelle à l’adresse gitlab.com/<votre-pseudo>/pipeline-craft.

  2. Clonez votre fork sur votre machine (une seule fois)

    Fenêtre de terminal
    # Remplacez <votre-pseudo> par votre nom d'utilisateur GitLab
    git clone https://gitlab.com/<votre-pseudo>/pipeline-craft.git
    cd pipeline-craft

    Vous avez maintenant le projet en local. Toutes les branches des labs sont incluses.

  3. Passez sur la branche de départ de ce lab

    Fenêtre de terminal
    git checkout starter/lab-01

    Cette branche contient le code Python complet (l’API, les tests, le Dockerfile) mais aucun fichier .gitlab-ci.yml. C’est vous qui allez le créer.

  4. Vérifiez sur GitLab

    Dans votre fork sur gitlab.com, allez dans Build > Pipelines. La page est vide — aucun pipeline ne s’est jamais exécuté. Normal : sans .gitlab-ci.yml, GitLab ne sait pas quoi faire.

Sans fichier .gitlab-ci.yml, GitLab ne sait pas quoi faire de votre code. Vous pouvez pousser autant de commits que vous voulez — rien ne sera vérifié automatiquement :

  • Le code pourrait contenir des erreurs de style que ruff détecterait
  • Les tests pourraient échouer sans que personne ne le sache
  • Le Dockerfile pourrait être cassé depuis des semaines

L’objectif : écrire un pipeline qui lance automatiquement le lint, les tests et le build Docker à chaque push.

Vous allez créer un fichier .gitlab-ci.yml à la racine du projet avec 3 stages et 3 jobs.

Un pipeline GitLab est défini par un fichier YAML qui décrit :

ConceptRôleExemple
stageÉtape du pipeline — les stages s’exécutent dans l’ordrelint, test, build
jobTâche concrète — tourne dans un conteneur Dockerruff-lint, pytest, docker-build
imageImage Docker utilisée pour exécuter le jobpython:3.12-slim
scriptCommandes exécutées dans le jobpip install, pytest
Pipeline
├── Stage: lint
│ └── Job: ruff-lint (image: python:3.12-slim)
├── Stage: test
│ └── Job: pytest (image: python:3.12-slim)
└── Stage: build
└── Job: docker-build (image: docker:27)
  1. Créez le fichier .gitlab-ci.yml à la racine du projet

    stages:
    - lint
    - test
    - build

    Cette première section déclare les 3 étapes du pipeline, dans l’ordre d’exécution. Tous les jobs du stage lint s’exécutent d’abord, puis ceux de test, puis build.

  2. Ajoutez le job de lint

    ruff-lint:
    stage: lint
    image: python:3.12-slim
    before_script:
    - pip install ruff
    script:
    - ruff check app/ tests/

    Ce job :

    • S’exécute dans le stage lint
    • Utilise l’image Docker python:3.12-slim (légère, ~150 Mo)
    • Installe ruff via pip avant d’exécuter le script
    • Lance ruff check sur le code source et les tests
  3. Ajoutez le job de tests

    pytest:
    stage: test
    image: python:3.12-slim
    before_script:
    - pip install -r requirements-dev.txt
    script:
    - pytest -v

    Ce job installe toutes les dépendances de développement (qui incluent pytest, httpx et ruff), puis lance les 6 tests du projet.

  4. Ajoutez le job de build Docker

    docker-build:
    stage: build
    image: docker:27
    services:
    - docker:27-dind
    variables:
    DOCKER_TLS_CERTDIR: "/certs"
    script:
    - docker build -t pipeline-craft:test .

    Ce job est différent des deux précédents et mérite une explication.

    Chaque job GitLab CI tourne dans un conteneur Docker. Or, ce job doit lui-même construire une image Docker avec docker build. On se retrouve donc à lancer Docker… à l’intérieur d’un conteneur Docker. C’est ce qu’on appelle Docker-in-Docker (DinD).

    Concrètement, voici ce qui se passe :

    • L’image docker:27 fournit le client Docker (la commande docker)
    • Le service docker:27-dind démarre un daemon Docker séparé dans un conteneur annexe — c’est lui qui exécute réellement le build
    • Le client communique avec ce daemon via un réseau interne sécurisé par TLS (d’où la variable DOCKER_TLS_CERTDIR)

    Vous n’avez pas besoin de maîtriser le fonctionnement interne de DinD pour l’utiliser. Retenez simplement que ces trois lignes (image, services, variables) forment un bloc indissociable quand vous voulez construire des images Docker dans un pipeline GitLab.

  5. Committez et poussez

    Fenêtre de terminal
    git add .gitlab-ci.yml
    git commit -m "ci: add initial pipeline with lint, test and build"
    git push origin starter/lab-01

Voici le .gitlab-ci.yml complet que vous devez obtenir :

stages:
- lint
- test
- build
ruff-lint:
stage: lint
image: python:3.12-slim
before_script:
- pip install ruff
script:
- ruff check app/ tests/
pytest:
stage: test
image: python:3.12-slim
before_script:
- pip install -r requirements-dev.txt
script:
- pytest -v
docker-build:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker build -t pipeline-craft:test .

Après le push, allez dans Build > Pipelines sur GitLab. Vous devez voir :

  • Un pipeline avec 3 stages visibles dans le graphe
  • Le job ruff-lint au vert dans le stage lint
  • Le job pytest au vert dans le stage test — les 6 tests passent
  • Le job docker-build au vert dans le stage build
  • Le pipeline global affiche un badge vert ✅

Cliquez sur chaque job pour voir ses logs :

  • ruff-lint : doit afficher All checks passed!
  • pytest : doit afficher 6 passed
  • docker-build : doit afficher Successfully built à la fin

Un seul fichier a été ajouté :

.gitlab-ci.yml (nouveau — 30 lignes)

Ce fichier suffit pour que GitLab exécute automatiquement le lint, les tests et le build à chaque push sur n’importe quelle branche.

Quand vous poussez du code, GitLab :

  1. Détecte le .gitlab-ci.yml à la racine du projet
  2. Parse le YAML et crée les jobs dans l’ordre des stages
  3. Assigne chaque job à un runner disponible (sur gitlab.com, ce sont des runners SaaS partagés)
  4. Le runner tire l’image Docker spécifiée (python:3.12-slim ou docker:27)
  5. Il lance un conteneur à partir de cette image
  6. Il exécute le before_script puis le script dans ce conteneur
  7. Si toutes les commandes retournent un exit code 0, le job est vert
  8. Si une commande échoue (exit code ≠ 0), le job est rouge et le pipeline s’arrête
Push → GitLab lit .gitlab-ci.yml → crée 3 jobs
Runner prend le job "ruff-lint"
→ pull python:3.12-slim
→ crée un conteneur
→ pip install ruff
→ ruff check app/ tests/
→ exit 0 → ✅
Runner prend le job "pytest"
→ pull python:3.12-slim
→ crée un conteneur
→ pip install -r requirements-dev.txt
→ pytest -v
→ exit 0 → ✅
Runner prend le job "docker-build"
→ pull docker:27 + docker:27-dind
→ crée un conteneur avec le service DinD
→ docker build -t pipeline-craft:test .
→ exit 0 → ✅
Pipeline vert ✅
SymptômeCauseSolution
yaml syntax error immédiatIndentation incorrecte, tabulationsVérifier avec l’éditeur CI/CD de GitLab (Build > Pipeline editor)
ruff: command not foundbefore_script manquant ou mal indentéVérifier que before_script: est au bon niveau d’indentation
pytest: command not foundpip install absent ou requirements-dev.txt mal orthographiéVérifier le nom du fichier et le before_script
Cannot connect to the Docker daemonService docker:27-dind absent ou DOCKER_TLS_CERTDIR manquantAjouter services: et la variable DOCKER_TLS_CERTDIR
Pipeline jamais déclenchéFichier .gitlab-ci.yml mal nommé ou pas à la racineLe fichier doit s’appeler exactement .gitlab-ci.yml à la racine du dépôt
Job pending pendant longtempsRunner partagé occupé (gitlab.com)Patience — les runners SaaS Free peuvent avoir une file d’attente
  • Validez votre YAML avant de pousser : utilisez le Pipeline Editor intégré à GitLab (Build > Pipeline editor). Il vérifie la syntaxe et montre une visualisation du pipeline. Voir Valider un .gitlab-ci.yml.
  • Explorez les variables prédéfinies : GitLab injecte automatiquement des variables comme $CI_COMMIT_SHA, $CI_PROJECT_NAME, $CI_PIPELINE_ID. Ajoutez env | grep CI_ dans un script: pour les voir.
  • Comparez avec solution/lab-01 : si votre pipeline ne fonctionne pas, regardez la branche solution/lab-01 pour comparer avec la solution attendue.
  • Un fichier .gitlab-ci.yml à la racine suffit pour activer la CI/CD sur un projet GitLab
  • Un pipeline est découpé en stages (ordre séquentiel) contenant des jobs (tâches concrètes)
  • Chaque job s’exécute dans un conteneur Docker isolé — rien n’est partagé entre les jobs
  • before_script prépare l’environnement, script exécute la tâche
  • Le build Docker nécessite Docker-in-Docker (docker:27-dind en service) sur les runners SaaS
  • Ce pipeline est fonctionnel mais pas optimisé : chaque job réinstalle ses dépendances, les images sont lourdes, et rien n’est mis en cache — ce sera l’objet des prochains labs

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn