đ GitLab CI/CD â Pipelines, Runners & DĂ©ploiement
Guide complet IDEOâLab sur l'automatisation CI/CD (.gitlab-ci.yml, Runners, Stages, Variables).
Concept : CI/CD Intégré
CI/CD intégré à GitLab, .gitlab-ci.yml.
Architecture
GitLab (Controller) -> Runner (Agent) -> Job.
Runner JobConcept : Runners
Shared vs Specific vs Group. Executors (Shell, Docker).
Shared Runner Specific RunnerInstallation (GitLab Runner)
apt/dnf, gitlab-runner register, Docker executor.
.gitlab-ci.yml (Syntaxe)
image, stages, job_name, stage, script.
Directives : script
before_script, script, after_script.
Exemple: Test Django (Base)
image: python, services: [postgres].
Variables
variables: (YAML) vs "CI/CD Variables" (Secrets).
Variables Prédéfinies
$CI_COMMIT_BRANCH, $CI_PROJECT_DIR.
Cache vs Artifacts
cache: (vitesse) vs artifacts: (résultats).
rules (ContrĂŽle)
rules: if:, when, allow_failure.
Environnements
environment: name:, url, on_stop.
Déploiement Manuel
when: manual, allow_failure: false.
Build Docker (DinD)
Docker-in-Docker, image: docker:dind, services.
Exemple: Pipeline Django
Test (Bdd), Build (Docker), Deploy (SSH).
Django PipelineGitLab Pages
Job pages, public/, artifacts.
Cheat-sheet .gitlab-ci.yml
Mots-clés YML (stages, script, rules, cache...).
L'automatisation intégrée à Git
GitLab CI/CD est la fonctionnalité d'Intégration Continue et de Déploiement Continu (CI/CD) **intégrée** à la plateforme GitLab.
Contrairement à Jenkins (un outil externe), GitLab CI est piloté par un seul fichier .gitlab-ci.yml que vous placez à la racine de votre dépÎt Git.
DÚs que vous "pushez" (git push) du code, GitLab détecte ce fichier et lance automatiquement un Pipeline (une série de tùches).
Flux de CI/CD
- Dev "push" vers une branche "feature".
- GitLab reçoit le push et lit
.gitlab-ci.yml. - GitLab trouve un "Runner" (un agent) disponible.
- Le Runner exécute les "Jobs" (tùches) définis (ex: Test).
- Le Runner rapporte le succÚs/échec à GitLab.
- Le Dev "merge" dans "main".
- GitLab lance un nouveau pipeline (Test, Build, Deploy).
GitLab CI vs. Jenkins
| CritĂšre | GitLab CI/CD | Jenkins |
|---|---|---|
| Configuration | YAML (.gitlab-ci.yml). "Pipeline as Code". | Groovy (Jenkinsfile) ou UI (Freestyle). |
| Intégration | Parfaite. Intégrée à Git, Merge Requests, Registre. | Externe. Nécessite des plugins pour tout (Git, UI...). |
| Agents (Workers) | Runners (Shared ou auto-hébergés). | Agents (Statiques ou dynamiques). |
| Courbe d'apprentissage | Facile Ă Moyenne (YAML). | ĂlevĂ©e (Groovy, UI, Plugins, Java). |
| ĂcosystĂšme | LimitĂ© Ă l'Ă©cosystĂšme GitLab. | IllimitĂ© (des milliers de plugins). |
Conclusion : GitLab CI est plus simple, plus moderne et mieux intégré si vous utilisez déjà GitLab. Jenkins est plus puissant et flexible (mais plus lourd) si vous avez des besoins complexes ou multi-VCS.
Les 3 Composants Clés
- GitLab (Serveur / Controller) : C'est l'instance principale (
gitlab.comou votre propre serveur). Elle gÚre l'UI, le dépÎt Git, et le planificateur de CI/CD. C'est le "cerveau". - GitLab Runner (Agent) : C'est l'exécutant (le "worker"). Un programme (écrit en Go) que vous installez sur un serveur (Linux, Windows, K8s). Il contacte le serveur GitLab et demande "Y a-t-il du travail ?".
- Job (Tùche) : Le "travail" (une étape, ex: `test_job`) défini dans votre
.gitlab-ci.yml, que le Runner exécute.
Schéma de flux (git push)
(1. git push)
|
âŒ
+-----------------------+
| GitLab (Serveur) |
| (lit .gitlab-ci.yml) |
| (Crée un "Pipeline") |
+-----------------------+
|
| 2. (Place Job 'test' dans la file d'attente)
|
| 3. (Ping: "Qui peut faire ce job ?")
|
âČ
| 4. (Polling: "J'ai du temps libre")
|
+-----------------------+
| GitLab Runner (Agent) |
| (Ex: Serveur Linux) |
| (Tags: 'docker') |
+-----------------------+
|
| 5. (GitLab envoie le Job au Runner)
|
âŒ
(Runner exécute le Job:
'docker run python:3.11 ...')
|
| 6. (Renvoie le log/statut Ă GitLab)
âŒ
+-----------------------+
| GitLab (Serveur) |
| (Met Ă jour l'UI: â OK) |
+-----------------------+
Types de Runners (OĂč s'exĂ©cute le Job ?)
(Settings -> CI/CD -> Runners)
| Type | Propriétaire | Usage | Temps/Coût |
|---|---|---|---|
| Shared (Partagé) | GitLab (ex: gitlab.com) | Jobs publics, standards, sans besoins spécifiques. | Limité (Quotas de CI, ex: 400 min/mois). |
| Specific (Spécifique) | Vous (Auto-hébergé) | Production. Jobs privés, déploiements, besoins spécifiques (ex: GPU, Docker-in-Docker). | Illimité (votre propre serveur). |
| Group | Vous (Auto-hébergé) | Runner partagé pour un groupe de projets. | Illimité. |
Tags (Ătiquettes)
Les "Tags" sont la clé. Vous "taguez" votre Runner (ex: docker, linux, prod-deploy).
Dans .gitlab-ci.yml, vous spécifiez les tags: dont le job a besoin.
Executors (Comment le Job s'exécute ?)
Quand vous enregistrez un Runner (voir 1.4), vous devez choisir son "Executor" (méthode d'exécution).
| Executor | Description | Usage |
|---|---|---|
docker | Recommandé. Lance le job dans un conteneur Docker (propre, isolé). | Standard (Test, Build). |
shell | Exécute les scripts directement sur la machine hÎte. | Déploiement. (ex: `ssh`, `rsync` sur le serveur de prod). |
docker-machine | (Avancé) Crée des VMs à la volée pour les builds (auto-scaling). | Infrastructure CI/CD large. |
kubernetes | (Avancé) Lance le job comme un Pod Kubernetes. | CI/CD Cloud-Native. |
1. Installation (sur votre VPS/serveur)
Ubuntu / Debian
# 1. Ajouter le dépÎt curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash # 2. Installer sudo apt-get install gitlab-runner
RHEL / Rocky / AlmaLinux
# 1. Ajouter le dépÎt curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash # 2. Installer sudo dnf install gitlab-runner
2. Enregistrement (Lier le Runner Ă GitLab)
Allez dans GitLab -> Settings -> CI/CD -> Runners et récupérez l'URL et le Token d'enregistrement.
# Lancer l'assistant interactif sudo gitlab-runner register
L'assistant va demander :
- GitLab instance URL : (ex:
https://gitlab.com) - Registration token : (ex:
glrt-AbC...) - Description : (ex: "Mon runner docker prod")
- Tags : (ex:
docker,linux,prod-deploy) (Séparez par des virgules !) - Executor : (ex:
docker) - Default Docker image : (ex:
alpine:latest) (si executor=docker)
Le runner est maintenant enregistré dans /etc/gitlab-runner/config.toml et démarré.
.gitlab-ci.yml (Anatomie)Anatomie de base
C'est un fichier YAML. Les noms à gauche sont des **Jobs** (ex: build_job). Les mots-clés (image, stage, script) sont des directives.
# (1) Définir l'image Docker par défaut
default:
image: node:18-alpine
# (2) Définir l'ordre des étapes
stages:
- build
- test
- deploy
# (3) Définir le Job "build"
build_job:
stage: build # (Lien vers 'stages')
script:
- echo "Building..."
- npm install
- npm run build
# (4) Définir le Job "test"
test_job:
stage: test
script:
- echo "Testing..."
- npm testConcepts
default: image:: L'image Docker que le Runner utilisera (sauf si surchargée).stages: [...]: (Crucial) Définit l'ordre d'exécution des étapes. Tous les jobs d'un "stage" (ex: "test") s'exécutent en parallÚle. Le stage suivant (ex: "deploy") ne démarre que si *tous* les jobs du stage précédent ont réussi.[nom_job]:: (ex:build_job) C'est votre tùche.stage: build: Lie le job au stage "build".script: [...]: La (ou les) commande(s) shell à exécuter.
Ordre d'exécution
before_script (pour le setup), script (le travail), after_script (le cleanup).
default:
before_script:
- echo "Global: S'exécute avant CHAQUE job"
my_job:
stage: test
before_script:
- echo "Local: Surcharge le 'default:before_script'"
- apt-get update
script:
- echo "Le travail principal (tests)"
- npm test
after_script:
- echo "Cleanup (s'exĂ©cute mĂȘme si 'script' Ă©choue)"
- rm -rf node_modules/YAML (Listes vs Blocs)
Les scripts sont des listes YAML (-) ou des blocs (|).
Liste (-)
script: - commande 1 - commande 2
Bloc (|) (Plus lisible)
script: |
commande 1
commande 2
if [ "$VAR" == "true" ]; then
echo "Multi-lignes"
fiComment tester un projet Django qui a besoin d'une BDD (ex: PostgreSQL) ? On utilise la directive services.
default:
image: python:3.11-slim
stages:
- test
# Job de test
test_django_app:
stage: test
# 1. 'services' (lance un 2Ăšme conteneur 'postgres'
# et le lie au conteneur principal)
services:
- name: postgres:15-alpine
alias: postgres-db
# 2. Variables (pour configurer Django/Postgres)
variables:
# (Remplace le .env pour les tests)
POSTGRES_DB: test_db
POSTGRES_USER: test_user
POSTGRES_PASSWORD: "test_password"
# (Dit Ă Django oĂč trouver la BDD)
DATABASE_URL: "postgres://test_user:test_password@postgres-db:5432/test_db"
before_script:
- apt-get update && apt-get install -y postgresql-client
- pip install -r requirements.txt
script:
# (Attendre que le service Postgres soit prĂȘt)
- PGPASSWORD=$POSTGRES_PASSWORD psql -h postgres-db -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT 1"
# (Lancer les tests)
- python manage.py migrate
- python manage.py testVariables (Non-sensibles)
Définies dans .gitlab-ci.yml. Visibles dans Git.
# 1. Variable Globale
variables:
PYTHON_VERSION: "3.11"
NODE_VERSION: "18"
stages:
- test
test_job:
stage: test
# 2. Variable de Job (Surcharge)
variables:
NODE_VERSION: "20"
script:
- echo "Python: ${PYTHON_VERSION}" # Affiche 3.11
- echo "Node: ${NODE_VERSION}" # Affiche 20Variables CI/CD (Secrets / Masquées)
â ïž Ne **jamais** commiter de secrets (mots de passe, tokens, clĂ©s SSH) dans .gitlab-ci.yml.
Utilisez l'UI de GitLab : Settings -> CI/CD -> Variables -> Expand
Création (UI GitLab)
- Key :
PROD_SSH_KEY - Value :
-----BEGIN RSA PRIVATE KEY... - Flags :
- âïž Protect variable : (RecommandĂ©) N'est disponible que sur les branches/tags protĂ©gĂ©s (ex:
main). - âïž Mask variable : (RecommandĂ©) Cache la valeur dans les logs du job.
- âïž Protect variable : (RecommandĂ©) N'est disponible que sur les branches/tags protĂ©gĂ©s (ex:
Cette variable sera disponible en tant que {{ $PROD_SSH_KEY }} dans vos scripts.
$CI_...)GitLab injecte des centaines de variables d'environnement dans chaque job. Voici les plus utiles.
| Variable | Description (Exemple) |
|---|---|
{$}CI_COMMIT_BRANCH | Nom de la branche (ex: feature/login ou main). (Si when: manual, main). |
{$}CI_COMMIT_TAG | Le nom du tag (si le pipeline tourne sur un tag). |
{$}CI_PIPELINE_SOURCE | Comment le pipeline a démarré (push, web, schedule, trigger). |
{$}CI_JOB_STAGE | Le stage du job (ex: test, deploy). |
{$}CI_PROJECT_DIR | Le chemin absolu du workspace (ex: /builds/user/projet). |
{$}CI_REGISTRY_IMAGE | L'URL du registre Docker intégré à GitLab. |
{$}CI_REGISTRY_USER | Login pour le registre Docker (gitlab-ci-token). |
{$}CI_JOB_TOKEN | Token d'authentification (temporaire) pour ce job. |
cache (Vitesse) vs artifacts (Résultats)C'est une confusion trÚs fréquente chez les débutants.
| CritĂšre | cache: (Cache) | artifacts: (Artefacts) |
|---|---|---|
| But | Accélérer les builds (ré-utiliser les fichiers). | Stocker les résultats d'un job (ex: un binaire, un .jar). |
| Partage | Entre les jobs d'un mĂȘme pipeline. | Entre les stages (le job 'deploy' rĂ©cupĂšre l'artifact de 'build'). |
| Expiration | Non garanti (Best-effort). | Garanti (expire_in:). |
| Usage | node_modules/, .venv/, .m2/ (Maven). | dist/, target/*.jar, report.xml. |
Exemple
# (Mise en cache de 'node_modules' si 'package-lock.json' n'a pas changé)
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
build_job:
stage: build
script:
- npm install
- npm run build
artifacts:
paths:
- dist/ # (Le résultat du build)
expire_in: 1 day # (Garde l'artifact 1 jour)
deploy_job:
stage: deploy
script:
- echo "Déploiement du dossier 'dist/'..."
- ls dist/
dependencies:
- build_job # (Télécharge automatiquement l'artifact 'dist/' de 'build_job')rules:)ContrÎle d'exécution des Jobs
rules: est la méthode moderne (remplace only/except) pour décider *si* un job doit s'exécuter.
deploy_job:
stage: deploy
script:
- echo "Déploiement..."
rules:
# 1. (Condition IF)
- if: '$CI_COMMIT_BRANCH == "main"'
# (Le job ne s'exécute QUE si on est sur 'main')
# 2. (Condition IF AND)
- if: '$CI_COMMIT_BRANCH == "dev" && $CI_PIPELINE_SOURCE == "schedule"'
# 3. (Condition IF Fichier modifié)
- if: '$CI_COMMIT_BRANCH == "main"'
changes:
- Dockerfile
- src/**/*
# 4. Bloquer
- when: neverwhen (Quand exécuter)
when: | Description |
|---|---|
on_success | (Défaut) S'exécute si les stages précédents réussissent. |
on_failure | S'exécute si un stage précédent échoue (ex: Notif Slack). |
always | S'exécute toujours (pour le cleanup). |
manual | (Crucial) N's'exĂ©cute pas. Affiche un bouton "Play" (â¶) dans l'UI GitLab. |
allow_failure (Autoriser l'échec)
Permet à un job (ex: "linting") d'échouer sans bloquer le pipeline.
lint_job:
stage: test
script:
- npm run lint
allow_failure: trueLa directive environment: (environnement) permet à GitLab de "suivre" vos déploiements (Operations -> Environments).
deploy_staging:
stage: deploy
script:
- echo "Déploiement Staging..."
# (script de déploiement)
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
environment:
name: staging
url: https://staging.ideolab.com
deploy_prod:
stage: deploy
script:
- echo "Déploiement PRODUCTION..."
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
environment:
name: production
url: https://ideolab.comwhen: manual)Il est dangereux de déployer en production automatiquement à chaque 'push' sur 'main'. On utilise when: manual pour forcer une action manuelle.
deploy_prod:
stage: deploy
script:
- echo "Déploiement PRODUCTION (SSH, rsync...)"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
# 1. Le job ne s'exécute pas auto
when: manual
# 2. (Optionnel) Si 'manual', le pipeline
# est considéré comme "bloqué".
# 'false' le met en "succĂšs" (jaune).
allow_failure: false
environment:
name: production
url: https://ideolab.comDans l'UI GitLab, un bouton (â¶) apparaĂźtra. Le dĂ©ploiement ne dĂ©marre que si un humain clique dessus.
Pour *construire* une image Docker (docker build) dans un job, le Runner a besoin d'un accĂšs Docker. On utilise "Docker-in-Docker" (DinD).
# Job pour builder et pusher une image
build_docker_image:
stage: build
# 1. Utiliser l'image 'docker' (client)
image: docker:24.0
# 2. Lancer le 'daemon' docker (dind)
# en tant que service lié.
services:
- docker:24.0-dind
# 3. (Requis) Dire au client de ne pas utiliser TLS
variables:
DOCKER_TLS_CERTDIR: ""
DOCKER_HOST: tcp://docker:2375
before_script:
# 4. Login au registre GitLab
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
script:
# 5. Build & Push
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHAstages:
- test
- deploy_staging
- deploy_prod
# Job de Test (cf 2.3)
test_django:
image: python:3.11
stage: test
services:
- postgres:15
variables:
POSTGRES_DB: test_db
POSTGRES_USER: test_user
POSTGRES_PASSWORD: "test_password"
DATABASE_URL: "postgres://test_user:test_password@postgres:5432/test_db"
script:
- pip install -r requirements.txt
- python manage.py migrate
- python manage.py test
# Job de Déploiement (Staging)
deploy_staging:
image: alpine:latest
stage: deploy_staging
before_script:
# (Installer SSH & Clé privée)
- apk add --no-cache openssh-client rsync
- eval $(ssh-agent -s)
- echo "$STAGING_SSH_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan -H $STAGING_SERVER_IP >> ~/.ssh/known_hosts
script:
- echo "Déploiement Staging..."
# (Ex: rsync -avz . ideo_user@$STAGING_SERVER_IP:/opt/ideo_project/)
# (Ex: ssh ideo_user@$STAGING_SERVER_IP "cd /opt/ideo_project && ./deploy.sh")
environment:
name: staging
url: http://staging.ideolab.com
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
# Job de Déploiement (Production)
deploy_prod:
# (Copie de 'deploy_staging', mais avec $PROD_SSH_KEY)
stage: deploy_prod
script:
- echo "Déploiement PRODUCTION..."
environment:
name: production
url: http://ideolab.com
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # (Protection)
GitLab CI peut héberger des sites statiques (HTML/CSS/JS) gratuitement, si le job s'appelle pages et que l'artifact est un dossier public/.
# (Exemple: Déployer un site React/Vite)
# 1. Nom de job spécial
pages:
stage: deploy
image: node:18-alpine
script:
- npm install
- npm run build # (Crée le dossier 'dist')
# 2. Renommer 'dist' en 'public'
- mv dist public
# 3. L'artifact DOIT ĂȘtre 'public'
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "main"'Le site sera disponible sur https://[username].gitlab.io/[projectname].
.gitlab-ci.ymlStructure Globale
# (Image Docker par défaut)
default:
image: python:3.11
# (Ordre d'exécution)
stages:
- build
- test
- deploy
# (Variables non-secrĂštes)
variables:
MY_VAR: "valeur"
# (Mise en cache)
cache:
key:
files:
- requirements.txt
paths:
- .venv/Structure d'un Job
mon_job:
image: node:18 # (Surcharge 'default')
stage: test
services:
- postgres:15
tags:
- docker # (Runner requis)
script:
- echo "Exécution"
artifacts:
paths:
- dist/
expire_in: 1 week
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
- if: '$CI_COMMIT_BRANCH == "dev"'