Skip to content

GitLab CI/CD

Vue d'ensemble

GitLab CI/CD est un outil intégré de GitLab pour l'intégration continue et le déploiement continu, permettant d'automatiser les tests, builds et déploiements directement depuis le repository.

Avantages clés

  • Intégration native : Directement dans GitLab
  • Configuration as Code : .gitlab-ci.yml versionné
  • Runners flexibles : Shared, group, project runners
  • Pipeline complexes : Jobs parallèles, conditions, artifacts

Structure de base

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

before_script:
  - echo "Setting up environment"

after_script:
  - echo "Cleaning up"

# Job de test
test:
  stage: test
  image: php:8.2
  script:
    - composer install --no-dev --optimize-autoloader
    - php artisan test
  artifacts:
    reports:
      junit: tests/results.xml
    paths:
      - storage/logs/
    expire_in: 1 week
  only:
    - merge_requests
    - main

# Job de build
build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE
  only:
    - main

Pipeline pour Laravel

# Pipeline complet Laravel
stages:
  - prepare
  - test
  - security
  - build
  - deploy

variables:
  MYSQL_ROOT_PASSWORD: secret
  MYSQL_DATABASE: laravel_test
  MYSQL_USER: laravel
  MYSQL_PASSWORD: laravel

cache:
  key: $CI_COMMIT_REF_SLUG
  paths:
    - vendor/
    - node_modules/

# Installation des dépendances
composer:
  stage: prepare
  image: composer:latest
  script:
    - composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
  artifacts:
    paths:
      - vendor/
    expire_in: 1 hour

npm:
  stage: prepare
  image: node:18
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - node_modules/
      - public/build/
    expire_in: 1 hour

# Tests unitaires avec Pest
pest_tests:
  stage: test
  image: php:8.2
  services:
    - mysql:8.0
  dependencies:
    - composer
  before_script:
    - apt-get update -qq && apt-get install -y -qq git unzip
    - docker-php-ext-install pdo_mysql
    - cp .env.testing .env
    - php artisan key:generate
    - php artisan migrate:fresh --seed
  script:
    - php artisan test --parallel --coverage --min=80
  artifacts:
    reports:
      junit: tests/results.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

# Tests d'intégration avec [React](<../development/react.md>)
react_tests:
  stage: test
  image: node:18
  dependencies:
    - npm
  script:
    - npm run test -- --coverage --watchAll=false
  artifacts:
    reports:
      junit: jest-junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

# Analyse de sécurité
security_scan:
  stage: security
  image: php:8.2
  dependencies:
    - composer
  script:
    - composer audit
    - php artisan enlightn  # Security scanner pour Laravel
  allow_failure: true

# Build Docker
build_image:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  dependencies:
    - composer
    - npm
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main

# Déploiement staging
deploy_staging:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - curl -X POST "$STAGING_WEBHOOK_URL" -H "Authorization: Bearer $STAGING_TOKEN"
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - main

# Déploiement production (manuel)
deploy_production:
  stage: deploy
  image: alpine:latest
  script:
    - curl -X POST "$PRODUCTION_WEBHOOK_URL" -H "Authorization: Bearer $PRODUCTION_TOKEN"
  environment:
    name: production
    url: https://example.com
  when: manual
  only:
    - main

Intégration avec Terraform

# Pipeline Infrastructure
terraform_plan:
  stage: plan
  image: hashicorp/terraform:1.5
  before_script:
    - cd infrastructure/
    - terraform init -backend-config="address=$TF_STATE_ADDRESS" -backend-config="lock_address=$TF_LOCK_ADDRESS" -backend-config="unlock_address=$TF_UNLOCK_ADDRESS"
  script:
    - terraform plan -out=plan.cache
  artifacts:
    paths:
      - infrastructure/plan.cache
    expire_in: 1 week
  only:
    - merge_requests

terraform_apply:
  stage: deploy
  image: hashicorp/terraform:1.5
  dependencies:
    - terraform_plan
  before_script:
    - cd infrastructure/
    - terraform init -backend-config="address=$TF_STATE_ADDRESS" -backend-config="lock_address=$TF_LOCK_ADDRESS" -backend-config="unlock_address=$TF_UNLOCK_ADDRESS"
  script:
    - terraform apply plan.cache
  environment:
    name: production
  when: manual
  only:
    - main

Déploiement sur Kubernetes

# Déploiement K8s
deploy_k8s:
  stage: deploy
  image: bitnami/kubectl:latest
  before_script:
    - echo $KUBECONFIG_BASE64 | base64 -d > kubeconfig
    - export KUBECONFIG=kubeconfig
  script:
    - kubectl set image deployment/laravel-app laravel-app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - kubectl rollout status deployment/laravel-app
    - kubectl get pods -l app=laravel-app
  environment:
    name: production
    url: https://app.example.com
  only:
    - main

Intégration Ansible

# Déploiement avec Ansible
ansible_deploy:
  stage: deploy
  image: quay.io/ansible/ansible-runner:latest
  before_script:
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - ansible-playbook -i inventory/production playbooks/deploy.yml -e "app_version=$CI_COMMIT_SHA"
  environment:
    name: production
  only:
    - main

Monitoring avec Prometheus

# Push métriques vers Prometheus
push_metrics:
  stage: monitor
  image: curlimages/curl:latest
  script:
    - |
      cat <<EOF | curl --data-binary @- http://pushgateway:9091/metrics/job/gitlab-deploy/instance/$CI_JOB_ID
      deployment_duration_seconds{job="$CI_JOB_NAME",pipeline="$CI_PIPELINE_ID"} $((CI_JOB_FINISHED_AT - CI_JOB_STARTED_AT))
      deployment_status{job="$CI_JOB_NAME",pipeline="$CI_PIPELINE_ID",status="success"} 1
      EOF
  when: on_success
  dependencies:
    - deploy_production

# Notification échec
notify_failure:
  stage: monitor
  image: curlimages/curl:latest
  script:
    - |
      curl -X POST http://pushgateway:9091/metrics/job/gitlab-deploy/instance/$CI_JOB_ID \
        --data-binary 'deployment_status{job="'$CI_JOB_NAME'",pipeline="'$CI_PIPELINE_ID'",status="failed"} 1'
  when: on_failure

Optimisations avancées

# Pipeline optimisé avec cache et parallélisme
variables:
  FF_USE_FASTZIP: "true"
  CACHE_COMPRESSION_LEVEL: "fastest"

# Cache distribué
cache:
  - key: composer-$CI_COMMIT_REF_SLUG
    paths:
      - vendor/
  - key: npm-$CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
      - .npm/

# Tests parallèles
test_unit:
  stage: test
  parallel: 4
  script:
    - php artisan test --parallel --processes=4

# Conditional jobs
deploy_staging:
  script:
    - echo "Deploying to staging"
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"
      when: manual

# Include external configs
include:
  - project: 'devops/ci-templates'
    file: '/laravel.yml'
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml

Ressources