Aller au contenu principal
GitLabCI/CDFormation

GitLab CI avancé : templates, includes, DAG et rules

30 min de lecture GitLab CI/CD — Chapitre 4

Maîtrise les techniques avancées de GitLab CI : templates réutilisables, includes, pipelines parent-child, DAG pour la parallélisation, et rules complexes.

Un pipeline linéaire avec trois stages, ça fonctionne pour un projet solo. Mais avec 20 microservices, 5 environnements et des conditions de déclenchement complexes, tu as besoin d’abstractions. GitLab CI propose quatre mécanismes puissants : les templates (extends), la composition (include), le parallélisme (needs/DAG), et le contrôle fin (rules). Maîtrise-les et tes pipelines passent de “ça marche” à “c’est maintenable à l’échelle”.

Templates avec extends

Le problème classique : cinq jobs de test qui partagent 80% de configuration. Copier-coller le YAML, c’est une dette technique immédiate.

Les hidden jobs (préfixés par .) ne s’exécutent pas mais servent de templates. extends permet d’en hériter avec un deep merge :

# Template réutilisable (hidden job)
.test-template:
  stage: test
  image: python:3.12-slim
  before_script:
    - pip install -r requirements.txt
  cache:
    key: pip-${CI_COMMIT_REF_SLUG}
    paths: [.cache/pip]

# Jobs qui héritent
test-unit:
  extends: .test-template
  script:
    - pytest tests/unit/ --junitxml=report.xml

test-integration:
  extends: .test-template
  script:
    - pytest tests/integration/ --junitxml=report.xml
  services:
    - postgres:16

💡 Le deep merge fonctionne pour les maps (objets) : les clés enfant écrasent ou complètent le parent. Mais attention — les listes ne se concatènent pas. Un script dans l’enfant remplace entièrement celui du parent. Mets le commun dans before_script et la partie spécifique dans script.

Tu peux chaîner les templates sur plusieurs niveaux. Un .base hérité par .test-base hérité par test-unit — chaque niveau override ou complète les variables du parent. C’est du deep merge récursif pour les maps, mais remplacement total pour les listes et les scalaires.

Les YAML anchors (& / *) font la même chose en apparence, mais ne font pas de deep merge et ne marchent pas entre fichiers. En plus, elles sont résolues avant que GitLab ne parse le YAML — impossible de les débugger dans le CI Lint. Préfère extends dans 99% des cas.

Include : découper et composer

Un .gitlab-ci.yml de 500 lignes est un cauchemar. include permet de découper en modules — quatre types disponibles :

# .gitlab-ci.yml — orchestrateur
stages: [build, test, security, deploy]

include:
  - local: '.gitlab/ci/build.yml'          # même repo
  - local: '.gitlab/ci/test.yml'
  - project: 'devops/ci-templates'          # autre projet GitLab
    ref: v2.1.0
    file: '/templates/security-scan.yml'
  - template: 'Security/SAST.gitlab-ci.yml' # template officiel

🔥 La vraie puissance : un repo de templates CI partagé entre tous tes projets. Tu crées un repo devops/ci-templates avec des fichiers comme python-test.yml, docker-build.yml, helm-deploy.yml. Tu centralises les bonnes pratiques, tu versionnes avec des tags (ref: v2.1.0, jamais ref: main), et chaque équipe inclut ce dont elle a besoin. Tu peux aussi conditionner les includes avec rules — par exemple n’inclure le deploy staging que sur la branche develop.

DAG et needs : le parallélisme intelligent

Les stages classiques sont linéaires : tout le stage test attend la fin de tout le stage build. Mais test-frontend ne dépend que de build-frontend — pourquoi attendre le backend ?

needs casse cette linéarité en créant un graphe de dépendances (DAG) :

stages: [build, test, deploy]

build-frontend:
  stage: build
  script: [npm run build]
  artifacts:
    paths: [dist/]

build-backend:
  stage: build
  script: [go build -o app]

test-frontend:
  stage: test
  needs: [build-frontend]       # démarre dès que build-frontend finit
  script: [npm test]

test-backend:
  stage: test
  needs: [build-backend]        # n'attend pas build-frontend
  script: [go test ./...]

deploy:
  stage: deploy
  needs: [test-frontend, test-backend]
  script: [./deploy.sh]

Résultat : test-frontend et test-backend démarrent indépendamment, dès que leur build respectif est prêt. Sur un gros pipeline avec 10+ jobs de build et test, le gain de temps peut atteindre 40-60%. En entreprise, c’est souvent le premier levier d’optimisation avant de toucher aux runners ou au cache.

💡 Si un job est filtré par une rule, les dépendants ne bloquent pas grâce à optional: true. Par défaut, needs télécharge les artifacts des jobs listés — ajoute artifacts: false si tu n’en as pas besoin pour accélérer. Limite : maximum 50 jobs dans un needs.

Rules avancées et workflow

rules remplace only/except avec bien plus de puissance. Les conditions dans une même rule sont en AND, les rules entre elles en OR (première qui matche gagne) :

deploy:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      variables: { DEPLOY_ENV: "staging" }
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
      variables: { DEPLOY_ENV: "production" }
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
      variables: { DEPLOY_ENV: "production" }
    - when: never

Tu peux combiner if, changes (fichiers modifiés) et exists (fichier présent) dans une même rule — toutes les conditions doivent être vraies (AND logique). Entre les rules, c’est du OR : la première qui matche gagne. Attention : changes est toujours vrai sur les pipelines schedulés — combine avec un if sur $CI_PIPELINE_SOURCE pour garder le contrôle.

Workflow : éviter les duplicate pipelines

Sans workflow, un push sur une branche avec MR ouverte crée deux pipelines. La config recommandée :

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if: $CI_COMMIT_BRANCH
    - if: $CI_COMMIT_TAG

⚠️ Configure workflow.rules dès le jour 1 — c’est le piège n°1 des équipes qui débutent avec GitLab CI.

Pipelines parent-child et dynamiques

Un pipeline parent peut déclencher des enfants qui tournent en parallèle. Avec strategy: depend, le parent attend la fin de chaque enfant :

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

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

🔥 Pattern monorepo : un script Python ou Bash détecte les services modifiés (via git diff), génère un fichier YAML enfant à la volée, et le transmet via trigger: include: artifact:. Seuls les services impactés passent dans le pipeline — c’est le pattern le plus puissant pour les grosses codebases. Tu peux aussi déclencher des pipelines dans d’autres projets avec trigger: project:, utile pour chaîner app → infra → doc automatiquement.

Pièges et bonnes pratiques

🎯 Les listes ne mergent pas : avec extends, un script enfant remplace totalement le parent. Mets le commun dans before_script.

🎯 changes sur les schedules : toujours évalué à true. Combine avec un if sur $CI_PIPELINE_SOURCE.

🎯 Templates non versionnés : ref: main sur un include distant = tu vis dangereusement. Utilise toujours un tag (ref: v2.1.0).

🎯 Duplicate pipelines : sans workflow, un push + MR ouverte = deux pipelines. Configure workflow.rules systématiquement.

En entreprise, les équipes matures combinent un repo de templates CI centralisé, rules + workflow pour le contrôle, et needs pour la performance. C’est l’investissement qui se rentabilise le plus vite sur un projet GitLab.

En résumé : extends élimine la duplication YAML, include structure et permet le partage cross-projets, needs parallélise intelligemment, rules + workflow donnent un contrôle chirurgical, et les pipelines dynamiques parent-child gèrent les monorepos. Ces cinq outils combinés couvrent 95% des besoins CI/CD en entreprise.

Commence par ajouter workflow.rules et convertir tes only/except en rules — c’est le quick win qui a le plus d’impact immédiat. Le prochain cours attaque la sécurité intégrée dans GitLab CI : SAST, DAST, scanning de dépendances et de containers. 🔒

🖥️ Pratique sur ton propre serveur

Pour suivre GitLab CI/CD en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.

Obtenir 200$

Articles liés