(Ce billet est la traduction (à 90% complète et francisée) d'un article de Rekado paru en avril 2015.)

Je travaille en tant qu'administrateur système dans un institut de recherche dans un environnement informatique très diversifié. Nous avons deux supercalculateurs en clusters (un sur CentOS et l'autre sur Ubuntu) avec environs 100 nœuds chacuns et des douzaines de postes de travail sous GNU/Linux. Une de mes tâches est de m'assurer que les utilisateurs peuvent faire tourner leurs programmes de bio-informatique, à la fois sur leurs postes de travail et sur les clusters. Seuls quelques outils et librairies de cette discipline sont assez populaires pour avoir été packagés pour CentOS ou Ubuntu, donc généralement on doit construire les applications (avec toutes leurs dépendances) sur les plateformes cibles.

1 Comment perdre du temps à compiler et à déployer des logiciels

En théorie, compiler un logiciel n'est pas très difficile. Une fois que les fichiers d'en-tête ont été installés sur la machine hôte, la compilation ne consiste plus qu'à configurer le build avec un script de configuration et de lancer GNU make avec les flags appropriés (c'est une hypothèse régulièrement violée par les programmes de bio-informatique, mais laissons ça de côté pour le moment).

Néanmoins, il y a des problèmes pratiques qui deviennent clairement embêtants dans un environnement partagé avec un grand nombre d'utilisateurs.

2 La compilation

Compiler directement sur la machine cible n'est une option que dans les cas les plus simples. Quand le système de build ou les dépendances sont plus compliqués, les administrateurs système sont tentés de ne lancer les grosses opérations qu'une seule fois. La plupart du monde s'accorderait à dire que les gestionnaires de paquets sont une grande avancée par rapport à la compilation: les étapes de build sont formalisées dans des sortes de recettes qui peuvent être exécutées par des outils de build de façon reproductible. Pour mettre à jour un paquet on modifie seulement sa recette. Les gestionnaires de paquets sont une bonne chose.

3 Les dépendances système

Les programmes non triviaux qui ont été buildés et liés dynamiquement sur une machine avec un ensemble de librairies et de fichiers d'en-tête à une version donnée ne peuvent vraiment marcher que sur un système qui aura les mêmes libraires dans les mêmes versions. Nos gestionnaires de paquets habituels permettent aux mainteneurs de spécifier des plages de versions, mais les binaires produits sur la machine de build ne vont marcher que sous les contraintes imposées au moment du build. Pour faire tourner un paquet à la fois sous, mettons, CentOS 6.5 et CentOS 7.1, il doit avoir été construit dans les deux environnements et on doit récupérer un binaire pour chaque plateforme.

Il y a des moyens pour émuler un environnement de build différent (par exemple mockbuild sous Fedora), mais on ne peut ignorer le fait que des programmes liés dynamiquement construits pour une sorte de système ne vont marcher que sur cette sorte de système. On peut changer au runtime les librairies qui vont être dynamiquement chargées, mais c'est un hack qui remet un problème des mainteneurs aux utilisateurs. Les programmes avec LD_LIBRARY_PATH ne sont pas une solution, tout comme ne le sont pas les liens statiques, l'équivalent de copier des bouts de librairies au moment du build.

4 Les conflits de versions

Les bibliothèques logicielles ou les applications qui sont pré-installées ou pré-packagées dans un système ne le sont peut être pas dans la version dont l'utilisateur dit avoir besoin. Prenons un utilisateur qui souhaite la dernière version de GCC pour compiler du code qui utilise des fonctionnalités arrivées dans C++11 (par exemple, les fonctions anonymes). Le support complet de C++11 est arrivé dans GCC 4.8.1, pourtant sur CentOS 6.5 seule la version 4.4.7 était disponible dans les dépôts. Peut être que l'administrateur système ne peut pas mettre à jour GCC globalement. Ou peut être que d'autres utilisateurs sur le même système partagé ont besoin de la version 4.4.7 (pour la compatibilité de bugs par exemple). Il n'y a pas de moyen facile pour satisfaire tout le monde, donc un admin sys pourrait abandonner et laisser les utilisateurs construire leurs propres paquets dans leur répertoire personnel, au lieu de résoudre le problème.

Par contre, compiler GCC est une tâche énorme pour un utilisateur, ils ne devraient vraiment pas avoir à le faire. On a déjà dit que les gestionnaires de paquets sont une bonne chose. Pourquoi leur interdire ces bénéfices ? Les techniques de gestion de paquets traditionnelles ne sont pas faites pour installer plusieurs versions d'une même application. RPM, par exemple, permet d'utiliser une base de donnée de paquets locale, mais yum ne marche pas avec plusieurs sources. […]

5 Interopérabilité

Un admin sys qui décide de packager une application en RPM […] et de maintenir un dépôt logiciel se retrouve le bec dans l'eau quand un utilisateur lui demande d'installer cette application sur un poste Ubuntu. Il y a bien quelques moyens pour transformen un RPM en DEB, avec des degrés divers de réussite, mais ça paraît dingue de devoir tout constamment convertir ou reconstruire quand le logiciel, ses dépendances et son mode de déploiement n'ont pas changés.

Et que se passe-t-il lorsqu'un nouveau venu de Slackware pointe son nez ? Ou quelqu'un d'Arch Linux ? Bien sûr, en tant qu'admin sys vous pourriez refuser de prendre en charge autre chose que CentOS 7.1, au diable ces utilisateurs. On dirait bien que c'est cette politique qui prévaut chez les admin sys, pour des raisons de facilité et/ou pratiques, mais je considère que ça n'aide pas et que c'est même en quelque sorte oppressant.

6 La gestion de paquets fonctionnelle avec GNU Guix

Heureusement, je ne suis pas le seul à considérer la gestion de paquets traditionnelle inapte à un certain nombre d'usages. Plusieurs projets ont pour but d'améliorer et de simplifier le déploiement et la gestion de logiciels, notamment un sur lequel je vais me pencher à la suite de cet article. En tant qu'amateur de programmation fonctionnelle, du language Scheme et de logiciel libre, j'ai été très intrigué par ma découverte de GNU Guix, un gestionnaire de paquets au paradigme fonctionnel, écrit en Guile Scheme, le language d'extension du système GNU.

En programmation fonctionnelle pure, une fonction produit toujours la même sortie lorsqu'elle est appellée avec les mêmes variables en entrée. Ça permet des optimisations intéressantes, mais surtout ça rend possible et dans certains cas facile de réfléchir au comportement d'une fonction. Elle ne dépend pas de variables globales, elle n'a pas d'effets de bord, et sa valeur de sortie peut être mise en cache étant donné qu'il est certain qu'elle ne change pas (avec la même entrée).

La gestion de paquets "fonctionnelle" introduit ce concept dans la construction et le déploiement de logiciels. L'état global dans un système équivaut à une installation système d'un paquet, d'une bibliothèque ou d'un fichier d'en-tête. Les effets de bord sont les changements dans l'environnement du système ou dans certains répertoires tels que /usr/bin. Refuser un état global revient à rejeter la hiérarchie habituelle du système de fichiers pour le déploiement et d'utiliser un chroot minimal pour construire le logiciel. L'introduction du manuel de Guix décrit leur approche comme suit:

Le terme "fontionnel" renvoie à un concept spécifique de management de paquets. Dans Guix, le build et l'installation du paquet sont vus comme une fonction, au sens mathématique du terme. Cette fonction prend des arguments en entrée, comme des scripts de build, un compilateur, des bibliothèques, et retourne un paquet installé. En tant que fonction pure, son résultat de sortie ne dépend que de ses entrées. Par exemple, elle ne peut pas se référer à des programmes ou à des scripts qui n'aient pas été explicitement donnés en paramètres. Une fonction de build produit toujours le même résultat avec un même ensemble d'entrées. Elle ne peut en aucun cas altérer l'environnement du système, par exemple elle ne peut pas créer, modifier ou supprimer des fichiers en dehors de son répertoire d'installation. Ceci est possible en faisant tourner les processus de build dans des environnement isolés (ou "conteneurs"), dans lesquels seuls leurs entrées explicites sont visibles.

Le résultat de ces fonctions est mis en cache dans le système de fichiers, dans un répertoire spécial appelé "le magasin" (the store). Chaque paquet est installé dans son propre répertoire, dans le store (par défaut "/gnu/store"). Le nom du répertoire contient un hash de toutes les entrées du paquet, donc changer un paramètre d'entrée crée un autre répertoire.

7 Isolé, mais partagé

Remarquez que les sorties des paquets sont toujours liées dynamiquement. Les bibliothèques sont référencées dans les binaires avec leur chemin de store en entier (avec runpath). Ces résultats de fonctions, les paquets, ne sont pas des répertoires monolithiques et auto-contenants comme on peut en trouver sous MacOS.

Chaque paquet construit est mis en cache dans le store, lequel est partagé par tous les utilisateurs du système. Par contre, ce qu'il y a dans le store n'a par défaut pas d'influence sur l'environnement des utilisateurs, aucun fichier ne vient polluer /usr/bin ou /usr/lib/. Chaque changement est restreint à /gnu/store.

Guix propose par conséquent des profils d'utilisateur pour mapper l'environnement d'un utilisateur à ce qu'il y a dans le store, lequel servant de cache empêche de compiler deux fois la même chose. Un profil n'est rien de plus qu'une "forêt" de liens symboliques vers des éléments du store. En ajoutant une autre couche de liens symboliques, Guix permet aux utilisateurs de passer simplement d'une version de leur profil à une précédente, permettant de remonter dans le temps.

Chaque configuration d'utilisateur est complètement isolée des autres, ce qui permet donc à différents utilisateurs d'avoir une version différente de GCC. Mieux, un simple utilisateur pourrait avoir plusieurs profils avec chacun une version de GCC et passer de l'un à l'autre.

Guix prend le management de paquets fonctionnel au sérieux, donc à part le noyau et le hardware de la machine il n'y a pas de dépendances à des "variables globales" (des bibliothèques ou headers du système). Ça veut aussi dire que le Guix store est rempli avec l'arbre entier des dépendances, jusqu'aux headers du kernel et aux bibliothèques C. Par conséquent, les programmes d'un Guix store peuvent tourner sur des distributions GNU/Linux différentes; un Guix store partagé me permet d'utiliser les mêmes logiciels sur un poste de travail sous Fedora et sur des clusters Ubuntu et CentOS 6.5.

Cela veut dire que le programme ne doit être packagé qu'une seule fois. Et puisque les recettes de packaging sont écrites dans un langage très déclaratif construit sur Scheme, le packaging est étonnament simple (et dans ce cas c'est une joie de travailler avec Scheme).

8 Liberté de l'utilisateur

Guix libère les utilisateurs des décisions de déploiement de leurs admin sys en leur donnant le pouvoir de construire leurs logiciels dans le store, un répertoire isolé du reste du système, en utilisant des recettes simples. Les administrateurs ont seulement besoin de configurer et de lancer le démon Guix, la pièce centrale tournant en root. Ce démon écoute les requêtes émanant de l'outil Guix en ligne de commande, qui n'a pas besoin de permissions root. Ce dernier permet aux utilisateurs de gérer leurs profils, de passer d'un génération à l'autre, de construire et d'installer des paquets via le démon. Celui-là gère le store, évalue les expressions de build et met les résultats en cache, puis met à jour la forêt de liens symboliques pour mettre à jour le profil utilisateur.

Les utilisateurs sont enfin libres de gérer leurs logiciels comme ils le souhaitent, avec un degré de précison qu'ils ne pouvaient atteindre qu'avec des compilations manuelles.

9 Partager le store entre stations de travail

Guix n'est pas voué à être utilisé de manière centralisée. Le démon est censé tourner sur chaque système en root et écouter les requêtes RPC de ses utilisateurs locaux. Dans un environnement de clusters et postes de travail cette approche demande un certain effort pour être efficace et sécurisée.

À l'inverse nous avons choisi de faire tourner le démon dans un unique serveur dédié, qui écrit les profils utilisateurs et le store sur une partition en NFS. Les nœuds du cluster et les postes de travail la montent en lecture seule. Du coup les utilisateurs perdent le confort de modifier leurs profils directement sur leur poste de travail ou sur les nœuls du cluster (parce qu'ils n'ont pas d'installation locale ni du client Guix ni du démon, et parce qu'ils n'ont pas accès en écriture), mais en contrepartie leur profils sont accessibles de n'importe où. Ils n'ont qu'à se connecter au serveur Guix, où ils peuvent installer ce qu'ils veulent ou revenir à un point antérieur de leur configuration. (donc je trouve que Guix gagnerait à ce qu'on puisse lancer des commandes RPC en ssh, de manière à ce qu'on ne soit plus obligés de se connecter au serveur explicitement).

10 Guix en tant que plateforme pour logiciels scientifiques

Depuis l'hiver 2014 j'ai commencé à packager des logiciels pour GNU Guix, qui a donc pendant ce temps accumulé une bonne part de logiciels classique ou obscures de bioinformatique. Une liste de paquets disponibles avec Guix, mise à jour tous les jours, est disponible ici. Nous avons aussi des modules Python pour l'informatique scientifique, ainsi que des languages de programmation tels que R et Julia.

Je trouve que Guix est une très bonne plateforme pour informatique scientifique dans un environnement hétérogène. Le projet suit les lignes directrices d'un système d'exploitation libre (Free System Distribution Guidelines) […]. Pour les logiciels qui imposent plus de restrictions d'usage ou de distribution (comme quand est utilisée la licence Artistic licence au lieu de la nouvelle Clarified Artistic licence, ou quand l'usage commercial est prohibé par la licence), Guix permet d'utiliser des modules en-dehors de l'installation de base en utilisant la variable GUIX_PACKAGE_PATH. Et comme les paquets sont de simples variables Scheme dans des modules Scheme, il est trivial de rajouter des paquets à la distribution en utilisant cette variable.

Si vous souhaitez en apprendre plus sur GNU Guix je vous recommande de regarder l'excellente page du projet. N'hésitez pas à me contacter si vous souhaitez en apprendre plus sur l'empaquètement de logiciel scientifique pour Guix. Ce n'est pas dur et nous pouvons tous bénéficier à joindre nos forces pour adopter cette plateforme utilisable, fiable, hackable et libératrice pour de l'informatique scientifique faite de logiciels libres.

La communauté Guix est très sympathique, aidante et réactive. Venez donc visiter le canal IRC #guix sur Freenode, où vous me trouverez sous le pseudo "rekado".