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.
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
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"
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 : 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
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
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
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: filespour 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
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.
Commentaires