1 points par GN⁺ 4 시간 전 | 1 commentaires | Partager sur WhatsApp
  • NixOS permet de créer facilement des VM ou des ISO à partir de la configuration, mais même une image live presque minimale est générée à 458 MiB au départ, soit bien plus qu’une ISO de VM Alpine d’environ 66 MiB
  • L’essentiel de la taille provenait de nix-store.squashfs, qui contenait Python 3.13.13, les modules Linux, systemd, Perl, GRUB, la documentation et des dépendances liées à Nix
  • En passant par nix.enable = false, documentation.enable = false, puis la suppression de register-nix-paths, l’ISO est passée de 458 MiB → 384 MiB → 360 MiB, et la dépendance à Boost a aussi disparu
  • En retirant en plus le client OpenSSH, les paquets par défaut, les outils d’installation de GRUB, les modules du noyau chargés à l’exécution et le chemin d’activation basé sur Perl, la taille finale est descendue à 183 MiB
  • Cela peut servir de référence pour une petite image de démarrage expérimentale, mais comme beaucoup de fonctions nécessaires sont supprimées, il est difficile de l’utiliser telle quelle pour un poste de travail ou un environnement important

Créer une ISO à partir d’une configuration NixOS

  • NixOS permet de créer facilement une VM à partir d’une configuration
    • nixos-rebuild build-vm génère une VM à partir de la configuration système actuelle
    • avec pkgs.nixos, on peut aussi créer une VM depuis une configuration arbitraire, même si ce n’est pas la configuration du système
  • L’exemple de base crée une VM minimale avec seulement system.stateVersion = "26.05" et services.getty.autologinUser = "root"
  • Cette VM fonctionne comme une thin VM
    • l’image disque ne contient que les fichiers créés directement dans la VM
    • le reste, comme /nix/store, est monté depuis l’OS hôte
  • Si l’hôte n’a pas Nix, ou s’il faut exécuter l’image sur un hôte distant ou un hyperviseur classique, il faut une ISO autonome
  • On peut construire une ISO en important le module iso-image.nix de NixOS
    • image.baseName = lib.mkForce "nixos" permet de définir le nom du fichier ISO produit
    • exemple d’exécution : qemu-system-x86_64 --cdrom .../nixos.iso -m 1G --accel kvm
    • hors environnement Linux moderne amd64, il peut être nécessaire d’adapter l’architecture ou le mode d’accélération

Point de départ : une ISO de 458 MiB

  • Le résultat de la construction de l’ISO de base faisait 458 MiB
  • Cette image ne contenait même pas encore vim
    • après démarrage, lancer vim renvoyait command not found
  • À titre de comparaison, l’ISO VM d’Alpine mentionnée fait environ 66 MiB
  • Damn Small Linux est cité comme exemple d’une distribution ayant proposé un environnement de bureau abouti avec une taille bien plus réduite
  • L’objectif n’est pas d’atteindre le niveau de Damn Small Linux, mais de voir jusqu’où il est possible de réduire une ISO NixOS

Analyse de la taille à l’intérieur de l’ISO

  • Après montage de l’ISO et vérification avec du, la taille se répartissait ainsi
    • nix-store.squashfs : 416 MiB
    • initrd : 26 MiB
    • noyau : 13 MiB
    • ISO complète : 458 MiB
  • Le principal facteur de taille était donc nix-store.squashfs, qui constitue l’essentiel de l’espace utilisateur
  • Une fois le squashfs monté, on y trouvait des chemins ressemblant à ceux du Nix store
    • python3-3.13.13 : 128 MiB
    • linux-6.18.35-modules : 144 MiB
    • systemd-260.1 : 60 MiB
    • perl-5.42.0 : 56 MiB
    • grub-2.12 : environ 62 MiB au total sur plusieurs éléments
    • de la documentation comme nix-manual-2.34.7 et nixos-manual-html était aussi incluse
  • Comme l’ISO est construite depuis l’hôte, les chemins du store présents dans l’ISO peuvent aussi être retracés dans le /nix/store de l’hôte
  • nix why-depends a été utilisé pour identifier l’origine des dépendances
    • Boost arrivait via le chemin du démon Nix
    • en passant par nix-daemon.conf, nix, puis libnixutil.so, on atteignait boost-1.89.0

Retirer Nix et la documentation

  • Une première tentative a consisté à enlever Nix de l’image avec nix.enable = false
  • La documentation a aussi été désactivée avec documentation.enable = false
  • Le premier résultat a été 458 MiB → 384 MiB
  • Mais Boost était toujours présent
    • register-nix-paths.service essayait d’enregistrer au démarrage le contenu du store de l’ISO
    • ce chemin réintroduisait Nix et Boost
  • Le service a été vidé puis supprimé avec systemd.services.register-nix-paths = lib.mkForce {}
  • L’ISO est alors descendue à 360 MiB, et nix why-depends a confirmé l’absence de dépendance à Boost

Suppression d’OpenSSH et des paquets par défaut

  • De la même manière, il a aussi été possible de vider environment.defaultPackages
  • La suppression de ssh a été plus délicate
    • modules/programs/ssh.nix ajoute OpenSSH à environment.corePackages
    • il n’a pas été possible de trouver une option comme programs.ssh.enable pour le contrôler
    • services.openssh.enable concerne la configuration du serveur, pas la suppression du client
  • Il était possible d’exclure programs/ssh.nix avec disabledModules, mais d’autres modules supposaient ensuite l’existence des options programs.ssh, ce qui provoquait des erreurs en cascade
  • La solution a été de fournir un stub d’option dans un module séparé, sans utiliser les options programs.ssh
    • options.programs.ssh = lib.mkOption {};
    • puis exclusion du vrai module SSH avec disabledModules = [ "programs/ssh.nix" ];
  • Dans ce processus, les réglages suivants ont aussi été appliqués
    • documentation.man.enable = false
    • networking.firewall.enable = false
    • environment.defaultPackages = lib.mkForce []

Notes sur la structure des modules NixOS

  • Un module NixOS comporte globalement trois parties
    • les éléments au niveau du module : imports, disabledModules
    • la définition des options : options.*
    • l’implémentation : config.*
  • Les modules qui ne définissent pas d’options peuvent utiliser une forme abrégée avec des propriétés d’implémentation écrites sans préfixe config.
  • Pour conserver cette forme abrégée dans le reste de la configuration, le stub d’option programs.ssh a été placé dans un module importé séparément

Retirer les outils d’installation de GRUB

  • Parmi les gros éléments restants figuraient environ 62 MiB de fichiers liés à GRUB
  • Le chargeur d’amorçage lui-même reste nécessaire, mais il n’était pas jugé utile d’inclure tous les outils d’installation
  • Le preset ISO de NixOS embarque à la fois les versions UEFI et BIOS de GRUB
  • Faute d’option claire pour les désactiver, une méthode plus brutale a été utilisée en réinitialisant les valeurs suivantes
    • system.extraDependencies = lib.mkForce []
    • environment.systemPackages = lib.mkForce config.environment.corePackages
  • environment.systemPackages n’a pas été vidé complètement
    • sans bash, getty peut entrer dans une boucle de crash, donc corePackages a été conservé pour garder un shell minimalement fonctionnel

Suppression des modules du noyau

  • linux-6.18.35-modules pesait 144 MiB, soit environ un quart de la taille totale
  • NixOS ne semblait pas offrir de bon point d’accroche pour limiter les modules du noyau utilisés à l’exécution
  • À la place, le dossier kernel-modules a été supprimé de la sortie système
    • system.systemBuilderCommands = lib.mkAfter "rm $out/kernel-modules";
  • Cette méthode désactive en pratique le chargement dynamique des modules à l’exécution
    • les modules nécessaires doivent être placés dans boot.initrd.kernelModules ou availableKernelModules
  • Après ce changement, il n’était plus possible de passer à une résolution d’affichage plus confortable, mais le démarrage restait possible
  • La taille de l’ISO est alors tombée à 197 MiB

Retirer Perl et recourir à des fonctions de remplacement expérimentales

  • Il restait encore 56 MiB de Perl
  • Avec nix why-depends, il a été constaté que Perl était utilisé pendant l’activation du système pour configurer les utilisateurs et /etc
  • Il n’était pas possible d’abandonner complètement la gestion des utilisateurs et de /etc
  • À la place, un chemin alternatif expérimental a été utilisé
    • gestion de /etc via un mécanisme d’overlay
    • gestion des utilisateurs via userborn en natif
  • La configuration appliquée était la suivante
    • system.etc.overlay.enable = true
    • system.etc.overlay.mutable = false
    • services.userborn.enable = true
  • La taille finale de l’ISO est ainsi descendue à 183 MiB

Résultat final et limites

  • Le point de départ de 458 MiB a été ramené à 183 MiB, soit presque un tiers de la taille initiale
  • Malgré cela, le résultat n’est pas considéré comme véritablement satisfaisant
  • Il ne convient pas à un poste de travail réel ni à un environnement important
    • toutes les fonctions supprimées avaient une raison d’être
  • En revanche, cela peut servir de référence si l’on a besoin d’une petite image de démarrage expérimentale ne réalisant que des tâches très limitées
  • Copier tel quel la configuration finale peut laisser de côté des fonctions nécessaires à l’usage visé

Pistes pour réduire encore

  • Ce travail s’est concentré sur des éléments « simplement supprimables » ou pour lesquels il existait un remplacement relativement clair
  • Des zones demandant un travail plus profond restent ouvertes
    • actuellement, systemdMinimal et systemd sont tous deux embarqués
    • essayer d’en retirer un cassait d’autres chemins de build
  • D’autres petits éléments peuvent encore être supprimés, et leur somme pourrait devenir significative
  • Une optimisation supplémentaire demandera davantage d’enquête et d’expérimentation

1 commentaires

 
GN⁺ 4 시간 전
Avis sur Lobste.rs
  • Il existe un module conçu exactement pour ce genre d’usage. Il demande pas mal de compilation, mais il permet de produire un initrd entièrement autonome, incluant tout l’espace utilisateur de NixOS, pour environ 80 MiB une fois compressé avec zstd
    Ce travail ne se limite pas aux initrd autonomes et peut servir à réduire la taille de n’importe quel système NixOS. Cela devrait probablement aussi s’appliquer à l’ISO d’installation
    https://github.com/wucke13/minimal-nixos/

  • Le système de base de TinyCore Linux est Core, à 17 MB
    Si vous voulez aussi X et FLTK/FLWM, il y a TinyCore à 23 MB, et si vous voulez davantage de gestionnaires de fenêtres et d’applications, il y a CorePlus à 248 MB
    http://www.tinycorelinux.net/downloads.html

    • Je ne vois pas bien le rapport avec une configuration déclarative ou des VM reproductibles
  • Je recommande cette présentation de la NixCon sur une réduction de NixOS pour en faire une alternative à Yocto : https://youtu.be/AsXY61laNb8
    Ce n’était pas aussi détaillé que je l’espérais, mais ce que j’ai entendu directement d’Óli et Matthew à la conférence était impressionnant. Je me demande s’il existe un billet récapitulatif

  • J’ai toujours trouvé un peu frustrant de créer une petite empreinte d’installation sur NixOS
    On peut probablement réduire la partie SSH avec la configuration suivante

    programs.ssh.setXAuthLocation = false;  
    security.pam.services.su.forwardXAuth = lib.mkForce false;  
    fonts.fontconfig.enable = false;  
    

    On peut aussi importer "${nixpkgs}/nixos/modules/profiles/minimal.nix". Il contient une partie des optimisations mentionnées dans l’article

    • Dans le cas d’usage qui a motivé l’article, ssh lui-même n’était en réalité presque pas nécessaire
      Cela dit, dans la plupart des cas, cette approche a sans doute plus de sens
      "${nixpkgs}/nixos/modules/profiles/minimal.nix", je l’avais déjà vu auparavant et je l’avais trouvé moins convaincant que prévu, donc je n’ai pas pensé à l’inclure au début de mon enquête. Quand j’y ai repensé plus tard, j’étais déjà arrivé à mi-parcours, et l’ajouter à ce stade, alors qu’il aurait dû figurer dès le début, m’a semblé un peu malhonnête
  • C’est étrange de voir à quel point Perl est souvent embarqué dans les systèmes de nos jours. Même pour une petite ISO, on se retrouve avec Perl, et si on essaie de compiler quelque chose de sérieux depuis zéro, on tombe sur openssl -> Perl

  • Avant même de lire, je me suis dit que ce serait sûrement à cause d’un script Perl idiot que personne n’a réécrit en C
    Édit : c’était bien ça

  • À partir de la version 26.05, NixOS utilise systemd dans l’initrd par défaut. En raison du grand nombre de cas d’usage de l’initrd qu’un système d’exploitation moderne doit prendre en charge
    systemdMinimal est un binaire systemd compilé avec moins de drapeaux et de dépendances, ce qui aide à garder un initrd plus petit
    Cela dit, si l’objectif est une ISO minimale, il semble possible de faire en sorte que les deux dépendent du même binaire