Blog Ingénierie Intégration continue : créez votre premier pipeline CI avec GitLab
Mise à jour : January 7, 2025
Lecture : 20 min

Intégration continue : créez votre premier pipeline CI avec GitLab

Vous débutez dans l'intégration continue ? Apprenez à créer votre premier pipeline CI avec GitLab. Lisez notre guide complet.

cicd-cover

Imaginons que vous ne connaissiez rien au concept d’intégration continue (CI) ni à son rôle clé dans le cycle de vie du développement logiciel.

À présent, supposons que vous travaillez sur un projet pour lequel l'intégralité du code est répartie dans seulement deux fichiers. Pour garantir le bon fonctionnement de ce projet, il est impératif que la concaténation de ces deux fichiers contienne la phrase « Hello world ».

Toute la réussite du projet repose sur cette simple phrase, car sans elle, tout serait compromis.

Conscient de cet enjeu, votre meilleur développeur logiciel a décidé de créer un script qui s'exécute dès qu’un nouveau morceau de code est envoyé aux clients.

Voici à quoi cela ressemble :

cat file1.txt file2.txt | grep -q "Hello world"

Même si, en l'état, ce script permet d'exécuter notre tâche, son déclenchement reste manuel. Et, avec une équipe de développement composée de 10 personnes, vous n'êtes pas à l'abri d'une erreur humaine qui pourrait vous coûter très cher.

La preuve en est, pas plus tard que la semaine dernière, un nouveau membre de votre équipe a oublié d'exécuter le script, provoquant des erreurs de compilation pour trois de vos clients.

Vous prenez donc la décision de résoudre ce problème, une bonne fois pour toutes, en utilisant le pipeline d'intégration et de livraison continues de GitLab. Par chance, votre code est déjà sur la plateforme. Il ne vous reste plus qu'à vous lancer.

Effectuer un premier test dans le pipeline CI de GitLab

À la lecture de la documentation de GitLab, nous savons qu'il suffit de réunir deux lignes de code dans un fichier appelé .gitlab-ci.yml:

test:
  script: cat file1.txt file2.txt | grep -q 'Hello world'

Nous le validons et constatons que la compilation s'est déroulée avec succès

Compilation réussie dans le pipeline d’intégration continue

Maintenant, remplaçons « world » par « Africa » dans le deuxième fichier et voyons ce qui se passe :

Échec de compilation dans le pipeline CI GitLab

Comme nous pouvions le prévoir, la compilation a échoué.

Nous avons désormais mis en place l'automatisation des tests.

À partir de maintenant, GitLab CI exécutera notre script de test dès que nous effectuerons un push du code vers le dépôt de code source dans l'environnement DevOps.

Remarque : dans l'exemple ci-dessus, nous supposons que file1.txt et file2.txt existent sur l'hôte du runner. Pour exécuter cet exemple dans GitLab, utilisez le code ci-dessous, qui crée d'abord les fichiers, puis exécute le script.

test:
before_script:
      - echo "Hello " > | tr -d "\n" | > file1.txt
      - echo "world" > file2.txt
script: cat file1.txt file2.txt | grep -q 'Hello world'

Pour simplifier notre démonstration, nous partons du principe que ces fichiers existent déjà sur l'hôte. Nous n'allons donc pas les créer dans les étapes suivantes.

Rendre les résultats des compilations téléchargeables

La prochaine étape consiste à empaqueter le code avant de l'envoyer aux clients. Alors, pourquoi ne pas automatiser aussi cette partie du processus de développement logiciel ?

Pour cela, tout ce que nous devons faire est de définir un autre job pour l'intégration continue.

Commençons par nommer notre job « package » :

test:
  script: cat file1.txt file2.txt | grep -q 'Hello world'

package:
  script: cat file1.txt file2.txt | gzip > package.gz

Nous avons maintenant deux onglets :

Deux onglets - générés par deux jobs

Cependant, nous avons oublié de spécifier que le nouveau fichier est un artefact de compilation, afin qu’il puisse être téléchargé. Nous pouvons corriger cela en ajoutant une section artefacts :

test:
  script: cat file1.txt file2.txt | grep -q 'Hello world'

package:
  script: cat file1.txt file2.txt | gzip > packaged.gz
  artifacts:
    paths:
    - packaged.gz

Vérifions que tout est en place :

Artefact de compilation téléchargeable dans le pipeline CI/CD de GitLab

Félicitations ! Tout semble fonctionnel. En revanche, dans la configuration actuelle, les jobs s'exécutent en parallèle. Cela signifie que notre application pourra être empaquetée, et ce, même si les tests échouent. Pour éviter que cela ne se produise, nous allons devoir exécuter les jobs de manière séquentielle.

Exécuter des jobs de manière séquentielle

Pour éviter d'empaqueter une application contenant des erreurs, nous allons faire en sorte d'exécuter le job « package » uniquement si les tests sont préalablement réussis. Pour commencer, définissons l'ordre d'exécution en établissant des étapes spécifiques :

stages:
  - test
  - package

test:
  stage: test
  script: cat file1.txt file2.txt | grep -q 'Hello world'

package:
  stage: package
  script: cat file1.txt file2.txt | gzip > packaged.gz
  artifacts:
    paths:
    - packaged.gz

Cela devrait maintenant fonctionner.

Nous souhaitons également garantir que la compilation (qui est représentée par la concaténation dans notre exemple) ne s'exécute qu'une seule fois. En effet, cette étape pouvant être chronophage, il serait dommage de l'exécuter inutilement.

Pour éviter cela, définissons une étape supplémentaire :

stages:
  - compile
  - test
  - package

compile:
  stage: compile
  script: cat file1.txt file2.txt > compiled.txt
  artifacts:
    paths:
    - compiled.txt

test:
  stage: test
  script: cat compiled.txt | grep -q 'Hello world'

package:
  stage: package
  script: cat compiled.txt | gzip > packaged.gz
  artifacts:
    paths:
    - packaged.gz

Jetons un œil à nos artefacts :

Artefacts de compilation dans le pipeline CI de GitLab

Tout a l'air de fonctionner. En revanche, il faudrait éviter de rendre le fichier « compile » téléchargeable. Pour cela, nous allons rendre nos artefacts temporaires expirables en définissant expire_in à « 20 minutes ».

compile:
  stage: compile
  script: cat file1.txt file2.txt > compiled.txt
  artifacts:
    paths:
    - compiled.txt
    expire_in: 20 minutes

Maintenant, notre configuration semble plutôt complète :

  • Nous avons trois étapes séquentielles pour compiler, tester et empaqueter notre application.
  • Nous transmettons également l'application compilée aux étapes suivantes pour ne pas exécuter la compilation à deux reprises (ce qui accélère le processus).
  • Et nous stockons une version empaquetée de notre application dans les artefacts de compilation pour une utilisation ultérieure.

Savoir quelle image Docker utiliser

Jusqu'ici, tout va bien. Cependant, en regardant de plus près, nos compilations semblent toujours lentes. Prenons un moment pour regarder les journaux (logs) :

Image ruby 3.1

En observant de plus près, nous remarquons la mention suivante : ruby:3.1. Cela signifie que GitLab.com utilise des images Docker pour exécuter nos compilations, et qu’il utilise par défaut, l'image ruby:3.1.

Cette image contient certainement de nombreux paquets dont nous n'avons pas besoin. Dans un souci d'optimisation de notre pipeline CI, il serait donc préférable de changer d'image. Après une courte recherche sur Google, nous découvrons qu'il existe une image Linux presque vierge appelée alpine. Nous allons alors l'utiliser. Pour cela, nous devrons ajouter image: alpine au fichier .gitlab-ci.yml.

Et voilà ! Nous avons maintenant réduit le temps de compilation de presque trois minutes :

Vitesse de compilation améliorée dans le pipeline de GitLab

Vous pouvez également trouver des images libres de droits sur mysql, Python, Java et php. Il est facile, alors, d'en choisir une pour notre pile technologique.

Note : il sera toujours préférable d'utiliser une image qui ne contient aucun logiciel supplémentaire dont vous n'avez pas besoin, car cela minimise grandement le temps de téléchargement.

Gérer des scénarios complexes

Imaginons maintenant un scénario un peu plus complexe. Par exemple, un nouveau client souhaite que nous empaquetions notre application au format .iso plutôt qu'en .gz.

Étant donné que le pipeline d'intégration continue gère tout le processus, et que les images ISO peuvent être créées avec la commande mkisofs, il suffit d'ajouter un job supplémentaire.

Voici à quoi notre configuration devrait ressembler :

image: alpine

stages:
  - compile
  - test
  - package

# ... "compile" and "test" jobs are skipped here for the sake of compactness

pack-gz:
  stage: package
  script: cat compiled.txt | gzip > packaged.gz
  artifacts:
    paths:
    - packaged.gz

pack-iso:
  stage: package
  script:
  - mkisofs -o ./packaged.iso ./compiled.txt
  artifacts:
    paths:
    - packaged.iso

Notez que les noms des jobs ne doivent pas être nécessairement identiques. S'ils l'étaient, il serait impossible de faire s'exécuter les jobs en parallèle dans la même étape du processus de développement logiciel.

Ainsi, dans l'exemple qui suit, ignorez le fait que les jobs et étapes portent le même nom.

Quoi qu'il en soit, la compilation échoue :

Echec de compilation dans le pipeline de GitLab

Le problème vient de mkisofs qui n'est pas inclus dans l'image alpine. Nous devons donc d'abord l'installer.

Gérer des logiciels et des paquets manquants

Selon le site Web d’Alpine Linux, mkisofs fait partie des paquets xorriso et cdrkit. Voici les commandes que nous devons exécuter pour installer un paquet :

echo "ipv6" >> /etc/modules  # enable networking
apk update                   # update packages list
apk add xorriso              # install package

Ces commandes s'exécutent de la même manière que toute autre commande au sein du processus d'intégration continue. La liste complète des commandes que nous devons transmettre à la section script devrait ressembler à ceci :

script:
- echo "ipv6" >> /etc/modules
- apk update
- apk add xorriso
- mkisofs -o ./packaged.iso ./compiled.txt

Cependant, pour des raisons sémantiques, plaçons les commandes liées à l'installation du paquet dans before_script.

Notez que si vous utilisez before_script au niveau supérieur d'une configuration, alors les commandes s'exécuteront avant tous les jobs. Dans notre cas, nous voulons simplement qu'elles s'exécutent avant un job spécifique.

Graphes acycliques orientés pour des pipelines CI plus rapides et flexibles

Plus haut, nous avons configuré des étapes pour que les jobs d'empaquetage ne s'exécutent qu'à la condition que les tests réussissent. Mais, que se passerait-il si nous voulions bouleverser le séquencement des étapes en exécutant certains jobs plus tôt qu'initialement prévu ?

Dans certains cas, le séquencement traditionnel des étapes peut ralentir la durée globale d'exécution du pipeline CI/CD. Pour éviter cela, nous pouvons choisir de personnaliser le séquencement de nos jobs.

Par exemple : imaginons que notre étape de test comprenne des tests lourds, prenant beaucoup de temps à s'exécuter. Supposons aussi que ces tests ne soient pas nécessairement liés aux jobs d’empaquetage. Dans ce cas, il serait préférable que les jobs d’empaquetage puissent démarrer sans attendre la fin de ces tests. C'est là qu'interviennent les graphes acycliques orientés. Ces derniers visent à rompre l'ordre normal d'exécution des jobs (ordre séquentiel) grâce à la création de dépendances entre certains jobs. Vous pouvez ainsi définir un ordre personnalisé pour exécuter les différents jobs de votre pipeline CI.

Grâce au mot-clé needs, GitLab crée des dépendances entre les jobs et leur permet de s'exécuter plus tôt, dès que leurs jobs dépendants sont terminés.

Dans l'exemple ci-dessous, les jobs d’empaquetage commenceront à s'exécuter dès que le test sera terminé. Ainsi, à l'avenir, si quelqu'un ajoute d'autres tests à l'étape de test, les jobs d’empaquetage commenceront à s'exécuter avant la fin des nouveaux tests :

pack-gz:
  stage: package
  script: cat compiled.txt | gzip > packaged.gz
  needs: ["test"]
  artifacts:
    paths:
    - packaged.gz

pack-iso:
  stage: package
  before_script:
  - echo "ipv6" >> /etc/modules
  - apk update
  - apk add xorriso
  script:
  - mkisofs -o ./packaged.iso ./compiled.txt
  needs: ["test"]
  artifacts:
    paths:
    - packaged.iso

Voici notre version définitive de .gitlab-ci.yml:

image: alpine

stages:
  - compile
  - test
  - package

compile:
  stage: compile
  before_script:
      - echo "Hello  " | tr -d "\n" > file1.txt
      - echo "world" > file2.txt
  script: cat file1.txt file2.txt > compiled.txt
  artifacts:
    paths:
    - compiled.txt
    expire_in: 20 minutes

test:
  stage: test
  script: cat compiled.txt | grep -q 'Hello world'

pack-gz:
  stage: package
  script: cat compiled.txt | gzip > packaged.gz
  needs: ["test"]
  artifacts:
    paths:
    - packaged.gz

pack-iso:
  stage: package
  before_script:
  - echo "ipv6" >> /etc/modules
  - apk update
  - apk add xorriso
  script:
  - mkisofs -o ./packaged.iso ./compiled.txt
  needs: ["test"]
  artifacts:
    paths:
    - packaged.iso

Nous venons de créer un pipeline ! Ainsi, nous avons trois étapes séquentielles avec les jobs pack-gz et pack-iso qui s'exécutent en parallèle à l'intérieur de l'étape d'empaquetage :

Représentation d'un artefact de compilation d'un pipeline CI

Améliorer votre pipeline CI

Nous allons maintenant découvrir comment améliorer notre pipeline d'intégration continue.

Intégrer des tests automatisés dans vos pipelines CI

L'objectif clé du développement logiciel avec une approche DevOps est de réussir à créer des applications offrant une excellente expérience utilisateur.

Avec cet objectif en tête, pourquoi ne pas renforcer le cycle de développement logiciel en cherchant à détecter d'éventuels bogues dès le début du processus ? Pour ce faire, ajoutons des tests à notre pipeline CI. De cette façon, nous pourrons résoudre les problèmes le plus tôt possible.

Par chance, le pipeline CI de GitLab nous facilite la tâche en proposant des templates de tests prêts à l'emploi. Tout ce que nous avons à faire, c'est d'inclure ces templates dans la configuration de notre pipeline CI.

Dans cet exemple, nous allons réaliser des tests d'accessibilité :

stages:
  - accessibility

variables:
  a11y_urls: "https://about.gitlab.com https://www.example.com"

include:
  - template: "Verify/Accessibility.gitlab-ci.yml"

Personnalisez la variable a11y_urls pour répertorier les URL des pages web à tester avec Pa11y et GitLab Code Quality.

   include:
   - template: Jobs/Code-Quality.gitlab-ci.yml

GitLab facilite la consultation du rapport de test directement dans la zone du widget de la merge request. Ce widget vous permet de voir la revue de code, l'état du pipeline et les résultats des tests au même endroit. La capture d'écran ci-dessous montre à quel point ce widget facilite le travail de vos équipes.

Exemple de rapport d'accessibilité dans le pipeline CI/CD de GitLab

Widget pour les merge requests en matière d'accessibilité

Exemple de rapport de test sur la qualité du code suite à une merge request dans GitLab

Widget de merge request pour GitLab Code Quality

Matrice des compilations

Dans certains cas, nous devons tester notre application dans différentes configurations, versions de systèmes d'exploitation et langages de programmation. Nous utilisons alors la compilation « parallel:matrix ». Cela nous permet de tester notre application à travers diverses combinaisons en parallèle dans un seul job. Maintenant, testons notre code avec différentes versions de Python et avec le mot-clé « matrix ».

python-req:
  image: python:$VERSION
  stage: lint
  script:
    - pip install -r requirements_dev.txt
    - chmod +x ./build_cpp.sh
    - ./build_cpp.sh
  parallel:
    matrix:
      - VERSION: ['3.8', '3.9', '3.10', '3.11']   # https://hub.docker.com/_/python

Lors de l'exécution du pipeline, ce job s'exécute en parallèle quatre fois, chaque fois en utilisant une image Python différente comme indiqué ci-dessous :

Exécution de jobs en parallèle dans le pipeline CI/CD

Tests unitaires

Que sont les tests unitaires ?

Les tests unitaires sont des tests ciblés et de petite envergure qui vérifient des composants ou des fonctions d'un logiciel. Ces tests permettent d'assurer qu'il fonctionne comme prévu. Ils sont essentiels pour vérifier que chaque partie du code fonctionne correctement et permettent de détecter les bogues le plus tôt possible dans le processus de développement logiciel.

Exemple : imaginez que vous développiez une application de calculatrice. Un test unitaire pour la fonction « addition » va vérifier si le résultat d'un calcul comme 2 + 2 est bien égale à 4. Si ce test est concluant, nous avons confirmation que la fonction « addition » fonctionne correctement.

Tests unitaires : les bonnes pratiques

Mettre en place des tests unitaires, c'est bien, mais il est possible d'aller encore plus loin pour faciliter la vie de vos équipes de développement.

Par exemple, lorsqu'un test échoue, vos équipes reçoivent une notification. S'engage alors un long processus de vérification des job logs afin de trouver et de corriger les erreurs. Ce processus est long et pourrait être optimisé.

Il est possible de configurer votre job pour qu'il utilise des rapports de tests unitaires (rapports détaillés des erreurs permettant de les traiter plus efficacement). GitLab affiche ces rapports dans la merge request et sur la page de détails des pipelines CI. Cela facilite l'identification des échecs, car il n'y a alors plus besoin de consulter l'intégralité du journal.

Rapport de test JUnit

Ci-dessous un exemple de rapport de test JUnit :

Rapport de test JUnit dans un pipeline d'intégration continue

Stratégies d'intégration et de tests de bout en bout

Afin de s'assurer que toutes les parties de notre code fonctionnent en harmonie (y compris les microservices, les tests d'interface utilisateur), il est capital de configurer un pipeline dédié à l'intégration et aux tests de bout en bout.

Ces tests sont exécutés chaque nuit et il est possible de configurer le système pour que les résultats soient automatiquement envoyés vers un canal Slack dédié. Ainsi, lorsque les équipes de développement arrivent le matin, elles peuvent rapidement travailler sur les problèmes identifiés la veille. L'objectif étant de détecter et de corriger les problèmes le plus tôt possible dans le processus de développement logiciel.

Environnement de test

Dans certains cas, nous avons besoin d'un environnement dédié pour tester correctement nos applications. On parle alors d'environnement de test. Avec le pipeline CI/CD de GitLab, nous pouvons automatiser le déploiement des environnements de test, et ainsi gagner un temps considérable.

Comme cet article se concentre principalement sur les pipelines d'intégration continue, nous ne nous attarderons pas sur ce point ici. En revanche, libre à vous de consulter la section dédiée à ce sujet dans la documentation GitLab.

Implémenter des scans de sécurité dans un pipeline CI

Voici comment mettre en œuvre des scans de sécurité dans un pipeline CI.

Intégration des SAST et des DAST

Avant toute chose, nous souhaitons garder notre code en sécurité. S'il y a la moindre vulnérabilité dans nos dernières modifications, nous voulons en être informés dès que possible. C'est pourquoi il est judicieux d'ajouter des scans de sécurité à votre pipeline CI. Ces scans vérifient le code à chaque commit et vous alertent dès qu'une faille est détectée.

Il existe deux types de scan :

  • les tests statiques de sécurité des applications (SAST)
  • les tests dynamiques de sécurité des applications (DAST)

Ci-dessous, consultez notre guide interactif qui vous montre comment ajouter des scans de sécurité à votre pipeline CI.

Cliquez sur l'image ci-dessous pour commencer.

Scans product tour

Grâce à l'IA et à ses capacités d'analyse, nous pouvons également obtenir des suggestions sur la manière dont les vulnérabilités peuvent être corrigées. Consultez cette démonstration pour plus d'informations.

Cliquez sur l'image ci-dessous pour commencer.

product tour explain vulnerability

Récapitulatif

Dans cet article, nous avons volontairement simplifié les exemples afin de faciliter l'intégration des différents concepts de GitLab CI.

Résumons rapidement ce que nous avons appris :

  1. Pour déléguer certaines tâches à GitLab CI, vous devez définir un ou plusieurs jobs dans .gitlab-ci.yml.
  2. Les jobs doivent avoir des noms, de préférence facilement identifiables.
  3. Chaque job contient un ensemble de règles et d'instructions pour le pipeline de GitLab. Ces derniers sont définis par des mots-clés spécifiques.
  4. Les jobs peuvent s'exécuter de manière séquentielle, en parallèle, ou dans l'ordre de votre choix grâce aux graphes acycliques orientés.
  5. Vous pouvez transférer des fichiers entre les jobs et les stocker dans des artefacts de compilation afin de pouvoir les télécharger depuis l'interface de GitLab CI.
  6. Ajoutez des tests et des scans de sécurité au pipeline CI pour garantir la qualité et la sécurité de votre application.

Ci-dessous se trouvent des descriptions des termes et des mots-clés que nous avons abordés dans cet article.

Mots-clés, descriptions et documentation

Mots-clés/termes Description
.gitlab-ci.yml Fichier contenant toutes les explications sur la façon dont votre projet doit être construit
script Définit un script shell à exécuter
before_script Utilisé pour définir la commande qui doit être exécutée avant tous les jobs
image Définit l’image Docker à utiliser
stages Définit une étape du pipeline CI (par défaut : test)
artifacts Définit une liste d'artefacts de compilation
artifacts:expire_in Utilisé pour supprimer les artefacts téléchargés après une durée spécifiée
needs Permet de définir les dépendances entre les jobs et permet d'exécuter des jobs dans l'ordre de votre choix
pipelines Un pipeline est un groupe de compilations exécutées par étapes (batch)

En savoir plus sur les pipelines CI/CD

FAQ sur le pipeline d’intégration continue

Comment choisir entre l'exécution séquentielle et parallèle des jobs dans un pipeline CI ?

Il y a plusieurs considérations à prendre en compte pour choisir entre l'exécution séquentielle et parallèle des jobs dans un pipeline CI. Ainsi, il faut considérer les dépendances entre les jobs, la disponibilité des ressources, les temps d'exécution, les interférences potentielles, la structure de la séquence de tests ou encore les coûts que cela implique.

Par exemple, si vous avez un job de compilation qui doit se terminer avant qu'un job de déploiement puisse démarrer, vous exécuterez ces jobs de manière séquentielle pour garantir le bon ordre d'exécution. En revanche, les tâches telles que les tests unitaires et les tests d'intégration peuvent généralement s'exécuter en parallèle, car elles ne dépendent pas de l'achèvement des autres.

Que sont les graphes acycliques orientés dans GitLab et comment améliorent-ils la flexibilité du pipeline CI ?

Un graphe acyclique orienté dans un pipeline CI permet d'exécuter des jobs en fonction de leurs dépendances, plutôt que dans un ordre strictement séquentiel. Ce graphe vous permet ainsi de définir un ordre d'exécution des jobs pour que ceux des étapes ultérieures commencent dès que les jobs des étapes précédentes se terminent. Cela réduit le temps d'exécution global du pipeline, améliore l'efficacité et laisse même à certains jobs la possibilité de se terminer plus tôt que s'ils avaient été exécutés dans un ordre purement séquentiel (du premier au dernier dans la liste).

Pourquoi est-il important de choisir la bonne image Docker pour les jobs d'un pipeline CI GitLab ?

GitLab utilise des images Docker pour exécuter des jobs dont l'image par défaut est ruby 3.1. Pour optimiser votre pipeline CI, il sera cependant crucial de choisir l'image la plus appropriée à vos besoins. Comprenez que les jobs téléchargent d'abord l'image Docker spécifiée. Si l'image contient des paquets supplémentaires inutiles, cela augmentera les temps de téléchargement et d'exécution. Il est donc important de s'assurer que l'image choisie contient uniquement les paquets essentiels afin d'éviter des retards inutiles dans l'exécution des jobs.

Prochaines étapes

Pour moderniser davantage vos pratiques de développement logiciel, consultez le catalogue GitLab CI/CD pour savoir comment standardiser et réutiliser les composants CI/CD.

Votre avis nous intéresse

Cet article de blog vous a plu ou vous avez des questions ou des commentaires ? Partagez vos réflexions en créant un nouveau sujet dans le forum de la communauté GitLab. Partager votre expérience

Lancez-vous dès maintenant

Découvrez comment la plateforme DevSecOps unifiée de GitLab peut aider votre équipe.

Commencer un essai gratuit

Découvrez le forfait qui convient le mieux à votre équipe

En savoir plus sur la tarification

Découvrez ce que GitLab peut offrir à votre équipe

Échanger avec un expert