Devops
Difficulte: Intermediate
16 min de lecture

GitLab CI/CD : Pipeline d'intégration continue

Apprenez à configurer des pipelines CI/CD complets avec GitLab : runners, stages, jobs, cache, artifacts, Docker et déploiement automatisé.

Retour aux tutoriels
À propos de GitLab CI/CD
GitLab CI/CD est un outil d'intégration et de déploiement continu intégré nativement à GitLab. Il permet d'automatiser la compilation, les tests, l'analyse de sécurité et le déploiement de vos applications à chaque commit, via un fichier de configuration versionné avec votre code source.

Prérequis

  • GitLab : Instance GitLab.com (SaaS) ou GitLab self-hosted (version 14.0 minimum recommandée)
  • Dépôt Git : Un projet GitLab avec un dépôt Git initialisé
  • Runner : Accès à un GitLab Runner (runners partagés disponibles sur GitLab.com)
  • Connaissances : Bases en Git, YAML et ligne de commande Linux
  • Docker (optionnel) : Pour les jobs bases sur des conteneurs Docker

Architecture GitLab CI/CD

Avant de configurer votre premier pipeline, il est essentiel de comprendre les composants fondamentaux de l'architecture GitLab CI/CD et la manière dont ils interagissent.

Les composants principaux

  • Pipeline : L'ensemble du processus CI/CD déclenché par un événement (commit, merge request, schedule). Un pipeline contient un ou plusieurs stages exécutés séquentiellement.
  • Stage : Une étape logique du pipeline (build, test, deploy). Tous les jobs d'un même stage s'exécutent en parallèle.
  • Job : L'unité de travail élémentaire. Chaque job contient un script et s'exécute sur un runner. Un job appartient à exactement un stage.
  • Runner : L'agent d'exécution qui prend en charge les jobs. Il peut fonctionner sur une machine physique, une VM ou un conteneur Docker.
Flux d'exécution
Un commit déclenche un pipeline. Le pipeline exécute les stages dans l'ordre défini. À l'intérieur de chaque stage, les jobs s'exécutent en parallèle. Si un job échoue, le pipeline s'arrête (sauf configuration contraire).

Types de Runners

GitLab propose trois niveaux de runners selon la portée souhaitée :

  • Runners partagés (Shared) : Disponibles pour tous les projets de l'instance. Idéaux pour GitLab.com.
  • Runners de groupe : Dédiés à un groupe GitLab et à ses sous-projets.
  • Runners de projet : Assignés à un seul projet. Recommandés pour les besoins spécifiques (GPU, accès réseau interne).

Installation d'un GitLab Runner

Si les runners partagés ne suffisent pas, vous pouvez installer votre propre runner pour bénéficier de plus de contrôle et de performances.

Installation sur Linux

# Ajout du depot officiel GitLab Runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash

# Installation du runner
sudo apt install -y gitlab-runner

# Vérification de l'installation
gitlab-runner --version

Enregistrement du Runner

Après l'installation, enregistrez le runner auprès de votre instance GitLab. Récupérez le token d'enregistrement dans Settings > CI/CD > Runners de votre projet :

# Enregistrement interactif
sudo gitlab-runner register

# Ou en une seule commande (non-interactif)
sudo gitlab-runner register 
  --non-interactive 
  --url "https://gitlab.example.com/" 
  --registration-token "VOTRE_TOKEN" 
  --executor "docker" 
  --docker-image "alpine:latest" 
  --description "runner-docker-01" 
  --tag-list "docker,linux,ci" 
  --run-untagged="true"

Tags et sélection de Runner

Les tags permettent de diriger les jobs vers des runners spécifiques. Utilisez-les pour cibler des runners avec des capacités particulières :

# Job qui nécessite un runner avec GPU
train_model:
  stage: build
  tags:
    - gpu
    - linux
  script:
    - python train.py
Sécurité des Runners
N'activez jamais le mode privilégié sauf si absolument nécessaire (Docker-in-Docker). Un runner compromis avec des privilèges élevés peut compromettre l'hôte entier et les autres projets.

Fichier .gitlab-ci.yml

Le fichier .gitlab-ci.yml est le cœur de votre configuration CI/CD. Placé à la racine de votre dépôt, il définit l'ensemble de votre pipeline.

Structure de base

# Definition des stages (ordre d'execution)
stages:
  - build
  - test
  - deploy

# Variables globales
variables:
  APP_NAME: "mon-application"
  NODE_VERSION: "20"

# Configuration par défaut pour tous les jobs
default:
  image: node:20-alpine
  before_script:
    - echo "Debut du job $CI_JOB_NAME"

# Job de compilation
build_app:
  stage: build
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour

# Job de test
test_unit:
  stage: test
  script:
    - npm ci
    - npm run test

# Job de déploiement
deploy_prod:
  stage: deploy
  script:
    - echo "Déploiement en production..."
    - ./deploy.sh
  only:
    - main

Mots-clés essentiels

  • image : L'image Docker utilisée pour exécuter le job
  • stage : Le stage auquel appartient le job
  • script : Les commandes à exécuter (obligatoire)
  • before_script / after_script : Commandes exécutées avant/après le script principal
  • artifacts : Fichiers à conserver entre les stages
  • only / except : Conditions d'exécution (déprécié, préférez rules)
  • tags : Sélection de runner par tags

Pipelines multi-stages

Un pipeline typique en production comporte plusieurs stages qui s'exécutent séquentiellement, chacun regroupant des jobs qui tournent en parallèle.

stages:
  - install
  - build
  - test
  - quality
  - staging
  - production

# --- STAGE: install ---
install_deps:
  stage: install
  image: node:20-alpine
  script:
    - npm ci --cache .npm
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - .npm/
      - node_modules/

# --- STAGE: build ---
build_frontend:
  stage: build
  image: node:20-alpine
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 2 hours

build_backend:
  stage: build
  image: golang:1.22-alpine
  script:
    - go build -o bin/api ./cmd/api
  artifacts:
    paths:
      - bin/
    expire_in: 2 hours

# --- STAGE: test ---
test_unit:
  stage: test
  script:
    - npm run test:unit -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      junit: reports/junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

test_intégration:
  stage: test
  services:
    - postgres:16-alpine
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: secret
  script:
    - npm run test:intégration

# --- STAGE: quality ---
lint_code:
  stage: quality
  script:
    - npm run lint
  allow_failure: true

# --- STAGE: staging ---
deploy_staging:
  stage: staging
  script:
    - ./scripts/deploy.sh staging
  environment:
    name: staging
    url: https://staging.example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

# --- STAGE: production ---
deploy_production:
  stage: production
  script:
    - ./scripts/deploy.sh production
  environment:
    name: production
    url: https://example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  when: manual

Variables et Secrets

GitLab CI/CD offre un système complet de gestion des variables pour paramétrer vos pipelines et protéger vos secrets.

Variables prédéfinies

GitLab injecte automatiquement de nombreuses variables dans chaque job :

debug_info:
  stage: build
  script:
    - echo "Branche   : $CI_COMMIT_BRANCH"
    - echo "Commit    : $CI_COMMIT_SHA"
    - echo "Pipeline  : $CI_PIPELINE_ID"
    - echo "Projet    : $CI_PROJECT_NAME"
    - echo "Auteur    : $CI_COMMIT_AUTHOR"
    - echo "MR        : $CI_MERGE_REQUEST_IID"
    - echo "Runner    : $CI_RUNNER_DESCRIPTION"

Variables CI/CD (Settings)

Définissez vos secrets dans Settings > CI/CD > Variables. Deux options de protection essentielles :

  • Protected : La variable n'est injectée que dans les jobs exécutés sur des branches ou tags protégés (main, release/*).
  • Masked : La valeur est remplacée par [MASKED] dans les logs du pipeline. La valeur doit faire au minimum 8 caractères.
deploy_app:
  stage: deploy
  script:
    # $DEPLOY_TOKEN est défini dans Settings > CI/CD > Variables
    # avec les options Protected + Masked activees
    - echo "$DEPLOY_TOKEN" | docker login registry.example.com -u deploy --password-stdin
    - docker push registry.example.com/$CI_PROJECT_PATH:$CI_COMMIT_SHA
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
Ne jamais stocker de secrets dans .gitlab-ci.yml
Le fichier .gitlab-ci.yml est versionné dans Git. Toute valeur sensible (token, mot de passe, clé SSH) doit être configurée via les variables CI/CD de l'interface GitLab, jamais en dur dans le fichier de configuration.

Cache et Artifacts

Le cache et les artifacts sont deux mécanismes distincts pour optimiser la vitesse de vos pipelines et partager des fichiers entre jobs.

Cache : accélérer les builds

Le cache persiste entre les exécutions du pipeline. Utilisez-le pour les dépendances qui changent rarement :

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"

build_python:
  stage: build
  image: python:3.12-slim
  cache:
    key:
      files:
        - requirements.txt
    paths:
      - .pip-cache/
      - venv/
  script:
    - python -m venv venv
    - source venv/bin/activate
    - pip install -r requirements.txt
    - python setup.py build

Artifacts : partager entre stages

Les artifacts sont des fichiers produits par un job et transmis aux stages suivants :

build_app:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
      - package.json
    exclude:
      - dist/**/*.map
    expire_in: 1 week
    when: on_success

test_e2e:
  stage: test
  # Les artifacts de build_app sont automatiquement disponibles
  script:
    - ls dist/   # Le repertoire dist/ est present
    - npm run test:e2e
  artifacts:
    when: on_failure
    paths:
      - screenshots/
    expire_in: 3 days
Cache vs Artifacts
Cache : optimisation best-effort, peut ne pas être disponible, idéal pour les dépendances (node_modules, pip). Artifacts : garanti, transmis entre stages, idéal pour les fichiers de build et les rapports de test. Utilisez le cache pour la vitesse et les artifacts pour la fiabilité.

Docker dans le CI/CD

Construire des images Docker dans un pipeline CI/CD est un besoin courant. Deux approches principales existent, chacune avec ses avantages et compromis.

Docker-in-Docker (DinD)

DinD lance un daemon Docker complet à l'intérieur du runner. Simple, mais il nécessite le mode privilégié :

build_image_dind:
  stage: build
  image: docker:26
  services:
    - docker:26-dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  before_script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin

Kaniko (recommandé)

Kaniko construit des images Docker sans daemon et sans privilèges élevés, ce qui en fait l'option la plus sûre :

build_image_kaniko:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.23.0-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - |
      echo "{"auths":{"$CI_REGISTRY":{"auth":"$(echo -n $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD | base64)"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor
      --context $CI_PROJECT_DIR
      --dockerfile $CI_PROJECT_DIR/Dockerfile
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      --destination $CI_REGISTRY_IMAGE:latest
      --cache=true
Recommandation
Privilégiez Kaniko pour la construction d'images Docker dans vos pipelines. Il ne nécessite pas le mode privilégié, offre un cache layer efficace et est plus sécurisé que DinD en environnement partagé.

Tests automatisés

L'intégration continue prend tout son sens avec une suite de tests automatisés exécutée à chaque commit. GitLab CI/CD offre des fonctionnalités avancées pour les rapports de test.

Tests unitaires avec couverture

test_unit:
  stage: test
  image: node:20-alpine
  script:
    - npm ci
    - npm run test:unit -- --coverage --reporters=default --reporters=jest-junit
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    paths:
      - coverage/
    when: always

Tests Python avec pytest

test_python:
  stage: test
  image: python:3.12-slim
  before_script:
    - pip install -r requirements.txt
    - pip install pytest pytest-cov pytest-html
  script:
    - pytest tests/
      --cov=app
      --cov-report=xml:coverage.xml
      --cov-report=html:htmlcov/
      --junitxml=report.xml
  coverage: '/TOTAL.*\s+(\d+%)$/'
  artifacts:
    reports:
      junit: report.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
    paths:
      - htmlcov/
    when: always

Rapports dans les Merge Requests

En configurant les rapports JUnit et Cobertura, GitLab affiche automatiquement les résultats de tests et la couverture directement dans les merge requests. Le badge de couverture se configure dans Settings > CI/CD > General pipelines > Test coverage parsing.

Déploiement

GitLab CI/CD intègre un système d'environnements qui offre une traçabilité complète des déploiements et des fonctionnalités avancées comme les review apps et le rollback.

Environnements et Review Apps

# Review app : environnement ephemere par merge request
review_app:
  stage: staging
  script:
    - kubectl apply -f k8s/review/
    - kubectl set image deployment/app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop_review
    auto_stop_in: 1 week
  rules:
    - if: $CI_MERGE_REQUEST_IID

stop_review:
  stage: staging
  script:
    - kubectl delete namespace review-$CI_COMMIT_REF_SLUG
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  rules:
    - if: $CI_MERGE_REQUEST_IID
      when: manual

Déploiement production avec validation manuelle

deploy_production:
  stage: production
  script:
    - echo "Déploiement en production..."
    - ansible-playbook -i inventory/prod deploy.yml
      -e "app_version=$CI_COMMIT_SHA"
      -e "app_image=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
  environment:
    name: production
    url: https://example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
  allow_failure: false
Déploiement en production
Utilisez toujours when: manual pour le déploiement en production afin d'exiger une validation humaine. Combinez avec des branches protégées et des approbations de merge request pour un workflow sécurisé.

Pipelines avancés

GitLab offre des fonctionnalités avancées pour créer des pipelines performants et flexibles adaptés aux projets complexes.

Rules : conditions d'exécution

Le mot-clé rules remplace only/except et offre une logique conditionnelle plus puissante :

deploy_staging:
  stage: staging
  script:
    - ./deploy.sh staging
  rules:
    # Déployer sur develop automatiquement
    - if: $CI_COMMIT_BRANCH == "develop"
      when: always
    # Déployer manuellement sur les MR
    - if: $CI_MERGE_REQUEST_IID
      when: manual
    # Ne pas exécuter dans les autres cas
    - when: never

Needs : graphe de dépendances (DAG)

Le mot-clé needs permet à un job de démarrer dès que ses dépendances sont terminées, sans attendre la fin du stage entier :

stages:
  - build
  - test
  - deploy

build_frontend:
  stage: build
  script: npm run build:frontend

build_backend:
  stage: build
  script: go build ./...

test_frontend:
  stage: test
  needs: ["build_frontend"]  # Demarre des que build_frontend est fini
  script: npm run test:frontend

test_backend:
  stage: test
  needs: ["build_backend"]   # Demarre des que build_backend est fini
  script: go test ./...

deploy:
  stage: deploy
  needs: ["test_frontend", "test_backend"]
  script: ./deploy.sh

Jobs parallèles

test_parallel:
  stage: test
  parallel: 4
  script:
    - echo "Execution du job $CI_NODE_INDEX sur $CI_NODE_TOTAL"
    - npm run test -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL

# Matrice de test multi-versions
test_matrix:
  stage: test
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.10", "3.11", "3.12"]
        DB: ["postgres", "mysql"]
  image: python:${PYTHON_VERSION}-slim
  services:
    - $DB
  script:
    - pip install -r requirements.txt
    - pytest tests/

Child pipelines (include/trigger)

# Pipeline parent
stages:
  - triggers

frontend:
  stage: triggers
  trigger:
    include: frontend/.gitlab-ci.yml
    strategy: depend
  rules:
    - changes:
        - frontend/**/*

backend:
  stage: triggers
  trigger:
    include: backend/.gitlab-ci.yml
    strategy: depend
  rules:
    - changes:
        - backend/**/*

Sécurisation du pipeline

GitLab intègre des outils d'analyse de sécurité directement dans le pipeline CI/CD. Ces scanners détectent les vulnérabilités avant la mise en production.

SAST (Static Application Security Testing)

include:
  - template: Security/SAST.gitlab-ci.yml

# Personnalisation du SAST
sast:
  stage: test
  variables:
    SAST_EXCLUDED_PATHS: "spec,test,tests,docs"
    SEARCH_MAX_DEPTH: 4

Dependency Scanning

include:
  - template: Security/Dependency-Scanning.gitlab-ci.yml

dependency_scanning:
  stage: test
  variables:
    DS_EXCLUDED_ANALYZERS: "gemnasium-python"

DAST (Dynamic Application Security Testing)

include:
  - template: Security/DAST.gitlab-ci.yml

dast:
  stage: staging
  variables:
    DAST_WEBSITE: "https://staging.example.com"
    DAST_FULL_SCAN_ENABLED: "true"
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

Pipeline de sécurité complet

include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
  - template: Security/License-Scanning.gitlab-ci.yml

# Les resultats apparaissent dans
# Security & Compliance > Vulnerability Report
Security Dashboard
Les résultats de tous les scanners de sécurité sont agrégés dans le Security Dashboard de GitLab (menu latéral > Security & Compliance). Les vulnérabilités sont classées par sévérité et peuvent être suivies avec des issues dédiées.

Troubleshooting

Voici les problèmes les plus fréquents rencontrés avec GitLab CI/CD et leurs solutions.

Pipeline ne démarre pas

# Vérifier la syntaxe du fichier YAML
# Dans GitLab : CI/CD > Pipelines > CI Lint
# Ou en local avec :
pip install gitlab-ci-lint
gitlab-ci-lint .gitlab-ci.yml
  • Vérifiez les rules/only/except : Le job est peut-être exclu par une condition
  • Branche protégée : Les runners ne traitent peut-être que les branches protégées
  • Runners disponibles : Vérifiez dans Settings > CI/CD > Runners que des runners actifs existent

Job échoue avec une erreur de permission

# Ajouter les permissions nécessaires
job_with_perms:
  script:
    - chmod +x scripts/deploy.sh
    - ./scripts/deploy.sh
  # Ou utiliser before_script pour installer des outils
  before_script:
    - apt-get update && apt-get install -y curl jq

Cache non disponible

  • Le cache est best-effort : il peut ne pas être disponible après un changement de runner
  • Vérifiez la clé de cache : utilisez key: files pour un cache basé sur le contenu des fichiers
  • Les runners Docker utilisent un volume pour le cache ; assurez-vous qu'il est correctement configuré

Mode debug

# Activer le mode debug pour un job
debug_job:
  variables:
    CI_DEBUG_TRACE: "true"
  script:
    - echo "Les commandes executees sont affichees en detail"
    - env | sort   # Lister toutes les variables d'environnement
Attention au debug en production
N'activez jamais CI_DEBUG_TRACE sur des pipelines qui manipulent des secrets. Le mode debug affiche toutes les variables d'environnement, y compris les valeurs masquées, dans les logs.

Conclusion

GitLab CI/CD est un outil complet et mature qui couvre l'intégralité du cycle de vie DevOps, de l'intégration continue au déploiement en production. En maîtrisant les concepts abordés dans ce guide, vous pouvez désormais :

  • Configurer des pipelines multi-stages robustes avec le fichier .gitlab-ci.yml
  • Installer et gérer des GitLab Runners adaptés à vos besoins
  • Protéger vos secrets avec les variables CI/CD protégées et masquées
  • Optimiser la vitesse de vos builds avec le cache et les artifacts
  • Construire des images Docker de manière sécurisée avec Kaniko
  • Automatiser vos tests et suivre la couverture de code
  • Déployer avec des environnements, review apps et validation manuelle
  • Intégrer des analyses de sécurité (SAST, DAST, dependency scanning)
  • Créer des pipelines avancés avec rules, needs et child pipelines

L'adoption progressive de ces pratiques transformera votre workflow de développement : chaque commit est automatiquement compilé, testé, analysé et prêt à être déployé en toute confiance.

Morgann Riu

Écrit par

Morgann Riu

Expert en cybersécurité et administration Linux. Je partage mes connaissances à travers des tutoriels gratuits et des formations pour aider les administrateurs systèmes et développeurs à sécuriser leurs infrastructures.

Partager ce tutoriel

Cet article vous a plu ?

Commentaires

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.