Skip to main content

NixPIE

Maintenant que vous avez été introduit à toutes les notions nécessaires, il est temps d'aborder la raison de l'existence de ce cours : NixPIE.

NixPIE est un flake disponible sur un dépôt public du Gitlab du CRI, contenant l'ensemble des définitions Nix nécessaires au fonctionnement du PIE.

C'est à partir de ce dépôt (et plus précisément avec sa CI) que sont générées les images qu'il est possible de boot sur les machines de l'école. Mais il ne sert pas qu'à ça, il est aussi possible de l'importer en tant qu'input pour l'utiliser dans des projets liés à l'école.

Pour commencer, passons en revue la définition du flake :

Conseil

Pour avoir une vue d'ensemble du flake, je vous conseille de cloner le dépôt et de l'ouvrir dans l'éditeur/IDE de votre choix (il existe notamment des plugins Nix pour VSCode, Vim, IntelliJ, Emacs, etc.).

Les inputs

À l'heure actuelle, NixPIE importe 7 inputs :

  • nixpkgs : Nixpkgs stable, actuellement sur la branche nixos-22.05
  • nixpkgsUnstable : Nixpkgs unstable, branche nixpkgs-unstable
  • nixpkgsMaster : Nixpkgs le plus à jour possible, branche master
  • machine-state : Un programme maison servant à tracker l'état d'une machine, hébergé lui aussi sur un dépôt public du Gitlab du CRI et configuré pour suivre notre input nixpkgs
  • docker-nixpkgs : Un projet de la communauté (semi-officiel) exposant des images Docker construites avec Nix sous la forme de dérivations, disponible sur GitHub
  • futils : Flake utils, comme mentionné sur la page précédente
  • flake-compat : Flake compat, comme mentionné aussi sur la page précédente

Grâce à flake-compat, NixPIE exporte en plus d'une flake des fichiers shell.nix et defaut.nix facilitant son utilisation avec Nix classique.

Les outputs

Les checks

NixPIE exporte un certain nombre de tests permettant d'assurer le bon fonctionnement de certaines des parties les plus importantes, comme le fait de pouvoir se connecter ou que la toolchain fonctionne bien.

Les tests sont disponibles dans le dossier tests du dépôt, chacun dans un fichier contenant leur définition, et ils sont ensuite enregistrés et créés dans le fichier default.nix qui exporte la liste de dérivation créée.

À l'heure actuelle, c'est l'ancien système de test qui est utilisé (car le nouveau n'existait pas encore à l'époque), customisé pour exporter du XML. Les modules utilisés et les special args passés aux tests (contenant notamment les inputs) sont importés depuis le dossier images dont on reparlera plus bas.

Les tests sont automatiquement lancés par la CI.

Attention

À l'heure actuelle, le test login-epita est cassé.

Les packages

NixPIE exporte beaucoup de packages, définis dans le dossier pkgs du dépôt, auxquels s'ajoutent les versions Docker des images définies dans le dossier images (encore une fois, on en parlera en détail plus bas).

Y sont définis, entre autre :

Ces paquets sont utilisés notamment dans diverses images, mais peuvent aussi être importés lorsque NixPIE est utilisé comme input ou installés via la CLI.

Dans le dossier pkgs, le fichier default.nix expose les paquets listés dans le fichier top-level/all-packages.nix filtrés pour ne garder que ceux disponibles sur le system donné en paramètre.

Il existe aussi un fichier overrides.nix qui est un overlay ajouté au Nixpkgs utilisé notamment dans les modules. À l'intérieur, pkgsUnstable et pkgsMaster sont disponibles pour y récupérer des packages.

Les applications

NixPIE exporte également une liste d'applications, notamment certaines qu'il a besoin de pouvoir lancer (dans la CI par exemple) alors que Nix n'a pas réussi à déduire le chemin de leur binaire, comme la CLI AWS ou nixpkgs-fmt.

En plus de ça, des applications list-checks, list-pkgs, etc. sont aussi définies, correspondant à des scripts permettant de lister certains types d'outputs. Ces scripts sont utilisés dans la CI pour savoir pouvoir générer un job pour chacun des tests, paquets, images, etc., mais aussi pour pouvoir les comparer et donc savoir quand il est nécessaire les rebuild, permettant d'alléger la CI dans le cas d'une MR.

Les overlays

NixPIE expose un overlay pour chaque paquet qu'il exporte, permettant de les utiliser sous la forme d'overlay ajoutable à Nixpkgs. Ces derniers sont disponibles dans les outputs overlays.nom-du-package.

Les modules NixOS

Une partie très importante de NixPIE est constituée de modules NixOS. Ces derniers servent de base à la création d'images, et contiennent beaucoup de logique commune à ces derniers.

La plupart d'entre eux définissent des options, par exemple :

  • cri.afs.enable, définie dans modules/services/network-filesystems/openafs/client.nix - lorsque défini à true, le module va mettre en place et configurer le client OpenAFS.
  • cri.users.enable, définie dans modules/config/users-groups.nix - lorsque défini à true, le module va mettre en place le système d'authentification via CRI, et créer l'utilisateur epita. Si l'option cri.afs.enable est à true, il mettra en place l'authentification pour l'AFS.

Pleins d'autres options en cri.* permettent aux images de sélectionner facilement les fonctionnalités dont elles ont besoin.

Dans le dossier modules, le fichier default.nix exporte un set contenant chacun d'entre eux, et le fichier nixpie.nix est un module important tous les autres (disponible aussi dans la liste exportée par default.nix).

Il existe aussi un dossier profiles contenant des modules NixOS correspondant aux profils types d'image. Par exemple, profiles/graphical/default.nix est utilisé pour les images avec une interface graphique : il active X.org, i3, le son, les drivers vidéos, etc.

Le profil profiles/core/default.nix est un peu spécial, car il est automatiquement inclus dans toutes les configurations NixOS de NixPIE. C'est lui qui contient la base : la mise en place du réseau, les programmes utilisés pour boot, etc.

La liste des modules exportée par le fichier default.nix est disponible dans l'output nixosModules.

Les configurations NixOS

Évidemment, NixPIE expose des configurations NixOS correspondant à chacune des images du PIE.

Chacune des images est définie dans un fichier du dossier images, important les profils dont elle a besoin et activant les options nécessaires. Le fichier module.nix exporte des modules communs, notamment le profil core et un module global définissant encore des options globales comme la configuration de Nix et Nixpkgs.

Le fichier default.nix exporte un set contenant chacune des configurations qu'il aura initialisée proprement en incluant les modules exposés par modules.nix et ceux de l'output nixosModules (où sont enlevés les profils et le module nixpie).

Ce set est disponible dans l'output nixosConfigurations.

Les dev shells

NixPIE expose un dev shell par défaut utilisé par la CI, contenant les outils nécessaires à cette dernière (nixpkgs-fmt, nix-diff, awscli, etc.).

Les outputs non-standard

En plus du reste, NixPIE expose un output lib contenant des fonctions utilitaires définies dans le dossier du même nom, comme une fonction permettant de générer une dérivation pour la création d'un SquashFS.

Utiliser NixPIE

Avec la CLI

Il est facile d'utiliser NixPIE via la nouvelle CLI, par exemple pour utiliser leodagan :

nix run git+https://gitlab.cri.epita.fr/cri/infrastructure/nixpie.git#leodagan -- --help
Info

Le paramètre -- indique de passer les arguments après lui au programme qu'on lance (leodagan), et non pas à nix run.

Conseil

La commande est un peu longue, mais on peut la raccourcir en passant par le miroir GitHub de NixPIE :

nix run github:epita/nixpie#leodagan -- --help

On aurait aussi pu ajouter le flake à notre registre pour que ça soit encore plus simple !

Comme input dans un autre flake

Évidemment, il est possible d'utiliser NixPIE comme input dans un autre flake. Exemple d'un flake exposant un dev shell sur Linux x86_64 avec les nswrappers servant à tester myvswitch :

{
inputs = {
nixpie.url = "github:epita/nixpie";
nixpkgs.follows = "nixpie/nixpkgs"; # On utilise le même Nixpkgs qu'à l'école !
};

outputs = { self, nixpie, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShell.${system} = pkgs.mkShell {
buildInputs = with nixpie.packages.${system}; [ nswrappers ];
};
};
}

Pour construire une image

Dans le cadre du fonctionnement des salles machines, NixPIE est surtout utilisé pour générer les images qui pourront être boot dans ces dernières. Pour ce faire, il suffit de build la dérivation exposée par la valeur de l'option system.build.toplevel, qui est la dérivation racine d'un système NixOS.

Par exemple, pour build l'image NixOS PIE :

nix build github:epita/nixpie#nixosConfigurations.nixos-pie.config.system.build.toplevel
Info

Vous pouvez tester cette commande sur Linux, elle devrait télécharger environ 3.5Go et prendre 50Go de place (que vous pourrez libérer à l'aide de nix-collect-garbage).

Dans le dossier result, la configuration construite (contenu du dossier /run/current-system du futur système) devrait être disponible.

Il est aussi possible de build la version pour démarrage réseau (dite Netboot) de cette image, en utilisant la dérivation personnalisée toplevel-netboot :

nix build github:epita/nixpie#nixosConfigurations.nixos-pie.config.system.build.toplevel-netboot

C'est cette dérivation qui est utilisée pour produire le nécessaire pour boot l'image en salle machine. Il devrait y avoir dans le dossier result :

Comment la sortie de NixPIE est utilisé lors du démarrage

Pour faire un bref résumé de comment c'est utilisé, avant que cela vous soit détaillé lors de la prochaine semaine de cours : lors du démarrage d'un PC en salle machine, iPXE téléchargera le bzImage et le initrd depuis le serveur PXE, et lancera le noyau Linux avec le système de fichier d'initialisation.

Ces derniers démarreront la base de NixOS et lanceront notamment les commandes définies dans l'option boot.initrd.postDeviceCommands, au niveau du fichier modules/system/boot/netboot.nix, qui vont télécharger (dans le cache de boot pour pouvoir le réutiliser, ou sinon la RAM) le squashfs à l'aide du torrent, dans le dossier /srv/torrent (valeur par défaut de l'option netboot.torrent.mountPoint).

Ensuite, NixOS montera les systèmes de fichiers listés dans l'option fileSystems :

  • / : un tmpfs, c'est-à-dire un système de fichier temporaire dans la RAM, qui sera donc vide à chaque démarrage
  • /nix/store : le squashfs monté
  • /srv/torrent : le cache de boot où sera téléchargé le torrent (si la partition de cache n'existe pas, ça sera aussi un tmpfs)
  • /home : Sur les VM, c'est une partition physique du disque accueillant les données des utilisateurs, permettant leur persistence. En salle machine, la partition n'existe pas et comme il est indiqué que son montage peut ne pas réussir, NixOS continuera son démarrage malgré tout.

Le démarrage normal de NixOS reprendra ensuite, avec au milieu le script post-boot qui enregistrera le contenu du Nix Store, créera /etc/NIXOS (nécessaire à NixOS) et utilisera nix-env pour activer la génération du système dans /run/current-system (oui, on peut l'utiliser comme ça ! C'est ce qui est appelé à la fin d'un nixos-rebuild).

Pour aller plus loin

Le Nix Store sur le PIE est en vérité un OverlayFS, un système de fichier constitué de plusieurs couches :

  • Une couche de lecture seule, correspondant au squashfs
  • Une couche de lecture/écriture, optionnelle, située sur une partition physique du disque

Le squashfs est monté sur /nix/.ro-store, et la partition physique sur /nix/.rw-store. À partir de ces couches, un OverlayFS est monté sur /nix/store.

L'utilisation d'un OverlayFS permet d'assurer que le store initial reste intact, tout en permettant l'ajout de nouvelles entrées à l'intérieur (si l'option netboot.nix-store-rw.enable est activée).

Mais attention, même si la couche de lecture/écriture du Nix Store est sur une partition physique, cela ne veut pas dire qu'elle est persistante. Elle a été mise sur le disque, car elle aurait pris trop de place dans la RAM avec un tmpfs. Mais la couche du Nix Store est formatée à chaque démarrage par le script de l'initrd.

Étant assez petite et vite remplie, il n'est pas rare de rencontrer une erreur No space left on device lors de l'installation de paquets en SM. Un redémarrage suffit donc à régler le problème.

La CI de NixPIE

NixPIE dispose d'une CI Gitlab décrite dans le fichier .gitlab-ci.yml et le dossier .gitlab/ci.

D'abord, les scripts generate-checks-pipeline.sh, generate-images-pipeline.sh et generate-packages-pipeline.sh sont lancés pour générer les pipelines servant respectivement à lancer les tests, générer les images, et générer les paquets. Pour les paquets, si la CI est lancée sur une MR, un système de diff génèrera uniquement les pipelines pour les paquets qui ont changé.

Ensuite, les tâches suivantes sont lancées :

  • Pour les checks, chacun sont lancés un par un.
  • Pour les paquets, chacun d'entre eux sont build et le contenu du store qui leur est lié est envoyé sur le S3 du cache Nix du CRI.
  • Pour les images, le toplevel de chacune d'entre elle est d'abord build, et le contenu du store qui y est lié est envoyé sur le cache. Ensuite, le toplevel Netboot est build et le résultat est envoyé sur le S3 du PXE.
  • Toujours pour les images, leur version Docker est build et envoyée sur le registry du CRI. À l'heure actuelle, ces tâches doivent être lancées manuellement dans la pipeline et ne sont pas nécessaires à son succès.

Le cache Nix du CRI ?

Il serait peu pratique de devoir compiler les paquets custom de NixPIE qui ne sont pas déjà installés sur le PIE à chaque fois que l'on veut les utiliser. Pour palier à ça, ainsi qu'améliorer les performances en proposant un cache local pour les machines, le PIE dispose de son propre cache Nix sur le S3 contenant toutes les dérivations (ainsi que celles dont elles dépendent) des images et des paquets de NixPIE.

Conclusion

C'est tout ! NixPIE n'est pas un projet si compliqué quand on enlève les notions vues précédemment.

En plus de faire tourner le PIE presque à lui tout seul, NixPIE peut être importé chez soi pour utiliser exactement les mêmes paquets qu'à l'école, et ça même sur macOS en partie !

Cette conclusion marque donc la fin de cette semaine de cours qui, je l'espère, n'aura pas été trop longue et ennuyeuse. Nous sommes évidemment disponibles pour répondre à vos questions, et on espère que vous vous sentirez plus à l'aise sur Nix désormais.