Ad majorem lulzis gloriam
Vomito ergo sum


LXD/LXC, Docker et autres joyeusetés (partie 1 sur beaucoup potentiellement…)

Posted on

Et là, la marmotte, elle met le chocolat dans le papier d'alu.

Point de départ

on serveur à ma maison (qui sert notamment à héberger ces lignes) commence à se faire vieux et surtout à être relativement surchargé :

Ça fait donc beaucoup beaucoup de bordel au même endroit, dans la même petite Debian.

Dans l'absolu, cette approche ne me pose pas tellement de souci : tous les logiciels sont installés proprement (NginX, PHP-FPM, etc…) et maintenus correctement par le projet Debian ou au pire par un dépôt tiers. Je n'ai pas vraiment de mouton à 3 têtes (sauf peut-être Mastodon, c'est bien pour ça qu'il est conteneurisé).

Au quotidien, le principal problème, c'est surtout que j'ai peur de tout casser : je fais les mises à jour régulières de sécurité sans trop me poser de question mais au moment de passer de Jessie à Stretch, je me suis fait quelques belles frayeurs. phpBB notamment ne supportait pas vraiment bien le passage de PHP 5 à PHP 7.

Fort heureusement, étant très prudent de base, c'est pratiquement le seul effet de bord que j'ai eu.

on second problème avec ce setup, c'est que je ne peux que difficilement déléguer : je peux donner des accès distants en SSH, je peux toujours faire des trucs avec du sudoers, mais ça devient assez vite limité s'il faut déléguer des droits plus fins ou plus permissifs.

L'objectif est donc de changer le fusil d'épaule et de passer à quelque chose de plus modulaire. Pour déléguer, mais aussi pour séparer un peu les choses.

Comparaison des différentes solutions possibles

Je suis donc reparti sur le tableau blanc pour essayer d'imaginer les différentes alternatives et solutions qui pourraient s'offrir à moi.

Touche pas, ça marche

La première consistait à reprendre tout tel quel. Ça ne simplifie pas forcément la migration (sic!) et en plus, je me retrouve avec le même bordel à l'arrivée. Garbage-in, garbage-out, j'ai éliminé très rapidement cette solution.

VM partout, justice nulle part

En poussant un peu le curseur, je me suis dit que je pourrais aussi virtualiser tout : une machine virtuelle pour chaque grande famille d'applications, avec une adresse IPv6 publique. Bien entendu, il faudrait forcément rajouter des petites choses pour que tout se passe bien :

Évidemment, le démarrage risque d'être le plus douloureux : il faut créer un socle de configuration commun à toutes les VMs (SSH, NTP, configuration générale de zsh, etc…) qui peut prendre pas mal de temps. Une fois le socle en place néanmoins, déployer une nouvelle VM devrait prendre un temps minimal.

A priori, ça peut être une bonne solution, mais il y a tout de même quelques réserves à émettre :

Pas une mauvaise solution donc, mais il faut quand même rajouter quelques composants pour arriver à s'en sortir. En terme de ressources, ça n'est pas forcément à la portée de tout le monde non plus (faut une machine costaude).

Conteneur LXD/LXC

LXC est une solution de conteneur permettant d'installer des « vrais » systèmes. Contrairement aux conteneurs Docker, l'objectif est ici d'avoir une gestion très proche des machines virtuelles, tout en ayant une empreinte mémoire bien plus légère qu'une machine virtuelle. On parle alors de VE (Virtual Environment) plutôt que de VM (Virtual Machine).

On peut donc y retrouver les mêmes principes de fonctionnement et avantages que pour la solution précédente, mais l'inconvénient de la ressource en moins. Cela oblige également à déployer les mêmes outils complémentaires dans la plupart des cas (orchestrateur, etc…).

LXD est l'évolution logique de LXC : plus simple, plus propre, plus facile, plus rigolo. Si on doit choisir entre les deux aujourd'hui, je pense qu'il vau mieux privilégier LXD. Le seul incovénient pour le moment, c'est que LXD ne tourne correctement que sous Ubunutu. L'intégrer correctement dans d'autres distributions est prévu mais pas encore fait pour le moment.

Un inconvénient majeur à retenir tout de même : si on peut faire à peu près n'importe quoi en terme d'image, c'est limité à Linux. Impossible d'installer un FreeBSD, un OpenBSD ou même (horreur !) un Windows. Par contre, on a accès à une large palette de Linux, d'Alpine à CentOS, en passant par Ubuntu, Archlinux et d'autres.

Il est même possible de faire du nesting pour faire du conteneur dans du conteneur (donc Docker dans LXC par exemple).

C'est pour le moment la solution qui me semble la plus adaptée à mon besoin. Mais ça m'empêche pas de regarder ailleurs pour vérifier s'il y a pas plus marrant à faire.

Conteneur Docker

Pour séparer correctement des applications, pourquoi ne pas utiliser Docker ? Après tout, c'est fait pour. Des dizaines d'image de conteneurs disponibles sur le [hub|https://store.docker.com/|en|Docker] directement faite par des gens kissiconèsse© et prêts à l'emploi.

Le souci, c'est que c'est compliqué de faire du Docker « seul ». Il faut obligatoirement l'outiller un peu aussi pour arriver à faire quelques chose. Pour déployer un ou deux conteneurs un peu à l'arrache, ça marche très bien, mais dès qu'il s'agit de gérer toute une batterie de conteneurs, il faut obligatoirement outiller l'ensemble avec un orchestrateur de conteneurs par exemple.

Il y a plusieurs possibilités pour ça :

Du coup, pour rigoler, j'ai monté une petite maquette en Docker Swarm pour voir si ça pourrait correspondre au besoin :

Maintenant, Docker (et Docker Swarm) apporte aussi son lot de problème, que je vais essayer de résumer ci-après.

Le fontionnement de Swarm lui-même oblige à avoir des ports « publiable ». Je m'explique : vous faites tourner votre conteneur NginX et vous voulez publier le port 80. Swarm permet de le faire et à ce moment, le port 80 est accessible (en IPv4 et en IPv6) sur l'ensemble des nœuds du cluster Swarm. C'est cool mais ça veut aussi dire que pour avoir un second conteneur NginX, il faut obligatoirement un autre port. Il faut également prévoir un load-balancer pour balancer les requêtes entre les différents nœuds du cluster du coup : on ne peut joindre qu'un seul port 80 sur l'ensemble du cluster, mais si on redirige le port 80 vers un seul nœud et que ce dernier tombe, c'en est fini…

Une autre solution pourrait consister à utiliser Keepalived en frontal pour avoir une adresse VRRP partagée entre les nœuds du Swarm.

La logique de Docker veut qu'on fasse tourner un seul processus dans un conteneur. Sauf qu'avec un seul processus MariaDB, je peux faire tourner 10 bases de données différentes pour 10 applications différentes. Que faut-il que je fasse dans ce cas précis ? Rien n'est moins évident…

Dans le même ordre d'idée, un conteneur de type Sonarr, NextCloud ou Gitlab fait tourner bien plus qu'un seul processus et pourtant, ça ne choque personne. La documentation de [s6-overlay|https://github.com/just-containers/s6-overlay#the-docker-way|en|Github s6-overlay] précise que normalement faudrait avoir qu'un seul process par conteneur, mais qu'en fait on s'en bat les couilles. Et les mecs font des conteneurs super populaires, la logique, on repassera donc…

Il y a aussi quelques points de la mise en œuvre de Docker, spécifiquement pour les applications PHP, qui me semblent relativement discutables : hors de question de faire du Apache (faut pas déconner non plus !), il faut donc nécessaire un conteneur NginX et un conteneur PHP-FPM lié pour faire tourner une appli. Cela implique qu'il faut un volume partagé entre les deux. Jusque là, rien de choquant…

ais faut-il inclure les données non variables de l'application PHP (toute la partie « programme ») dans le conteneur également ? Dans un NextCloud par exemple, le dossier config/ et data/ sont les seuls à bouger en théorie. Pourquoi ne pas inclure tout le reste ?

La gestion de la configuration reste aussi un peu plus complexe : on peut faire quelques templates de configuration dans des volumes accessibles en lecture seule à plusieurs conteneurs, mais il y a souvent beaucoup de choses à inclure pour faire fonctionner certains composants. Typiquement, pour NginX : il faut un nginx.conf correctement configuré, il faut y adjoindre quelques autres fichiers de configuration (mime.types, fastcgi_params, etc…), le fichier de conf de l'appli elle-même, ainsi que le certificat et la clé privée.

Autant avec Salt, il suffit de déployer ces fichiers de conf correctement modélisés sur l'ensemble des nœuds ayant besoin de cette configuration. En  Docker Swarm, on peut certes faire la même chose, mais modifier la configuration de certains services à la volée est complexe.

Dans le même ordre d'idée, faire tourner certains types d'applications qui se basent sur de la communication inter-process ou sur le fait de lancer une commande d'une autre programme, peut rapidement tourner au cauchemar. Postfix lance la commande deliver de Dovecot pour livrer les mails : on fait comment quand tout est dans un conteneur ? Il faut forcément repenser les choses autrement.

on dilemme est donc à présent le suivant : il est certain que j'aurais du Docker d'une manière ou d'une autre (ne serait-ce que pour reprendre mon instance Mastodon). Tout mettre dans du Docker me permettrait éventuellement de me simplifier considérablement la vie dans certains cas, mais ça ne résout pas tout et surtout pas les soucis de délégation… Que faire donc ?

Du coup, on fait quoi ?

Et bien on maquette mes loulous, on maquette.

onter une maquette LXD va être très simple. Il y a beaucoup de choses à tester, mais ça permet de se mettre rapidement le pied à l'étrier.

onter une maquette Swarm n'est pas beaucoup plus compliqué et cela permet au moins de vérifier comment on pourrait faire fonctionner le tout.

La suite au prochain numéro !