Patrice Ferlet
Patrice Ferlet
Créateur de ce blog.
Publié le avr. 17, 2016 Temps de lecture: 5 min

Me suivre sur Mastodon

Docker, Xvfb et links

Un conteneur = un service, c’est une règle qui peut parfois vous donner du fil à retordre. Et justement, un cas nous est tombé dessus au travail: un service qui doit utiliser Xvfb. Ça fait bien deux services, et il faut les séparer.

Docker, l’outil de la décennie, le projet qui change la face du monde de l’informatique, et son lot de mauvaises idées par exemple, utiliser un conteneur qui lance plusieurs services à coup de supervisord.

Je comprends que parfois ça puisse permette de faire “d’une pierre 1568 coups”, mais ce genre de pratique va à contre-courant du principe de “micro-service”.

Avant de lire la suite ! je sais que certains vont me dire “bha c’est évident !!!”, ou alors “super… Tu découvres qu’on peut lier des conteneurs…”. Stop, j’estime connaitre assez bien docker, et je milite depuis très longtemps sur le fait de séparer les services. Mais je ne suis pas infaillible (et vous non plus). Il arrive que l’on passe à côté d’un chose évidente et ce fut le cas ici. Je ne vais pas prétendre vous révéler un truc incroyable, mais plutôt vous montrer que parfois on peut oublier la règle de l’art et passer à côté d’une notion simple qui vous sauve. Utiliser Xvfb dans un conteneur séparé n’était pas “évident” dans mon esprit, cela ne veut pas dire que je ne savais pas le faire :) bon là je me suis bien brossé, on va pouvoir passer à l’article !

Pourquoi séparer les services ?

Si vous ne séparez pas les services en plusieurs images:

  • d’abord la taille de l’image va être conséquente car vous allez y injectez tous les services
  • vous forcez le service principal à utiliser une version gelée des services dépendants
  • vous risquez d’avoir des soucis si un des services plante sans couper les autres (on va y venir)

Je vais reprendre les deux premiers point. Prenons un exemple “bête et méchant” d’un service web utilisant:

  • nginx
  • mysql
  • php

Si vous créez une image qui embarque tout ça, alors vous allez avoir besoin des 3 logiciels, de lancer le service nginx et mysql en parallèle (via supervisord) et vous ne laissez pas le choix des versions. Si par exemple je décide d’utiliser MariaDB à la place de MySQL, alors je vais devoir reconstruire l’image moi-même et par conséquent perdre un temps précieux. Alors que Docker permet de lier des conteneurs et donc de ne pas m’imposer telle ou telle version de MySQL.

Bref, vous l’aurez compris, la bonne pratique est de créer 3 images et de lier les conteneurs.

Mais comment ça se passe avec Xvfb ?

Si vous ne connaissez pas Xvfb, je vous fais un résumé rapide. C’est un serveur d’affichage, sans affichage. Voilà… En gros, c’est un serveur Xorg qui n’a pas besoin d’écran. On peut alors utiliser la variable d’environnement “DISPLAY” pour lancer un programme graphique. Inutile ? Si vous avez envie de lancer des tests Selenium par exemple, dans un conteneur, et bien sans ce bijou ce sera une douleur.

Bref. Ici au boulot, nous utilisons un service que j’ai développé, nommé Belphegor, qui permet de prendre une capture de site et nous renvoyer l’image en png, jpeg, etc. Nous utilisons Docker, et j’ai donc simplement intégré à l’image un service Xvfb pour lancer le service sur l’affichage virtuel. Erreur de ma part, j’utilise Xvfb-run, un script fourni avec le service, qui a la fâcheuse tendance à ne pas éteindre mon service Belphegor quand Xvfb plante.

Cyril Compigne, collègue sysadmin, me dit alors “bha dois bien y avoir un moyen de séparer les deux services en deux conteneurs, comme ça quand Xvfb plante, on le relance sans couper Belphegor”.

Mais bon sang!!! Pourquoi j’y ai pas pensé ?

Il a raison, et voilà la méthode dite “de bourrin” que vous pourrez améliorer.

Conteneur 1: Xvfb. On crée une image depuis une distribution qui offre Xvfb. Alpine (la distribution que j’adore pour les conteneurs docker) propose un paquet “xvfb” qui fonctionne très bien.

FROM alpine
MAINTAINER Patrice FERLET <metal3d gmail.com>

RUN set -xe; \
    apk add --update xvfb;\
    rm -rf /var/cache/apk/*;
    
CMD ["Xvfb", "-ac", "-listen", "tcp", ":99"]

Juste une petite explication. L’option “-ac” de la commande veut dire “accepte tout le monde”. Vous pourrez l’améliorer plus tard. Vous pourrez, plus tard, adapter cela en sécurisant le service pour n’accepter que les connexions de conteneur choisi. Je n’ai pas poussé plus que ça pour trouver la solution, on n’avait pas trop le temps…

Conteneur 2: Belphegor à qui j’ai viré l’installation et le lancement de Xvfb. On utilisera la variable “DISPLAY” pour définir où se trouve le serveur d’affichage.

Et voilà !

On build et on lance le serveur:

docker build -t $USER/xvfb .
docker run -it --rm --name xvfbserveur $USER/xvfb

Maintenant, prenons l’image “metal3d/belphegor:noxvfb”, ou tout autre conteneur ayant besoin de X:

docker run --rm -it -p 8000:8000 -e DISPLAY=xvfbserver:99 metal3d/belphegor:noxvfb 

Le nom “xvfbserver” est celui de mon conteneur Xvfb, docker va résoudre ce nom. Mais c’est pas tout…

Si jamais je coupe le conteneur xvfbserver alors Belphegor n’arrive plus à s’y connecter. Sauf que depuis deux versions, Docker utilise une gestion réseau qui me permet de relancer le conteneur Xvfb sans avoir à relancer le conteneur Belphegor car la résolution de nom ne se fait plus (par défaut) via le fichier “/etc/hosts”.

Du coup, en utilisant l’option “restart=always”, le conteneur xvfbserver redémarre sans intervention humaine. Le conteneur Belphegor, quant à lui, retrouve le nouveau serveur fraichement redémarré. Bref, un QOS amélioré.

Le mot de la fin

Quel que soit le type de conteneur que vous voulez créer, par pitié, évitez supervisord. Je sais que c’est pratique en soi, que ça permet de faire “un seul conteneur” qui gère tout comme un seul service. Mais cela va à l’encontre de la philosophie de micro-service.

À la place, proposez un fichier “docker-compose” et séparez vos services. Le monde en sera que meilleur !

comments powered by Disqus