Gitlab-CI, Jenkins ou encore CircleCI. Dans celui-là, on va retrouver deux concepts majeurs : les workflows et les actions.
Un workflow est un fichier yaml qui va contenir la description du pipeline. Celui-ci se décompose de la manière suivante :
On retrouve tout d’abord les conditions d’exécution du workflow. Parmi celles-ci, on va tout d’abord retrouver le type de déclencheur (aussi appelé event). On en retrouve de différents types :
On va ensuite pouvoir lui appliquer des règles de filtrage en fonction du type d’event. Ainsi, il sera possible de déclencher le workflow si le code a été modifié dans un certain dossier par exemple.
Ensuite nous retrouvons les jobs qui vont représenter les étapes logiques de notre pipeline. Ceux-ci contiennent des steps (étapes exécutées séquentiellement) ainsi que des paramètres servant à décrire les caractéristiques du job (dépendance entre les jobs, stratégie d’exécution...).
Exemple d'un workflow basique
Chaque job va être récupéré par un runner et être exécuté (si possible) en parallèle. Il est possible de mettre des contraintes sur nos différents jobs pour pouvoir choisir :
Pour les amateurs de mono-dépôt Git, Github Action permet d’avoir plusieurs workflows déclarés simultanément dans un même dépôt. Couplé avec les bonnes règles de filtrages (par dossier par exemple), on va pouvoir définir des workflows dédiés à des usages spécifiques :
Chaque workflow doit être dans son propre fichier yaml et ceux-ci sont regroupés dans un sous-répertoire workflows au sein du dossier .github à la racine du projet.
Sur le projet Tezos-Link, cela nous a permis de séparer les différentes parties de notre projet dans des workflows dédiés (front, back, lambda, infra…) ce qui nous permet d’avoir une vue basée par groupement logique de code.
Un fichier dédié pour chaque workflow
L’interface de Github est assez triviale d’utilisation. On y retrouve une vue détaillée en fonction de chacun de nos workflows ainsi que la possibilité de créer des badges spécifiques pour les fichiers README. Cependant, on regrettera assez rapidement l’absence d’un bouton dans l’interface pour déclencher manuellement l’exécution d’un pipeline sur un build donné. (Il est possible de le faire, mais cela demande de passer par des outils externes comme https://www.actionspanel.app/).
L’interface permet de voir chaque fois que notre workflow a été exécuté
Enfin pour la visualisation du workflow, on dispose également d’une interface nous présentant les logs de nos différents jobs ainsi que le temps d’exécution par steps. Les jobs sont déclarés les uns en dessous des autres ce qui entraîne une perte de lisibilité par rapport à la traditionnelle version graphique que l’on retrouve chez les différents concurrents comme Jenkins (blue-ocean) ou CircleCI par exemple.
Une interface pas ultra claire malheureusement
Lorsqu’on utilise Github Action pour mettre en place un pipeline, on remarque assez rapidement l’absence de la notion de stage (que l’on retrouve chez Gitlab-CI par exemple) et qui évolue plus vers une notion de dépendance (à l’aide du mot clé needs) qui est la bienvenue, car elle peut nous permettre un gain de temps significatif si correctement utilisé.
Prenons par exemple un pipeline à base de stage composé de la façon suivante :
Une des limitations que nous imposait le système de stage est que le “chemin critique” de notre pipeline passait forcément par l’attente de la tâche la plus longue avant de pouvoir avancer même si celle-ci n’est utile que bien plus tard.
Avec la logique de dépendance, il est possible de revoir la parallélisation des tâches et ainsi obtenir un potentiel gain de temps sur l’exécution de notre pipeline.
On se retrouve donc avec quelque chose de bien plus proche d’un diagramme de dépendance. Couplé avec les filtres, on se retrouve avec un système vraiment efficace.
Un autre point fort des workflows se situe dans les matrices de tests (appelé matrix). Ces matrices permettent d’appliquer un template sur un job afin de changer les conditions lors de l’exécution (runner utilisé, version de langage…). Cela va alors créer un nombre de jobs correspondant au produit des tableaux d'élément déclaré dans ma matrice.
Prenons un exemple. J’ai une application Node.js qui doit être connectée avec une base de données MongoDB. Je souhaite pouvoir tester que mon application est rétrocompatible avec plusieurs versions de Node.js et qu’elle fonctionne correctement avec MongoDB dans 2 versions différentes.
On se retrouve ainsi avec un workflow qui va comprendre 8 jobs générés dynamiquement correspondants à un produit entre les matrices node-version et mongodb-version.
Si je tente une montée de version de Node.js pour mon application, il faudra simplement que je rajoute la version voulue dans la liste et je pourrais ainsi tester le bon fonctionnement pour cette nouvelle version ainsi que la rétrocompatibilité avec les anciennes.
Ces jobs seront exécutés en parallèle et si une dépendance est mise en place vers ce job, elle attendra la fin de l’exécution des 8 jobs.
On notera quand même que la limite maximum à cette génération est de 256 jobs générés (ce qui peut être atteint assez rapidement s’il y a beaucoup de croisement de matrice).
On voit ici la duplication des jobs liée à la matrice
Ceci est particulièrement pratique pour éviter les duplicatas de code et représente un gain de temps non négligeable tant sur l’écriture du pipeline que sur sa maintenabilité.
Pour l’instant, nous avons vu un système de pipeline classique pour ce type de service. Une des principales nouveautés proposées par Github Actions est son système d’Action. Celui-ci est similaire à ce qu’on peut retrouver sur Azure DevOps ou CircleCI (avec les orbs). Une Action est similaire à une définition de fonction. On peut lui définir :
Cette “fonction” pourra être ensuite appelée dans les workflows qui y ont accès, réutilisée et même partagée. Cette description se fait au travers d’un fichier yaml présent dans le dossier .github/actions/<nom de mon action>. Pour l’heure, il est possible d’appeler exclusivement des conteneurs Docker et des scripts Node.js.
Son principal objectif est de permettre une utilisation facilitée de code complexe à mettre en œuvre ou demandant des outils spécifiques pour fonctionner.
Quelques exemples d’utilisation : abstraction du déploiement de l’infrastructure, wrapping autour de l’exécution de scripts utilitaires…
Exemple d’une action custom à travers un conteneur
L’Action d’exemple présentée ci-dessus va simplement prendre un nombre en paramètre et l’ajouter à la suite de la commande du conteneur. Cela me permettra de l’utiliser dans un autre workflow directement (sans avoir d’étape d’installation du binaire et de ces dépendances par exemple). Pour l’utiliser dans un workflow, il suffit simplement d’utiliser le mot-clé uses avec un chemin vers l’action (chemin relatif ou un dépôt Github) pour la récupérer et le mot clé with pour lui donner des inputs.
Utilisation de l’action run-container présente dans le dépôt
L’avantage de ce système est que cela permet très facilement de partager des actions en les plaçant dans un dépôt spécifique et en ciblant le dépôt en question dans le workflow. On se retrouve alors avec un modèle hautement partageable et réutilisable comme pour des modules Terraform ou des collections Ansible. Ces éléments sont essentiels dans la mise en place de workflow car des fonctions vitales pour un pipeline comme la récupération du dépôt (actions/checkout) ou la gestion des artefacts (actions/cache) se font au travers d’Actions proposées nativement par Github. Ces derniers ont d’ailleurs mis en place une marketplace où l’on pourra retrouver les Actions de la communauté.
La marketplace de Github Actions
Le but de cette marketplace est de faciliter le partage et la récupération d’Action produite par la communauté. Par exemple, l’Action officielle Terraform (proposée par Hashicorp) permet de nous éviter la mise en place de l’outil (et donc de gagner du temps). Le code de ces actions est disponible publiquement et peut être consulté facilement. (ex : setup-terraform)
Utilisation de l’action officielle hashicorp/terraform-github-action
Une des forces de ce système est donc le gain de temps procuré, tant sur le développement (je n’ai pas besoin de préconfigurer une image docker ou développer un script qui jouerai tout pour moi) que sur le temps d’exécution de mon pipeline (plus besoin de steps d’installation pour utiliser certains binaires par exemple).
Parmi les Actions proposés par la communauté, on en retrouve qui peuvent nous servir à faciliter les migrations depuis des outils de CI/CD déjà existant comme Jenkins par exemple.
Action permettant l'exécution de Jenkinsfile directement dans un workflow
Comme pour beaucoup de solution de CI/CD sur le marché, Github Actions arrive avec une offre compétitive :
En comparant sur le marché par rapport aux autres concurrents :
Produit | Offre |
---|---|
Github-Action | 2,000 minutes/mois |
Gitlab-CI (tier gratuit) | 2,000 minutes/mois |
CircleCI (tier gratuit) | 1,000 minutes/mois |
Travis (free trial) | 100 builds fixes |
Ces minutes 2000 minutes par mois peuvent subir un multiplicateur selon le type de machine cible. D’après la documentation de Github, les multiplicateurs en fonction des OS sont les suivants :
OS | multiplicateur (* minutes consommées) |
---|---|
Linux | 1 |
MacOS | 10 |
Windows | 2 |
Un runner utilisant Windows va donc avoir un ratio de 2 minutes écoulé pour 1 minute de build. Enfin, utiliser ses propres runners permet de se soustraire des règles liées aux runners de Github.
En termes de concurrence des jobs, Github est aussi dans le “tier” haut par rapport à ces concurrents sur le “free tier” sur ces runners (seul Gitlab-CI le dépasse) :
Produit | Nombre de jobs concurrents possibles |
---|---|
Github-Actions | 20 |
Gitlab | N/A |
Circle-CI | 3 |
Travis | 2 |
Pour les tiers plus importants (Teams, Enterprise), la différence (vis-à-vis des Actions) ne se jouera que sur le nombre de minutes totales passant à 13000 pour Teams (3000 gratuites + 10000) et 50000 pour Entreprise. La réelle différence se fera sur le stockage dans la registry Github Package Registry.
Avec un système de multi-pipeline et son système d’Actions, Github Actions fait un pas-de-géant dans le monde de la CI/CD. Celui-ci n’est plus dépendant de solution externe telle que Circle-CI ou Travis-CI et dispose désormais d’un produit complet CI/CD disposant d’une bonne partie des outils que l’on attend de ce genre de solution.
De nombreux projets open source particulièrement connus ont d’ailleurs amorcé leur migration vers cette nouvelle solution. Parmi ceux-ci, on peut citer hashicorp/vagrant, kubernetes/minikube, une grande partie des collections Ansible... (ex : ansible-collections/kubernetes).
A titre personnel, c’est une technologie que j’aime utiliser pour les gains qu’elle m’apporte :
Pour récapituler :
Les plus |
---|
- Directement intégré au sein du dépôt Github.<br>- Une syntaxe disposant de feature vraiment agréable à utiliser.<br>- Les actions apportent un vrai gain de temps pour la mise en place de pipeline.<br>- Une communauté particulièrement active autour de ce projet.<br>- Prix et capacité attractive (surtout pour les projets open source). |
Les inconvénients |
- Absence de visualisation sous forme de graphe ce qui rend la lisibilité complexe sur des pipelines de tailles importantes.<br>- Un manque regrettable de quelques features clés comme un trigger manuel des pipelines.<br>- Obligation de passer par des artefacts pour conserver des packages entre deux jobs. |