banniere aubay

< Tous les articles

Juin 22, 2020

CICD via Gitlab

 

Je vous propose une série de trois billets concernant la CICD via Gitlab.

  • Pipeline-trigger, un outil pour les gouverner tous.
  • Remote gitlab-ci, moins y’en a mieux c’est.
  • Docker factory, l’usine à images !

Pourquoi ces trois billets ? Je pars d’un constat simple. Maintenir un CICD dans une entreprise qui évolue est très chronophage. Etre en phase avec les normes de sécurité, être assez modulaire pour prévoir les spécificités de chaque projet ou encore gérer les tokens et autres variables globales… peut vite devenir un cauchemar. Et je sais de quoi je parle.

Consultant DevOps pour une société dans le secteur de l’assurance, notre environnement de CICD est composé d’une trentaine de projets différents. Tout autant de gitlab-ci.yml à maintenir. Je vais donc vous exposer les solutions mises en place après les webinars et autres conférences docker/gitlab auxquels j’ai participé.

 

Pipeline Trigger

 

En tant que DevOps mon équipe a été amenée à mettre en place des outils permettant de faire des livraisons unitaires, service par service, et globales, tous les services d’un coup. Jusque-là, normal me direz-vous … Et pourtant la première difficulté était là. Comme je vous le disais dans l’intro, la société dans laquelle j’effectue ma mission possède une trentaine de projets dont certains sont dépendants les uns des autres.

Pour automatiser au maximum, nous avons créé un projet supplémentaire pour rajouter une couche d’abstraction au fait de devoir lancer des jobs sur chacun de projets.

Dans ce nouveau projet, des jobs vont venir faire un trigger sur les autres projets via des curl pour être en mesure lancer des pipelines à distance.

Imaginons un cas simple, vous avez deux projets A et B à déployer et chacun est indépendant l’un de l’autre. Dans ce cas précis, on crée un job pour chacun des deux projets qui va faire le déploiement, un gitlab-ci qui pourrait ressembler à ça :

Maintenant, imaginez que A ait besoin que B soit déployé (ou buildé, ou pushé sur Nexus… bref, X raisons d’avoir une dépendance). La ça devient compliqué.

Pourquoi ?

Parce que pour lancer le job `deploy_A` on a besoin que le job `deploy_B` soit FINI ! Le problème étant que curl ne permet pas ça, car curl renvoie un status_code=OK si le curl lui-même s’est bien passé, s’il a réussi à créer un pipeline distant, c’est tout, sans aucune considération de l’état du pipeline distant que ça a créé. Et là pas le choix que de faire des actions manuelles qui consistent à lancer le pipeline de B, attendre la fin puis lancer le pipeline de A. Dans cet exemple où il n’y a que 2 projets, l’impact est limité. Quoique cela peut vite devenir contraignant si les pipelines durent dans le temps (migrations de données ou autres…), mais dans le cadre d’une trentaine de projets, avec des dépendances croisées un peu partout ou du legacy, c’est l’enfer.

Heureusement on est vite tombé sur un outil open source qui nous a sauvé la mise et a grandement augmenté notre capacité de management de notre CICD.

Pipeline-trigger

Cet outil est un simple script python, vraiment très bien pensé qui a été créé pour répondre à la problématique précédemment expliquée. Je profite de ce billet pour remercier l’auteur du script qui a eu cette idée géniale.

Je ne vous expliquerai pas le code ici, je vous invite donc à le lire, en suivant le lien plus haut, pour une meilleure compréhension du fonctionnement de l’outil.

Voyons ce qui change avec cet outil pour nos deux jobs précédents :

On a changé l’image pour utiliser l’image officielle qui contient le script.

Mais le changement se fait surtout au niveau du curl, là où maintenant on a un nouveau mot clé, `trigger`.

Comme le script utilise l’api de gitlab il est nécessaire de lui fournir un `API_TOKEN` afin de s’authentifier.

  • Le user a besoin d’un `DEPLOY_TOKEN` propre à chaque projet, il faut aller dans les settings des projets pour pouvoir les créer.
  • Le `PROJECT_ID` propre à chaque projet
  • `–target-ref` la branch que l’on souhaite utiliser sur le projet distant pour créer le pipeline
  • `–env` permet d’envoyer une variable d’environnement au pipeline distant.

Quelle est l’utilité d’utiliser le pipeline-trigger, puisque de prime abord il a l’air de faire EXACTEMENT la même chose que curl?

La grosse différence est dans le fait que `trigger` ne se contente pas de créer un pipeline distant à l’instar de curl, il attend la réponse du pipeline, et fait échouer le job si jamais le pipeline distant échoue !

Grâce à cet outil on est capable d’automatiser complètement l’exemple vu précédemment. Pipeline-trigger permet aussi d’exécuter des jobs manuels de façon automatique, c’est toujours bon à savoir, alors n’hésitez pas à lire la doc pour en apprendre plus.

 

Remote GITLAB-CI

Maintenant que vous êtes en capacité de pouvoir tout contrôler à partir d’un point central, on va voir comment optimiser la maintenance et la modularisation de notre CICD.

Plus on a de projets et plus on a de gitlab-ci. Et à chaque modification il faut penser à répercuter la modification sur tous les gitlab-ci… Ça peut vite devenir dur à maintenir et encore plus à faire évoluer.

Donc le billet sera consacré à une fonctionnalité bien trop méconnue de gitlab, les includes.

Une fois de plus mon équipe s’est confrontée à la gestion de nos gitlab-ci. Avec notre trentaine de projets, à chaque modification sur un job il fallait faire une trentaine de MR aux techleads des projets pour que ce soit mergé, puis remonté jusqu’en master… bref, un enfer.

Alors on a fouillé la doc de gitlab en étant persuadé que les ingénieurs qui bossent chez gitlab avaient pensé à une solution. Et effectivement. On a trouvé les includes qui permettent d’importer un gitlab-ci d’un projet distant… de la vraie magie !

On a donc fait un projet d’abstraction exactement comme pour le pipeline-trigger, pour n’avoir qu’un seul endroit avec tous les gitlab-ci.

Du coup plutôt que d’avoir un gitlab de plusieurs centaines de lignes dans chaque projet, on les a tous remplacé par ça :

6 lignes !!! De cette façon la gestion était transparente pour les développeurs et on avait plus besoin de faire de MR vers leurs projets pour faire nos modifications, cela a permis de mettre une distanciation entre la partie purement développeur et purement DevOps.

Le fait est que nous avions toujours notre problème de duplication de code pour chaque projet.

Du coup on s’est dit qu’on allait appliquer le même principe à ce même projet contenant tous les gitlab-ci. On a donc éclaté tous les jobs de nos gitlab-ci et avons créé un gitlab-ci par job.

Oui, ça fait quelques fichiers… mais vous allez voir, le bénéfice en vaut largement la chandelle.

Rien que de faire ça a permis de supprimer l’intégralité du code dupliqué. Car pour chaque job identique, on a laissé le même gitlab-ci sans en créé un qui serait la copie parfaite du premier. Et donc au final ça ne faisait pas tant de fichiers que ça en plus.

Une fois tous ces nouveaux gitlab-ci on a modifié les gitlab-ci que nous avons exporté. Pour rappel dans notre projet on avait à la base ça :

Nous avons maintenant ça :

Sauf que nos gitlab-ci service-X.gitlab-ci.yml eux ont toujours leurs centaines de lignes.

Donc appliquons les includes… et maintenant ils ressemblent à ça :

Infiniment plus simple.

Une fois fini de mutualiser nos gitlab-ci on s’est rendu compte en fait que nous étions passé d’une trentaine de gitlab-ci à une demi-douzaine avec une vingtaine de jobs différents. Notre CICD était finalement moins complexe que nous ne le pensions. Et vous, comment est votre CICD ?

Docker factory

 

Et voici le troisième billet de la saga CICD.

Après avoir vu le management des pipelines, le management des gitlab-ci… nous avons cherché, une fois de plus, à vouloir mutualiser les autres ressources disséminées dans notre gitlab.

Le plus gros point que nous ayons relevé à ce moment-là, était les variables CICD, tous les tokens et autres variables nécessaires au fonctionnement du pipeline. Et ça sur notre trentaine de projets… c’était la galère dès qu’un token n’était plus valide, il fallait trouver lequel ne fonctionnait plus et tous les endroits où le changer.

Notre objectif était donc de supprimer toutes ces variables et de pouvoir les contrôler à un seul endroit à l’instar des triggers et des gitlab-ci.

Pour la troisième fois nous avons créé un projet pour faire une couche d’abstraction.

Dans ces projets on a ajouté toutes les variables de tous les projets et on s’est dit, et bien comme on utilise des images docker dans notre CICD on va ajouter ces variables à nos images. On va donc faire nos propres images avec une surcouche sur les officielles que nous stockerons chez AWS.

Voilà comment on a fait :

    • Création d’un docker-compose (pourquoi compose et pas docker tout simple ? juste pour pouvoir utiliser le fichier .env)

    • Création d’un docker-compase.overrides

    • Création du .env

    • Mise en place du gitlab-ci

Ce qui donne cette architecture:

Et voilà avec ça vous avez votre propre usine à images avec toutes vos variables à l’intérieur.

Je tiens à préciser que ce moyen n’a vocation à être utilisé que pour des images utiles à la CICD, jamais pour des images destinées à aller en PROD, ce serait une énorme faille de sécurité.
Pensez aussi à mettre vos variable en protected/masked dans votre projet afin de tout de même faire les choses proprement.

Puisque qu’on aborde la sécurité, mon prochain article sera certainement sur Anchor un outil très utile pour la détection de failles de sécurité des images à destination de la PROD !

Partagez cet article
Julien

Julien

Ingénieur Développement Python

< Retour à tous les articles

Aller au contenu principal