25 avril 2020

GuixSD, une distrib Linux originale: présentation et déploiement dans le cloud

GuixSD est une distribution Linux inspirée de Nix et unique en son genre: mises à jour atomiques, gestion des packages par utilisateur, rollbacks faciles, configuration des outils en Guile (une version de Scheme)…​ On ne voit pas ça tous les jours. Je présenterai dans cet article cette distribution, et montrerai comment la déployer dans le Cloud.

Note sur Scheme/H.S

Comme dit précédemment, la configuration de Guix se fait via le langage Guile. Cela peut sembler surprenant aux premiers abords, mais personnellement je trouve cela très intéressant.

Déjà, pourquoi un langage de type Scheme et non quelque chose de plus "classique" (comme Python par exemple) ? Les langages de la famille des Lisp sont généralement beaucoup plus flexibles que leurs homologues d’autres familles, et ont une syntaxe concise et cohérente.
Si vous n’avez pas l’habitude de Lisp/Scheme, insistez un peu, c’est juste une famille de langages différente de ce qu’on a l’habitude de voir. Et ces langages ont beaucoup d’avantages.

De manière générale, je préfère configurer si possible mes outils avec un vrai langage de programmation et non avec des DSL limités inventés pour l’occasion (coucou Prometheus). C’est d’ailleurs pour moi la grande force d’outils comme Riemann, dont les idées n’ont malheureusement pas été reprises dans d’autres outils de monitoring.

Au commencement: l’installation

Avant de pouvoir tester la distribution, il faut d’abord l’installer.
Une solution serait d’installer la distribution sur votre ordinateur. Cela peut marcher, mais GuixSD est une distribution founissant une quantité limitée de drivers (la distribution faisant partie du projet GNU, les drivers propriétaires en sont exclus), cela peut donc vous poser des problèmes.

Il est également possible d’installer Guix, le package manager de GuixSD (qui est la distribution), sur une distribution comme Debian ou Ubuntu, ce qui permet de l’utiliser tout en conservant une autre distribution comme distribution principale.
J’avais testé celle solution (Guix + Debian) il y a quelques années, mais j’avais rencontré de nombreux problèmes de conflits entre les packages venant de Guix et ceux venant de Debian, je pense donc que ce n’est pas une bonne idée.

La dernière solution est de déployer GuixSD sur un environnement contrôlé: le Cloud. Ici, pas besoin de drivers, et ça nous permettra de tester facilement la distribution. C’est la solution que nous allons explorer dans cet article.

Créer une image GuixSD

Nous allons tout d’abord créer une image GuixSD qui sera ensuite envoyée sur le Cloud. Une solution pourrait être de démarrer avec par exemple KVM l’iso d’installation de GuixSD, et de suivre le manuel (que vous trouverez ici).

Mais comme d’habitude, on va essayer d’automatiser un peu la chose, et nous allons construire notre image avec Packer. Cela nous permettra de reconstruire à tout moment notre image si besoin, en une commande.

Si vous ne connaissez pas Packer, je vous recommande la lecture de mon article sur le sujet qui donne un bon aperçu de l’outil.

GuixSD et Packer

Pour construire notre image GuixSD avec Packer, nous allons tout simplement suivre la documentation officielle pour une installation en mode shell de GuixSD, et l’implémenter dans Packer (avec quelques améliorations).

On part donc de l’iso d’installation de GuixSD, simulons le clavier grâce à l’option boot_command du builder QEMU, et lançons de cette façon l’installer en mode shell. Vous pouvez retrouver la boot_command utilisée ainsi que le fichier packer.json complet que j’ai utilisé pour cet article ici.

Nous continuons ensuite l’installation, toujours via l’option boot_command, en faisant exactement ce qui est décrit dans la documentation GuixSD, c’est à dire:

  • Configurer le partitionnement avec cfdisk.

  • Créer un système de fichier ext4.

  • Monter le système de fichier, lancer herd start cow-store /mnt, puis lancer l’installation de GuiXSD

Une chose intéressante est que l’installation du système d’exploitation se fait de manière déclarative via un fichier config.scm (la section correspondante dans la documentation est trouvable ici).

Si on suit la documentation officielle, on doit écrire ce fichier à la main.
Mon premier réflexe a été d’essayer d’implémenter ce qui est possible de faire pour d’autres distributions Linux (Red Hat par exemple): faire exposer le fichier de configuration (Kickstart, Preseed…​) via HTTP par Packer, et d’une façon ou une autre le faire "manger" par l’installer.

Pour les distributions Linux mainstream, c’est généralement supporté nativement par l’installer. Je n’ai pas trouvé comment réaliser cela avec GuixSD, il a donc fallu innover (surtout que les commandes comme curl, wget…​ ne sont pas disponibles dans le shell d’installation).

On expose donc avec Packer un dossier contenant notre fichier .scm grâce à l’option http_directory de Packer. Ensuite, il fallait récupérer ce fichier.

Heureusement, je me suis vite rendu compte que guile était disponible dans le shell de l’installer. J’ai donc passé cette commande (toujours via boot_command de Packer) à l’installer:

guile -c '(use-modules (web client)) (define-values (a b) (http-request \"http://{{ .HTTPIP }}:{{ .HTTPPort }}/config.scm\")) (display b)' > /mnt/etc/config.scm

Les variables {{ .HTTPIP }} et {{ .HTTPPort }} seront remplacées automatiquement par des valeurs choisies par l’outil pour exposer le fichier HTTP. De cette façon, je récupère le fichier décrivant mon installation pour pouvoir ensuite la démarrer.

Mon fichier config.scm est trouvable ici.

Le fichier config.scm

Ce fichier décrit comment votre système est installé, ce qui inclut notamment:

  • L’hostname de la machine, sa timezone, la configuration de la locale.

  • Les systèmes de fichiers et la configuration du boot.

  • Les utilisateurs, les groupes, la configuration du fichier sudoer.

  • Les packages et services à installer et à lancer.

  • Et plein d’autres choses.

l’idée de GuixSD est de ne jamais installer ou configurer un package manuellement (par exemple en posant un binaire dans /bin, ou en modifiant un fichier dans /etc manuellement) mais de tout faire de manière déclarative via ce fichier de configuration.

Durant mon installation, je pars donc d’un fichier qui me crée un utilisateur appelé guix, qui a les droits sudo sur la machine, et qui a un mot de passe par défaut. Sinon, le reste de ce fichier est assez basique (j’installe juste le package curl en plus des packages par défaut).
Les packages configurés dans ce fichier seront installés globalement, et disponible pour tous les utilisateurs.

Je reparlerai de ce fichier dans la suite de l’article.

Les scripts d’installation

Une fois l’installation principale terminée, la machine va redémarrer et Packer va exécuter la section provisioners via SSH.
Concernant la connexion SSH, j’ai fait le choix (par simplicité on va dire) de configurer la machine virtuelle avec un mot de passe SSH "en dur" dans le fichier packer.json ainsi que dans le fichier config.scm (les valeurs doivent bien sûr être les mêmes).

Je vous conseille fortement de changer ce mot de passe dans le script/désactiver l’authentification par mot de passe pour une configuration de production.

Dans mon cas, je me contente de poser un fichier .bash_profile et d’exécuter un script qui mettra la distribution à jour.

Enregistrement sur Exoscale

Il n’y a plus maintenant qu’à envoyer le template sur le Cloud. Dans mon cas, je le déploierai sur Exoscale. Il est possible d’utiliser le post-processor Exoscale pour Packer, ou bien de le faire avec un autre outil (comme la cli, ou bien l’interface web).

Premier boot et configuration

On peut maintenant démarrer notre machine virtuelle, et commencer à jouer avec en se connectant dessus en SSH (en utilisant le mot de passe défini précédemment).

Installer des packages

Subtitutes

Les packages de GuixSD sont par défaut compilés sur la machine après récupération. Il est possible de configurer GuixSD pour (essayer de) récupérer des binaires précompilés depuis un serveur.
Un serveur officiel existe pour fournir ces binaires. Il est censé être activé par défaut sur GuixSD mais ça n’avait pas l’air d’être le cas pour moi (le fichier /etc/guix/acl était vide et me posait des problèmes). C’est pour cela que j’exécute dans le script passé à Packer les lignes suivantes.

sudo rm /etc/guix/acl
sudo guix archive --authorize < $(sudo find / -name "ci.guix.gnu.org.pub")

Ces deux lignes permettent de configurer GuixSD avec le subtitute officiel. C’est peut être pas la façon officielle pour configurer un subtitute, mais je n’ai pas trouvé dans la documentation comment faire autrement.

Mettre à jour le système

La commande guix pull permet de télécharger les nouvelles versions des packages ou de Guix disponibles. Ensuite, a commande guix package -u permet de mettre les packages à jour.

Ces commandes sont à exécuter par utilisateur. Souvenez-vous, dans GuixSD, tout est cloisonné par utilisateur, packages inclus.

Attention, cette opération peut être très longue, surtout si vous avez le malheur de devoir compiler un package un peu gros. C’est pour ça que j’exécute également ces cmmandes dans le script passé à Packer.
De manière générale, j’ai trouvé que GuixSD mettait beaucoup de temps à se mettre à jour ou à installer de nouveaux packages.

Installer un package

Installons par exemple le package screen:

guix package -i screen

Ce package sera installé seulement pour l’utilisateur courant. Si vous changez d’utilisateurs (si vous passez root par exemple), la commande screen ne sera pas disponible.

/etc/config.scm

Le fichier /etc/config.scm contient la définition de l’installation globale du système (la même qui a été utilisée par Packer). Si vous voulez définir un nouveau utilisateur, modifier une configuration…​ Modifiez ce fichier, et lancez guix system reconfigure /etc/config.scm.

Rollbacks

Tout l’intérêt de GuixSD est de pouvoir rollback facilement (que ce soit au niveau système ou utilisateur) sur une "version" précédente. Donc si vous cassez votre système après une mise à jour, il devrait être en théorie très facile de rollback sur une version antérieure.

Voici un article provenant du site de Guix sur le sujet si cela vous intéresse. Je n’ai pas testé cette fonctionnalité (cf la section Pour aller plus loin où j’explique pourquoi).

Pour aller plus loin

A la base, j’avais prévu un article beaucoup plus conséquent sur GuixSD (installation de packages, de services, intégration Ansible, rollbacks…​). Mais après avoir passé pas mal de soirées à jouer avec la distribution, j’arrive aux conclusions suivantes:

Documentation

Le manque de documentation fait qu’il est quasiment impossible de comprendre comment utiliser la distribution (configurer un service, ou comment construire un package par exemple).

La documentation de GuixSD se limite généralement à une documentation des fonctions Guile pour intéragir avec les différents sous modules. Très bien, écrire du code je sais faire. Mais ensuite, j’en fais quoi ? Où est ce que je dois mettre mes fichiers de configurations, où est ce que je dois les référencer ? C’est quoi les bonnes pratiques ? Mystère.
Il manque selon moi toute la documentation "utilisateur". Donner une collection de fonctions Scheme et leurs paramètres n’est pas suffisant si on ne sait pas quoi en faire.

Quand vous avez un problème, vous trouverez peut être quelqu’un ayant eu le même problème ou la même question sur les logs IRC de Guix, ou bien quelqu’un aura donné une solution sur un site web. Sinon, vous serez bloqué.

Ne pouvant pas me permettre de repasser mes soirées pendant encore 2 semaines à lire des historiques de logs IRC et le code source GuixSD pour essayer de comprendre quoi faire, j’ai donc abandonné et livre donc un article incomplet. Cela ne m’était jamais arrivé, même mon article sur eBPF avait été plus facile à écrire.

Quelques exemples de points de blocages.

Packages

Il y a peu de documentation sur comment packager une application. A force de chercher, j’ai réussi à packager un simple shell script que je place dans un dossier bin. A la base, je voulais coder une simple implémentation de cloud-init en shell et pouvoir configurer ma machine Guix de cette façon (clés SSH notamment) sur Exoscale. Je mets le code du package (qui a l’air de marcher) ici, ça servira peut être à quelqu’un:

(define-module (gnu packages exo-init)
  #:use-module (guix packages)
  #:use-module (guix download)
  #:use-module (guix build-system copy)
  #:use-module (guix licenses))

(define-public exo-init
  (package
   (name "exo-init")
   (version "0.1.0")
   (source (origin
            (method url-fetch)
            (uri "https://sos-ch-gva-2.exo.io/mcorbin.fr.iso/exo-init.tar.gz")
            (sha256
             (base32
              "0igqkaz02fwr58fhzzrh51vfql6xz5ywhp9j0s2y5i2lxfvd9s4g"))))
   (build-system copy-build-system)
   (arguments
    `(#:install-plan
      `(("exo-init.sh" "bin/"))))
   (synopsis "A simple Cloud-Init like for the Exoscale platform")
   (description
    "A simple Cloud-Init like for the Exoscale platform")
   (home-page "https://www.gnu.org/software/hello/")
   (license gpl3+)))

exo-init

Services

Prenons l’exemple des services. Vous trouverez dans la documentation de Guix ou de shepherd (le daemon gérant les services dans Guix) une liste de fonctions pour configurer un service, mais pas vraiment d’exemples. La section example contient FIXME: This needs a lot of work., et l’exemple donné à base de make <service> ne fonctionne pas, j’ai jamais réussi à trouver quel module importer pour le faire marcher.
Ce problème d’import se rencontre d’ailleurs partout dans GuixSD, les exemples dans la documentation sont généralement mis sans la liste des modules à importer, donc le code est de fait inutilisable.

Je voulais à la base définir un service pour le package exo-init définit précédemment, et pouvoir lancer ce script à chaque démarrage de la machine. Déjà, je n’ai jamais trouvé comment configurer un service lors de l’installation du package, j’ai juste réussi à écrire cela en m’inspirant de différentes sources trouvées à droite à gauche (dont le code source de Guix):

(define-module (mcorbin services exoinit)
  #:use-module (gnu)
  #:use-module (gnu services)
  #:use-module (gnu services base)
  #:use-module (gnu services shepherd)
  #:use-module (guix gexp)
  #:export (exo-init-service))

(define exo-init-service-type
  (shepherd-service-type
  'exo-init
  (lambda (name)
    (shepherd-service
    (documentation "Exo init service")
    (provision '(exo-init))
    (start #~(lambda _
               (make-forkexec-constructor
               '("exo-init"))))
    (one-shot? #t)))))

(define (exo-init-service) (service exo-init-service-type "name"))

A noter: je voulais au début ne passer aucun paramètre à exo-init-service-type mais ça n’a pas l’air possible, je passe donc un paramètre non utilisé.

Et même une fois le service écrit, j’en fais quoi ? Je dois l’installer manuellement pour chaque utilisateur ? Je dois le définir dans /etc/config.scm ? Je n’ai pas la réponse à ces questions.

Bugs

Le coup de grâce a été un bug que je rencontre actuellement sur un template GuixSD tout neuf. Toutes les commandes guix package finissent en:

guix package: error: error parsing derivation `/gnu/store/b9vig6s9f3a9c6gr8889pn3dp6gaq89a-guile-gdbm-ffi-20120209.fa1d5b6.tar.xz.drv': expected string `Derive(['

Un guix gc --delete /gnu/store/b09qkb8r06l64p809nysp67ihmvrib2f-guile-gdbm-ffi-20120209.fa1d5b6.drv permet de supprimer ce fichier, mais il revient lors d’une installation de package.

Donc en gros je ne peux plus rien faire sur la machine. Je me dis donc tiens, c’est le moment de tester les rollbacks !. Je lance donc guix package --list-generations (commande trouvée dans l’article partagé précemment), j’obtiens maintenant:

$guix package --list-generations
guix package: error: profile '/var/guix/profiles/per-user/guix/guix-profile' does not exist

(╯°□°)╯︵ ┻━┻

En conclusion

Les idées de GuixSD sont très bonnes, le choix de Scheme pour la configuration est super. Mais pour faire marcher le machin, faut s’accrocher. Et encore, je faisais mes tests sur un cloud, donc aucun problème de drivers ou autre, et pas de risque de flinguer ma machine.

Peut être que je referais une tentative dans quelques années ?

Tags: cloud linux
Top of page