Skip to content

🌶️ Options avancées

Nous allons utiliser un autre projet demo web-api pour découvrir les fonctionnalités autour de la gestion de conteneurs.

🐳 Packaging Docker

  1. forker le projet web-api via le bouton Fork
  2. ajouter un job 🐳_build-image pour générer une image Docker et pour la publier dans la container registry du projet
    • la version de l'image doit être le sha court du commit (astuce)
    • une manière simple est d'utiliser kaniko, cela évite de faire du Docker-in-Docker ou DinD
    • 💡 snippet en remplacant les $ uniquement par des variables prédéfinies par GitLab
      • *** par les informations de connexion à la registry
      • ### par l'URL de la registry
      • ??? par le sha court du commit
  3. commiter sur la branche main

Success

  • Le pipeline s'exécute correctement ✅
  • Dans le menu Deploy, l'image apparait dans la Container Registry avec comme version le sha court du commit ✅

Il n'est peut pas nécessaire de publier une image pour chaque commit.

  1. modifier le .gitlab-ci.yml pour que le job 🐳_build-image ne s'exécute que sur la branche main et sur les merge request
  2. créer une branche (sans merge request) no-docker
  3. Faire une modification par exemple dans samples/main.js
  4. commiter sur la branche

    Success

    • Le pipeline s'exécute correctement ✅
    • Le job 🐳_build-image ne s'exécute pas ✅
  5. créer maintenant la merge request de cette branche (attention à pointer sur la branche main de votre projet et pas de celle du projet d'origine qui est sélectionnée par défaut)

Un pipeline se déclenche automatiquement.

Success

  • Le pipeline s'exécute correctement ✅
  • Le job 🐳_build-image s'exécute ✅

🧪 Tester une API directement depuis la pipeline

GitLab fournit le mécanisme de services pour faire tourner un container en parallèle du container du job. Cela permet de pouvoir utiliser des services annexes comme une BDD lors d'une série de tests.

On peut aussi utiliser ce mécanisme pour déployer un container créé lors du job 🐳_build-image et ainsi faire des tests sur les API exposés par notre application.

  1. repasser sur la branche main
  2. modifier le .gitlab-ci.yml et ajouter un job 🛂_test-api qui s'exécute après le job 🐳_build-image.
    • Ce job utilise via un service l'image construite à l'étape précédente.
    • Ce job appelle plusieurs fois l'url http://webapp:8080/api/hello
    • snippet
      • alias permet d'avoir une résolution DNS locale lors de l'exécution du job
  3. commiter

Success

  • Le pipeline s'exécute correctement ✅
  • le job 🛂_test-api est exécuté ✅

🛂 Contrôle de sécurité

L'utilisation de dépendances open-sources, la complexification de la construction des applications font que les vecteurs de failles de sécurité sont de plus en plus nombreux. Pour contrôler cela, il existe de nombreux analyzers sur le marché. GitLab fournit des jobs CI avec les outils prépackagés.

A partir de la branche main:

  1. créer une branche check-security et sa MR associée (attention à pointer sur la branche main de votre projet et pas de celle du projet d'origine qui est sélectionnée par défaut)
  2. à partir de cette branche, modifier le .gitlab-ci.yml afin d'activer les jobs d'analyze SAST à partir des templates fournis par GitLab
    • pour l'analyse SAST
    • pour la détection de secrets
    • pour l'analyse de container
  3. configurer les templates
    • ⚠️ ces jobs sont liés au stage test fourni par défaut par GitLab
    • ℹ️ certains templates sont configurés pour s'exécuter sur les branches d'autres pour les MR...
    • 🎚 le job pour le scanning des containers nécessite de paramétrer le nom de l'image Docker à scanner via une variable globale au pipeline DOCKER_IMAGE
    • un petit snippet pour vous mettre sur la voie
  4. commiter

Success

  • Le pipeline de la MR s'exécute correctement et sans warning ✅
  • 3 nouveaux jobs apparaissent dans le pipeline :
    • container_scanning
    • secret_detection
    • semgrep-sast
  • Le pipeline de la branche ne comprend pas les nouveaux jobs ✅

NB: On aurait facilement pu forcer également l'exécution d'un ou plusieurs jobs sur les branches également en forcant son utilisation en surchargeant la configuration

Par exemple

Example

semgrep-sast:
rules:
    - if: $CI_OPEN_MERGE_REQUESTS  # Add it to a *branch* pipeline even if it's already in a merge request pipeline.
    when: always

Ces jobs génèrent des rapports intéressants. Il serait pratique de pouvoir avoir les informations dans le rapport de MR. Malheureusement, cette fonctionnalité n'est disponible qu'en version Ultimate de GitLab... Mais il existe un contournement via le mécanisme de notes disponibles dans les MR et les API exposées par GitLab. Cela ne permet évidemment pas une intégration aussi avancée mais permet de remonter certaines informations dans la MR.

Pour cela, il faut ajouter un job qui parse les résultats des analyzers.

Toujours sur la branche check-security:

  1. ajouter le fichier display-findings.js qui permet de parser les fichiers json, à la racine du repository
  2. En utilisant ce snippet, modifier le .gitlab-ci.yml pour que les résultats soient remontés dans la MR
    • Il faut surcharger les jobs existants pour que le nom des rapports générés fonctionnent avec ce nouveau job (exemple) car les jobs GitLab génèrent le même nom de fichier pour les rapports
    • Il faut ajouter une variable CICD BOT_TOKEN avec un token pour que l'on puisse appeler l'API. Pour cela, vous pouvez soit :
      • réutiliser le token créé dans l'étape 1 : release du projet
      • recréer un token si vous l'avez perdu

Success

  • Le pipeline s'exécute correctement ✅
  • Les vulnérabilités remontent dans la MR dans une note ✅

Info

On utilise dans le snippet .gitlab-ci.yml, la notion de définition de job :

  • une définition est préfixée par . et référencée par un &
    • .my-function: &my-function
  • ces fonctions peuvent être importées dans les jobs en appelant *my-function
    • Les méthodes définies dans cette définition peuvent être ensuite utilisées dans le job

⏱️ Scheduled pipeline

Il peut être pratique également d'avoir des pipelines qui s'exécutent de manière périodique : par exemple le matin pour construire une image avec le cache de dépendances mis à jour pour accélérer le temps d'exécution de vos jobs (en particulier quand on fait du JS ou du Java 😇). Un autre exemple est l'utilisation de bot tel que renovabot / dependabot pour scanner et détecter des potentielles nouvelles versions/failles sur des dépendances.

Configurons renovate sur notre projet.

Sur la branche main :

  • Créer dans le répertoire .gitlab le fichier renovate.json basé sur le snippet suivant
  • Commiter

Sur une nouvelle branche renovate :

  • Créer le fichier renovate-config.js en utilisant ce snippet
  • Ajouter dans gitlab-ci.yml, le job renovate qui ne doit s'exécuter que sur des pipelines schedulés
  • Modifier les autres jobs pour qu'ils ne s'exécutent pas sur les pipelines schedulés
  • Commiter les 2 fichiers

On configure maintenant le pipeline schedulé.

  • depuis le menu GitLab, ajouter un scheduled pipeline qui s'exécute toutes les 5min
    • Faire un cron qui s'exécute 10min après l'heure courante, par exemple 55 11 * * *
    • Ne pas oublier de positionner le bon fuseau horaire
    • Utiliser la branche renovate
  • sauvegarder

On peut lancer manuellement le pipeline pour vérifier que tout est ok.

Success

  • Le pipeline s'exécute correctement ✅
  • 2 MR sont créées pour proposer des montées de versions de composants

Note

On ne va pas attendre 10min. Mettre un rappel sur votre téléphone, par exemple, pour vérifier que dans 10min le pipeline s'est bien exécuté.

Continuer le lab.


🚸 Utilisation de child pipelines

Dans certains cas de figures, il arrive qu'un projet nécessite de déclencher le pipeline d'un autre projet. Pour cela, GitLab propose le mécanisme des child pipelines. Dans ce workshop, nous allons modéliser le cas où l'on souhaite déclencher le pipeline de notre projet web-app à chaque fois que le projet web-api exécute un pipeline avec succès.

Sur la branche main du project web-api:

  • ajouter dans .gitlab-ci.yml la référence pour déclencher le pipeline de votre projet xxx-web-app créé précédemment comme sur le snippet suivant
  • commiter

Success

  • Le pipeline s'exécute correctement ✅
  • Depuis le pipeline de web-api, on voit bien le lien vers le projet web-app ✅
  • Un pipeline du projet web-app se déclenche bien et s'exécute correctement ✅

🧨 Dynamic child pipeline

Pour aller encore plus loin dans l'adaptabilité de nos pipelines, il est possible de générer des jobs / pipelines dynamiquement et de le déclencher (ou trigger). Cela peut être pratique lorsque l'on souhaite, par exemple, construire une application pour différentes architectures CPU, ou dans différents niveaux de compatibilités...

Sur la branche main du project web-api:

  1. dans .gitlab-ci.yml, ajouter un job 🥸_generate-mustache qui génère un fichier .yml contenant nos jobs
    • vous pouvez vous inspirer de ce snippet utilisant l'outil mustache de templating
    • ⚠️ Penser à ajouter les fichiers data.json & ci-template.yml à la racine du projet disponibles ici
  2. ajouter un autre job 👶_child-pipelines qui utilise le fichier précédemment généré
  3. commiter

Success

  • Le pipeline s'exécute correctement ✅
  • Un downstream job est généré ✅
  • Les 3 jobs générés s'exécutent correctement ✅

On peut ainsi facilement, ajouter de nouveaux jobs dans un pipeline sans avoir à dupliquer les configurations yml pour chacune des nouvelles valeurs.

  • Dans le fichier data.json, ajouter "rebellion" dans la liste des items
  • commiter

Success

  • Le pipeline s'exécute correctement ✅
  • Un downstream job est généré ✅
  • Il y a maintenant 4 jobs générés ✅

🏃 Utiliser ses propres runners (optionnel)

GitLab fournit un grand nombre de shared runners, mais dans certains cas, il peut être pratique d'avoir un private runner pour exécuter des jobs nécessitant un environnement d'exécution particulier, manipulant des données sensibles, ...

Il existe plusieurs méthodes pour instancier un runner, voici le chemin vers la documentation :

Le plus simple reste via Docker/Podman en local.

  • Il faut créer le runner dans le menu Settings > CICD > Runners > Project runners et le bouton New project runner
  • Enregistrer le runner local avec le tag local
  • Récupérer le token généré dans le paragraphe Step 1
  • Avec Docker, la commande par défaut pour initaliser le runner : docker run -d --name gitlab-runner --restart always -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest
  • Puis enregister le runner auprès de GitLab : docker exec -it gitlab-runner gitlab-runner register
    • Mettre https://gitlab.com pour l'URL
    • Entrer le token récupéré
    • Donner un nom au runner, par exemple my-laptop
    • Mettre alpine comme image par défaut
  • Depuis le pipeline editor, modifier le job dumb_job pour qu'il s'exécute sur le nouveau runner via le mot clé tags
  • Commiter

Success

  • Le runner apparaît dans la liste des project runners à l'état Active ✅
  • Le pipeline s'exécute correctement ✅
  • Le job taggé s'exécute bien sur le nouveau runner (visible dans les infos du job dans le menu de droite) ✅
  • Les autres jobs s'exécutent toujours sur les shared runners GitLab ✅