Il y a quelques temps je lançais un billet sur le projet Gachette qui permet de publier mon blog. Dans la même lancée je souhaite aujourd’hui vous présenter un autre projet sur lequel je travaille de temps en temps : Genèse. Genèse est assez difficile à définir. Cependant je l’imagine comme une commande unique pour permettre à plusieurs développeurs de travailler sur un environnement commun et en lançant une instance par ticket/problème relevé.

Cet outil, bien qu’il soit utile et facile à utiliser, demande un minimum d’explication pour comprendre à la fois le concept et l’utilité réelle.
C’est pourquoi je vais avant tout poser la problématique principale qui a initiée le projet, puis montrer quelques usages intéressants qui sont plus parlants et finalement tenter d’expliquer son fonctionnement en interne.

Explosion d’artifice au dessus de l’eau

Photo trouvée sur le profil de Damian Gadal sur Flickr sous licence CC BY 2.0.

Ce qui a initié le projet

De nombreuses années d’expérience professionnelle dans le domaine informatique m’ont amené à rencontrer plusieurs problèmes et à réfléchir sur énormément de sujets.

L’un d’eux se porte sur l’environnement de travail : je parle de l’environnement système utilisé pour travailler sur un projet. J’ai relevé quelques éléments intéressants, similaires à des postulats :

  • on essaie souvent de se rapprocher de l’environnement système de la production : ceci pour repérer assez tôt d’éventuels problèmes
  • plus l’environnement entre collègues est similaire, mieux nous travaillons : si un collaborateur rencontre un problème il peut se tourner vers le reste de l’équipe pour avoir de l’aide
  • nous en arrivons souvent à faire un fichier docker-compose.yml pour cela : peut-être est-ce initialement plus facile de partager un fichier léger (docker-compose.yml) qu’une machine virtuelle entière ?
  • quand on nouveau collaborateur arrive dans l’équipe, il a pas mal de choses à apprendre sur le projet. Et il peine souvent - en plus de toutes les choses à apprendre - à devoir installer l’environnement initial
  • on utilise souvent un fichier docker-compose.yml très proche entre plusieurs projets : ce qui est normal puisqu’on utilise très souvent des technologies similaires qu’on maîtrise pour les vendre à nos clients
  • on travaille souvent sur plusieurs tickets, c’est à dire plusieurs problèmes émis par le client. Chaque fois que nous démarrons un nouveau ticket, dans l’idéal, nous devrions avoir un environnement sain et propre pour ne pas avoir de fioritures de nos travaux précédents

En discutant avec Richard Dern (qui a d’ailleurs fait une refonte de son site web avec un nouveau moteur de blog), nous avons réfléchi à un outil capable de résoudre plusieurs de ces problématiques. Le fil rouge était :

  • les environnements doivent rester isolés : utilisation d’un fichier docker-compose.yml pour générer un environnement
  • création d’un répertoire unique pour chaque nouveau ticket/projet/autre afin de garder quelque chose de sain et propre
  • éviter de devoir se répéter entre plusieurs projets : mettre en commun les éléments qui le peuvent, comme par exemple le type de base de données
  • permettre à plusieurs utilisateurs de travailler sur la même machine : nommage des environnements d’une manière particulière (avec le nom de l’utilisateur par exemple)
  • permettre à chaque développeur de personnaliser son environnement : par exemple s’il souhaite avoir une interface web pour la base de données, qu’il puisse rajouter l’outil qu’il souhaite
  • si nous travaillons sur 2 tickets d’un même projet, sachant que l’environnement est similaire pour un même projet, ces deux environnements doivent pouvoir exister en même temps : s’ils utilisent les mêmes ports d’adresses, cela va échouer. Il faut donc des ports aléatoires.
  • si les ports sont aléatoires, alors il faut un accès facile à chacun des environnements, par exemple à l’aide d’une interface Web commune à tous ces environnements

C’est assez difficile à se l’imaginer sans des explications, une démonstration et/ou quelqu’un qui nous guide pour nous montrer le fonctionnement, alors je vais vous présenter un cas d’usage.

Usage et exemple de Genèse

Imaginons un cas d’usage typique : un client détient une application web (faite avec un framework) qui utilise une base de données MySQL. Appelons cette application : memory_game.

Il est très probable que nous ayons d’autres clients qui utilisent MySQL. Et il est très probable que notre client fera des tickets de bugs que nous aurons à résoudre. Nous aurons probablement plusieurs collaborateurs (pour lesquels nous allons répartir les tickets).

Il utilise un fichier docker-compose.yml avec le contenu suivant :

version: '3'

services:
  php:
    image: toasterlint/php-apache-mysql
    ports:
      - "8888:80"
    volumes:
      - ./:/var/www/html

  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: mot2passe
      MYSQL_DATABASE: maBdd
    volumes:
      - ./base_de_donnees:/docker-entrypoint-initdb.d

On comprend donc qu’il y a :

  • un service (nommé db) pour une base de données MySQL,
  • un service (nommé php) pour l’application écrite en PHP sur le port 8888.

Avec Genèse, générer un nouvel environnement de développement reviendrait à faire :

# Génère un docker-compose.yml dans le dossier instances/memory_game à partir du profil nommé memory_game
./genese -p memory_game

instances/memory_game est un répertoire de travail :

  • avec le dépôt de version du projet memory_game,
  • un fichier docker-compose.yml prévu pour lancer un environnement complet,
  • et un fichier .env contenant des variables permettant au projet d’être autonome.

Si l’exécutable ./genese venait à disparaître, le dossier reste fonctionnel pour travailler (avec Docker Compose évidemment).

Si on travaille sur plusieurs problèmes en parallèle, on peut vouloir créer un dossier par problème. Imaginons le ticket 42 et le ticket 16 :

# Génère une instance du projet dans le dossier instances/ticket42
./genese -p memory_game -n ticket42
# Même chose avec le dossier instances/ticket16
./genese -p memory_game -n ticket16

Il faut récupérer une branche spéciale du service memory_game ?

# Récupère dans le dépôt Git fourni dans le service memory_game la branche maBrancheSpeciale
BRANCHE_MEMORY_GAME="maBrancheSpeciale" ./genese -p memory_game -n essaiautrebranche

Vous découvrirez dans un prochain article à quel usage je réserve cela 😉

Quelles sont les instances dont je dispose ?

# Liste l'ensemble des instances
./genese -i

Et j’obtiens une liste des dossiers ayant été créés.

Si je veux supprimer une instance spécifique :

# Supprimer l'instance ticket16
./genese -s ticket16

Et si on veut avoir une visualisation des instances lancées, d’accéder à leur détail, éteindre un conteneur, le rallumer, regarder les fichiers de journalisation (logs) ou encore entrer dans un conteneur avec une invite de commande, on peut utiliser l’interface Web Portainer fournie avec Genèse. Cela ressemble à quelque chose comme :

Impression écran d’une liste d’instances lancées sur la machine locale

Seriez-vous curieux de savoir comment cela fonctionne (sans entrer dans le technique évidemment !) ?

Comment ça marche ?

Nous allons voir que Genèse s’appuie sur quelques éléments clés comme :

  • l’instance : version autonome et isolée, située dans le dossier instance,
  • le service : la description nécessaire pour générer un service, par exemple MySQL,
  • le profil : contient une liste de services permettant de démarrer une instance.

Prenons l’exemple précédent, nous aurons :

  • deux services :
    • l’un nommé mysql pour la base de données,
    • l’autre nommé memory_game pour notre application,
  • un profil nommé memory_game qui liste les services mysql et memory_game.

Chaque fois que nous lancerons la commande ./genese avec le profil memory_game nous aurons la possibilité de générer une instance avec un nom spécifique.

Comment cela peut-il fonctionner ? Les services ont une structure permettant de décrire :

  • la partie du fichier docker-compose.yml qui va être utilisée,
  • la possibilité de donner le dépôt Git utilisé pour récupérer les sources du projet et la branche par défaut à récupérer,
  • la possibilité de donner un dossier extras permettant de surcharger les sources du projet récupéré (par exemple pour mettre des fichiers de configuration),
  • la possibilité d’agir à des moments clés de la génération de l’instance via des déclencheurs qui sont des scripts pour :
    • procéder à des changements AVANT le lancement des conteneurs Docker, par exemple modifier des fichiers de configuration,
    • agir APRÈS le lancement des conteneurs, par exemple pour lancer des commandes dans les conteneurs Docker lancés,
    • afficher des informations à la fin du processus de création de l’instance, par exemple pour afficher les ports utilisés par les différents conteneurs

À cela s’ajoute une interface Web (Portainer) permettant de contrôler les instances lancées et accéder à des fonctionnalités particulières de Docker.

La documentation de Genèse devrait se charger bien mieux que moi pour les détails techniques et les possibilités de cet outil.

Conclusion

Malgré l’exemple d’utilisation et quelques détails sur son fonctionnement, Genèse mérite qu’on s’y attarde : nous n’avons gratté que la surface.

Cela ressemble à un framework pour des profils de personnes de type devops afin de leur fournir un outil unique ; à la fois déployer des instances pour les développeurs ; et des instances de type test pour les personnes fonctionnelles.

Je ne sais pas comment suggérer de passer un peu de temps sur l’outil pour comprendre que c’est un bon investissement de temps. Surtout pour des personnes d’un profil DevOps et qui connaissent le scripting Bash. J’espère que la curiosité vous piquera au vif !

Nous nous retrouverons très probablement pour en reparler dans un cadre différent encore.