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 :
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 branchenixos-22.05
nixpkgsUnstable
: Nixpkgs unstable, branchenixpkgs-unstable
nixpkgsMaster
: Nixpkgs le plus à jour possible, branchemaster
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 inputnixpkgs
docker-nixpkgs
: Un projet de la communauté (semi-officiel) exposant des images Docker construites avec Nix sous la forme de dérivations, disponible sur GitHubfutils
: Flake utils, comme mentionné sur la page précédenteflake-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.
À 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 :
- Des paquets custom, comme
Leodagan dans
pkgs/development/tools/leodagan/default.nix
ou le scriptclang-format-epita
danspkgs/development/tools/clang-format-epita/default.nix
- Des paquets tiers qui ne sont pas présents dans Nixpkgs, comme Clonezilla dans
pkgs/tools/backup/clonezilla/default.nix
ou l'API Wikipédia en Python danspkgs/development/python-modules/wikipedia/default.nix
- Des paquets modifiés, comme Geany qui a été tuné pour pouvoir compiler l'ASM
68k dans
pkgs/applications/editors/geany/default.nix
ou i3lock customisé pour utiliser notre fork à la place dans (rajoutant entre autre le système de limite de temps)pkgs/applications/window-managers/i3/lock.nix
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 dansmodules/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 dansmodules/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'utilisateurepita
. Si l'optioncri.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
Le paramètre --
indique de passer les arguments après lui au programme qu'on
lance (leodagan), et non pas à nix run
.
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
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
:
- La
bzImage
: le noyau Linux compressé - Le
initrd
: le système de fichier en RAM temporaire minimal d'initialisation - Le
squashfs
: l'image du Nix store du système au format SquashFS - Le
torrent
: le torrent pour télécharger lesquashfs
et le partager aux autres machines en Peer-to-Peer
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
).
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.