Web^ID
💬 Contact 💬 Contact

17 10 2019

retour à la liste des articles

Mise en place d'une Pipeline CI/CD Laravel sur Gitlab avec Docker

• écrit par [Web^ID]

Logo Laravel, GiLab et Docker

Cet article présente un exemple de mise en place d'une Pipeline Gitlab pour la compilation, les tests et le déploiement de projets Laravel.

 

Prérequis 

Laravel 5.8 / 6

Maitrise de Docker

Savoir déployer une image docker custom sur dockerhub ou autres repos https://hub.docker.com (cf. Article todo...).

Maitrise de Gitlab

 

Définition de la Pipeline

Gitlab permet de découper la Pipeline en "stages". Ici nous utiliserons 3 stages :

 

Build : c'est l'état où seront compilées toutes les dépendances et les composants en exécutant composer install et npm install. Nous verrons également que ces actions peuvent être exécutées en parallèles sur différents runners. 

 

Test : ici nous exécuterons les tests de code et de qualité.

 

Deploy : c'est la dernière étape qui permettra le déploiement de l'application vers vos serveurs de preprod, staging et prod. Cette action peut être automatique ou manuelle, ce qui est fortement recommandé pour un déploiement en production par exemple.

 

 

Voici un exemple de Pipeline qui build en parallèle composer et npm, puis qui joue une série de test et qui permet le déploiement (en prod) si tout a correctement fonctionné.

 

Amélioration : Cette pipeline peut être améliorée en ajoutant une dernière étape s'exécutant en parallèle du déploiement manuel (en prod) et permettant le déploiement automatique sur un autre environnement staging ou preprod. Il est également possible d'ajouter un second couche de test plus fonctionnel par exemple en utilisant Dusk.

 

Le partage de fichier entre stages

Gitlab permet 2 méthodes pour partager des fichiers entre les stages, l'utilisation du cache et les artefacts. Nous utiliserons ces 2 méthodes pour passer les fichiers dans nos différents stages. Cette action est évidement possible que pour les stages exécutés séquentiellement. Nous noterons également que cela améliore grandement les temps d'exécution et réduit la charge des runners.

 

Le Cache

Le cache est le premier mécanisme proposé par Giltlab pour le partage de fichier. Il est général à toute une branche et permet de conserver et partager ce cache entre plusieurs Pipelines. 

 

Nous pouvons par exemple mettre en cache les /vendor pour la branche master et réduire ainsi significativement le temps de traitement global.

 

Les Artefacts

C'est le second mécanisme proposé par Gitlab qui permet de générer des artefacts. Les artefacts sont exclusivement partagés entre les stages d'une même Pipeline. Ce sont les outputs des stages, ils sont compressés, versionnés et transmis dans le sens d'exécution de la pipeline.

 

Gitlab-ci.yml 

Le fichier utilisé par Gitlab pour la définition des pipelines est le fichier gitlab-ci.yml directement ajouté à la racine du projet. Nous allons maintenant reprendre pas à pas la construction de ce fichier pour la mise en place de notre pipeline décrite ci-dessus.

Image Docker custom

Je recommande fortement d'utiliser votre propre image docker pour la conception de votre pipeline. Vous pourrez également trouver beaucoup d'images préconstruites mais qui ne répondront certainement pas exactement à votre config serveur cible. Le but étant d'être le plus possible iso avec l'architecture serveur finale. 

 

Nous ne détaillerons pas dans cet article le contenu et la conception de cette image. Je vous joins le lien de mon image custom déposée sur le hub docker officiel laravel-docker ainsi que le code associé Dockerfile

 

L'intégration d'une image dans gitlab-ci.yml :

 

image: vincentcau/laravel-docker:latest

Vous remarquerez que j'utilise la même image pour tous mes stages, néanmoins il est possible d'utiliser des images différentes en fonction de besoin de vos stages. Voici l'implémentation d'une image spécifique à un stage :

 

npm:
  stage: build
  image: vincentcau/laravel-docker:latest

 

Vous pouvez également avoir besoin d'une image supplémentaire pour l'exécution d'un jobs comme MySQL. Il faudra alors utiliser l'argument service dans gitlab-ci.yml et définir si besoin de variables associés :


# Variables variables: MYSQL_ROOT_PASSWORD: root MYSQL_USER: homestead MYSQL_PASSWORD: secret MYSQL_DATABASE: sapiendo_id DB_HOST: mysql testing: stage: test services: - mysql:5.7 image: vincentcau/laravel-docker:latest

 

Le build stage

 

Dans ce stage nous pouvons paralléliser composer et npm sur 2 runners différents si vous avec configurez plusieurs runners dans votre Gitlab. Pour utiliser plusieurs runners en parallèles il faut utiliser le paramètre tag et leur attribuer le même nom de stage.

 

composer:
  stage: build
  image: vincentcau/laravel-docker:latest
  tags:
    - tag-runner1
  ....


npm:
  stage: build
  image: vincentcau/laravel-docker:latest
  tags:
    - tag-runner2
  ....

 

Voici le code complet avec l'utilisation de tag pour l'exécution des runners en parallèle, le cache, les artefacts et les scripts associés à composer et npm : 

 

composer:
  stage: build
  image: vincentcau/laravel-docker:latest
  tags:
    - tag-runner1
  cache:
    key: ${CI_COMMIT_REF_SLUG}-composer
    paths:
      - vendor/
  script:
    - composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
    - cp .env.example .env
    - php artisan key:generate
  artifacts:
    expire_in: 1 month
    paths:
      - vendor/
      - .env

npm:
  stage: build
  image: vincentcau/laravel-docker:latest
  tags:
    - tag-runner1
  cache:
    key: ${CI_COMMIT_REF_SLUG}-npm
    paths:
      - node_modules/
      - Modules/mon-module/node_modules/ #si utilisation de modules supp.
  script:
    - npm install
    - npm run production
    - cd Modules/mon-module/ && npm install && npm run production
  artifacts:
    expire_in: 1 month
    paths:
      - public/css/
      - public/js/
      - public/modules/
      - public/mix-manifest.json

Le test stage

 

C'est dans ce stage que seront exécutés tous les tests mis en place dans votre projet. Dans notre exemple nous utilisons security-checkerphpcsphpunit et artisan code:analyse. Le stage test récupère directement les artefacts et le cache des stages de build. Nous aurons également besoin d'utiliser une image de mysql pour exécuter nos tests.

 

testing:
  stage: test
  services:
    - mysql:5.7
  image: vincentcau/laravel-docker:latest    
  tags:
    - tag-runner1
  script:
    - ./vendor/bin/security-checker security:check
    - ./vendor/bin/phpcs --report=summary
    - php artisan migrate
    - php artisan migrate:refresh
    - ./vendor/phpunit/phpunit/phpunit --no-coverage
    - php artisan code:analyse

 

Ce stage est bloquant pour la suite du déploiement. Le développeur qui a push sur sa branche et qui rencontre une erreur recevra une notification d'échec et pourra se reporter aux logs du pipeline pour retrouver les erreurs en cours.

 

Le deploy stage

 

Ce stage est executé uniquement sur les branches designées par le paramettre only : et destinées au deploiement comme master, dev, preprod, rc. Il ne tournera pas sur les autres branches qui ne sont pas encore mergées sur ces branches designées. 

 

 

Dans ce stage vous pouvez utiliser les outils de déploiement comme deployer ou envoyer qui vous permettront de versionner et d'exécuter les commandes serveur nécessaires au bon déploiement de votre app sur le serveur distant.

 

Il faudra également préparer une connexion SSH avec échange de clés pour vous connecter à votre serveur distant depuis le container docker de votre pipeline.

 

Configurer la connexion SSH

Pour communiquer de façon sécurisée entre votre pipeline et votre/vos serveur(s) distant(s) il est nécessaire de mettre en place une connexion SSH avec échange de clés privées/publiques.

 

Voici la démarche à suivre pour la mise en place de cette connexion :

  1. Créer une nouvelle paire de clés SSH sur votre machine
  2. Déposer la clé publique sur votre serveur
  3. Déposer la clé privée sur Gitlab en utilisant les variables secrètes
  4. Utiliser la clé privée dans vos pipelines

 

1. Création de la clé SSH

 

Exécuter la commande ci-dessous dans votre terminal :

 

ssh-keygen -t rsa -C "gitlab@yourdomain.com" -b 4096

Ne pas mettre de passphrase.

 

Attention également à ne pas écraser votre clé locale si vous en utilisez déjà une.

 

2. Déposer la clé publique sur votre serveur

 

Connectez-vous sur votre serveur distant et coller la clé id_rsa.pub dans ~/.ssh/authorized_keys

 

3. Déposer la clé privée sur Gitlab en utilisant les variables secrètes

 

Dans Gitlab aller dans Settings > CI/CD et ouvrir l'onglet Variables 

 

 

Utiliser une clé que vous pourrez ensuite passer en paramètre de votre pipeline par exemple SSH_PRIVATE_KEY.

 

4. Utiliser la clé privée dans vos pipelines

 

Vous devez ensuite écrire un script qui permettra de passer la clé privée dans votre pipeline.

 

Voici un exemple :

 

.init_ssh: &init_ssh |
  eval $(ssh-agent -s)
  echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
  mkdir -p ~/.ssh
  chmod 700 ~/.ssh
  [[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config

 

Vous noterez que le format yaml permet d'écrire des scripts. Vous pouvez utiliser "." pour masque l'exécution de ce script set "&" pour le rendre réutilisable.

 

Vous pouvez rencontrer une erreur dans l'utilisation des fichiers dans Gitlab notamment au moment du déploiement. Je vous conseille donc d'écrire également un script pour modifier les droits des répertoires et des fichiers vendor :

 

.change_file_permissions: &change_file_permissions |
  find . -type f -not -path "./vendor/*" -exec chmod 664 {} \;    
  find . -type d -not -path "./vendor/*" -exec chmod 775 {} \;

 

Au final voici le résultat de notre stage deploy :

 

.init_ssh: &init_ssh |
  eval $(ssh-agent -s)
  echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
  mkdir -p ~/.ssh
  chmod 700 ~/.ssh
  [[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config

.change_file_permissions: &change_file_permissions |
  find . -type f -not -path "./vendor/*" -exec chmod 664 {} \;    
  find . -type d -not -path "./vendor/*" -exec chmod 775 {} \;


deploying:
  stage: deploy
  image: vincentcau/laravel-docker:latest
  tags:
    - tag-runner1
  script:
    - *init_ssh
    - *change_file_permissions
    - php artisan deploy yourdomain.com -s upload
  when: manual
  only:
    - master

 

Un dernier point, le déploiement est donc manuel et se déclenchera au click sur le bouton deploying. Cette action est possible en définissant le paramètre when : manual dans votre pipeline.

 

Vous souhaitez démarrer rapidement ?
Besoin d'accompagnement ?

Vous voulez en savoir plus ou vous souhaitez nous parler de votre projet ?
Nous sommes à votre disposition !