Aller au contenu

CI/CD GitLab

English version
Retour au sommaire


Vue d'ensemble du pipeline

push / merge request
        │
        ├── stage: test
        │     ├── test:unit               (tests Jest)
        │     ├── test:quality:sonarqube  (analyse qualité SonarQube)
        │     └── test:build:docker       (build partiel sur MR)
        │
        ├── stage: containerize
        │     └── package:image           (build + push image Docker)
        │
        └── stage: deploy
              ├── deploy:int:ansible      (staging, automatique)
              ├── deploy:dev:ansible      (dev, automatique)
              └── deploy:prod:manual      (production, ⚠️ MANUEL)

Déclenchement par branche

Branche Tests Build image Deploy
main ✅ (tag main) ⚠️ manuel → prod
tag v1.x.x ✅ (tag v1.x.x) ⚠️ manuel → prod
develop ✅ (tag develop) Auto → staging
test-ci ✅ (tag test-ci) Auto → staging
qa ✅ (tag qa)
dev ✅ (tag dev) Auto → dev
Merge Request ✅ (build partiel)
Autre branche

Stages

Stage : test

test:unit

test:unit:
  image: node:20-alpine
  stage: test
  cache:
    key:
      files: [package-lock.json]
    paths: [.npm/]
  before_script:
    - npm ci --cache .npm --prefer-offline
  script:
    - npm test
  rules:
    - if: main/develop/qa/test-ci ou tag
    - if: merge_request_event

Exécute npm test (Jest). Le cache npm accélère les runs suivants.

test:quality:sonarqube

test:quality:sonarqube:
  image: sonarsource/sonar-scanner-cli:11
  stage: test
  script:
    - sonar-scanner -Dsonar.host.url="${SONAR_HOST_URL}"
  allow_failure: true  # Ne bloque pas le pipeline en cas d'échec
  rules:
    - if: main/develop/qa/test-ci ou tag

Analyse statique du code. allow_failure: true : un échec SonarQube ne bloque pas le déploiement.

test:build:docker

test:build:docker:
  stage: test
  script:
    - docker build --target builder .
  rules:
    - if: merge_request_event  # Uniquement sur les MR

Build partiel (stage builder) pour valider que le Dockerfile est correct. Ne produit pas d'image finale.


Stage : containerize

package:image

package:image:
  stage: containerize
  needs: []  # Parallèle avec les tests
  environment:
    name: $DEPLOY_ENV
  script:
    - docker build --pull --tag ${TARGET_IMAGE} .
    - docker push ${TARGET_IMAGE}
  rules:
    - if: develop/qa/test-ci → DEPLOY_ENV=staging
    - if: dev → DEPLOY_ENV=development
    - if: main ou tag → DEPLOY_ENV=production

Construction du tag image :

IMAGE_TAG = CI_COMMIT_TAG (si tag git) OU CI_COMMIT_REF_SLUG (nom de branche)
TARGET_IMAGE = ${DOCKER_REG_URL}/${CI_PROJECT_PATH}:${IMAGE_TAG}

Exemples :

registry.id2real.net/racines/racines-app:develop
registry.id2real.net/racines/racines-app:main
registry.id2real.net/racines/racines-app:v1.2.3

Pas de --build-arg AUDIO_CDN_URL : l'URL CDN est injectée au runtime via variable d'environnement Docker. Une seule image sert tous les environnements.


Stage : deploy

deploy:int:ansible (staging)

deploy:int:ansible:
  stage: deploy
  environment:
    name: staging
  needs:
    - package:image
  script:
    - chmod 400 $SSHKEY_CI
    - ansible-playbook -i ansible/inventory.yaml -l integration \
        --private-key $SSHKEY_CI ansible/deployment_playbook.yaml
  rules:
    - if: develop ou test-ci → automatique

deploy:dev:ansible (dev)

deploy:dev:ansible:
  stage: deploy
  environment:
    name: development
  needs:
    - package:image
  script:
    - chmod 400 $SSHKEY_DEV
    - ansible-playbook -i ansible/inventory.yaml -l dev \
        --private-key $SSHKEY_DEV ansible/deployment_playbook.yaml
  rules:
    - if: dev → automatique

deploy:prod:manual (production)

deploy:prod:manual:
  stage: deploy
  environment:
    name: production
  when: manual  # ⚠️ Ne se déclenche pas automatiquement
  needs:
    - package:image
  script:
    - chmod 400 $DEPLOY_SSHKEY_FILE
    - ansible-playbook -i ansible/inventory.yaml -l prod \
        --private-key $DEPLOY_SSHKEY_FILE ansible/deployment_playbook.yaml
  rules:
    - if: main ou tag → disponible manuellement

Déclenchement :

GitLab → CI/CD → Pipelines → [pipeline sur main] → deploy:prod:manual → ▶

Playbook Ansible

Le playbook ansible/deployment_playbook.yaml exécute sur le serveur cible :

# 1. Créer le répertoire de déploiement
- file: path=/docker-apps/racines state=directory

# 2. Copier le docker-compose de l'environnement
- copy: src=ansible/docker-compose.{env}.yaml dest=/docker-apps/racines/docker-compose.yml

# 3. Arrêter l'application courante
- shell: docker compose -f /docker-apps/racines/docker-compose.yml down

# 4. Puller la nouvelle image et relancer
- shell: |
    docker compose -f /docker-apps/racines/docker-compose.yml pull
    docker compose -f /docker-apps/racines/docker-compose.yml up -d --force-recreate

L'inventaire (ansible/inventory.yaml) définit les hôtes : - integration → serveur staging (port SSH 9257, user ci_gitlab_deploy) - dev → serveur de développement - prod → serveur de production


Variables CI/CD

À définir dans GitLab → Settings → CI/CD → Variables :

Variable Type Description Environnements
DOCKER_REG_URL Variable URL du registry Docker Tous
DOCKER_REG_LOGIN Variable Login registry Tous
DOCKER_REG_PASS Masked Mot de passe registry Tous
SSHKEY_CI File Clé SSH → staging CI
SSHKEY_DEV File Clé SSH → dev Dev
DEPLOY_SSHKEY_FILE File Clé SSH → production Prod
SONAR_HOST_URL Variable URL SonarQube Tous

Aucune variable AUDIO_CDN_URL dans les variables CI/CD — cette variable est dans le docker-compose.yml de chaque environnement sur le serveur, pas dans le pipeline.


Fichiers de configuration par environnement

Fichier Environnement Contenu
ansible/docker-compose.integration.yaml Staging Compose avec URLs staging
ansible/docker-compose.dev.yaml Dev Compose avec URLs dev
docker-compose.yml Local dev Compose pour développement local

Chaque fichier contient les variables d'environnement spécifiques à l'environnement (notamment AUDIO_CDN_URL).


Rollback

Via pipeline GitLab

GitLab → CI/CD → Pipelines → [pipeline précédent sur main] → deploy:prod:manual → ▶

Le pipeline précédent est encore disponible et son image est dans le registry.

Manuel sur le serveur

cd /docker-apps/racines

# Identifier la version précédente
docker images | grep racines-app

# Forcer une image précédente
docker compose pull app
# Modifier docker-compose.yml pour pointer sur l'ancien tag
docker compose up -d --force-recreate app

Durées typiques

Stage Durée estimée
test:unit 1–2 min
test:quality:sonarqube 2–4 min
package:image 4–8 min (cache npm)
deploy:int:ansible 1–3 min
Total push → staging ~8–15 min

Diagnostics

Le job test:unit échoue

# En local pour reproduire
npm test
# ou
npm test -- --verbose

Le job package:image échoue à la connexion au registry

# Vérifier les variables DOCKER_REG_* dans GitLab CI/CD Settings
# Tester manuellement :
docker login registry.id2real.net -u USER -p PASS

Le déploiement Ansible échoue sur SSH

# Vérifier que la clé SSH est bien configurée dans les variables File
# Vérifier l'accès SSH depuis le runner GitLab :
ssh -i $SSHKEY_CI ci_gitlab_deploy@serveur-staging -p 9257 "echo ok"

L'image déployée n'est pas la bonne version

# Sur le serveur :
docker inspect racines-app | grep Image
# Comparer avec le tag attendu dans le registry

Étapes suivantes