Le gestionnaire de paquets Nix en version 2.0

Posté par  . Édité par nokomprendo, Guillaum, Olivier HUMBERT, Bruno Michel, Davy Defaud, Yves Bourguignon, palm123, ZeroHeure, Nÿco et Benoît Sibaud. Modéré par ZeroHeure. Licence CC By‑SA.
Étiquettes :
48
3
avr.
2018
Gestion de versions

Nix est un gestionnaire de paquets fonctionnel dont la version 2.0 est sortie récemment. Parmi les nouveautés, on peut noter qu’une commande nix a été introduite pour uniformiser les différentes sous‐commandes (nix-build, nix-env, etc.) et avoir de meilleures options par défaut (pour la recherche de paquets notamment). Une bonne partie du reste du travail concerne des couches moins visibles mais néanmoins importantes : prise en charge de HTTP/2, amélioration de sécurité, etc.

Logo Nix

Nix peut être utilisé sur n’importe quelle distribution GNU/Linux en complément du gestionnaire de paquets habituel. Il existe également une distribution GNU/Linux articulée autour de Nix : NixOS. La version 18.03 de NixOS, prévue pour sortir très prochainement, inclura Nix 2.0.

Mise à jour : Nix 18.03 est sorti.

Sommaire

Qu’est‐ce que Nix ?

Nix est un gestionnaire de paquets. Contrairement à de nombreux autres gestionnaires, qui sont le plus souvent liés à une distribution particulière, celui‐ci peut être installé sur n’importe quelle distribution.

Nix permet de créer des environnements de développement reproductibles. Au lieu d’installer globalement une version particulière d’une bibliothèque, Nix l’installe dans un dossier spécifique. Ceci permet de faire cohabiter différentes versions d’une même bibliothèque et de définir des profils utilisateur ou par projet (c’est‐à‐dire des ensembles de liens vers les bibliothèques, aux versions voulues).

Quels sont les cas d’usages ?

Nix peut être utile à des utilisateurs non développeurs :

  • cela peut être un moyen efficace d’installer des paquets qui ne sont pas disponibles via le gestionnaire de paquets de sa distribution ;
  • cela permet de tester l’installation de programmes ou bibliothèques sans polluer son système ; Nix conserve même l’historique des installations et permet de revenir aux états antérieurs ;
  • Nix gère les paquets efficacement en calculant une somme de contrôle du paquet ; ainsi, quand deux utilisateurs installent une même version d’un paquet, c’est le même dossier d’installation qui est utilisé, ce partage est sans risque car le dossier est en lecture seule et ne sera jamais supprimé tant qu’un utilisateur y fait référence ;
  • ce n’est pas un système de conteneurs ni de machines virtuelles, les logiciels installés par Nix sont exécutés directement par le système.

Pour des développeurs, Nix est particulièrement avantageux :

  • c’est pratique pour les développeurs qui jonglent avec plusieurs projets, avec des dépendances dans différentes versions ; par exemple, on peut facilement ouvrir un shell avec Python 2 par défaut et un autre avec Python 3 par défaut ;
  • Nix permet de définir explicitement un environnement, par exemple pour un projet de développement ; pour cela, il suffit d’ajouter un fichier .nix et d’y indiquer les dépendances (bibliothèques nécessaires…) et le système de compilation à utiliser (autotools, cmake, ant, go…) ; on peut alors compiler et installer le projet ou lancer un shell contenant toutes les dépendances nécessaires ;
  • c’est un gain de temps important pour commencer à travailler sur un projet : il suffit de cloner le dépôt et de faire nix-shell pour obtenir un environnement de travail complet ;
  • l’environnement étant utilisé par les développeurs eux‐mêmes, il est donc constamment testé et à jour ; il peut également servir de point de départ pour construire le projet dans un autre contexte, par exemple pour en faire un paquet RPM, Deb, etc. ;
  • enfin, cela peut simplifier les scripts de construction : il n’y a plus besoin de rechercher les chemins vers les bonnes versions des différents outils, tout est pris en charge par Nix.

Quelles sont les faiblesses ?

  • Le gestionnaire de paquets Nix utilise son propre langage, également appelé Nix, qu’il faut donc apprendre si l’on veut créer ses propres paquets ;
  • l’aspect fonctionnel de Nix est très puissant mais peut être déroutant au début : on ne peut notamment pas aller bidouiller dans un dossier d’installation pour corriger un problème, il faut le faire en amont dans l’empaquetage du logiciel ;
  • Nix est conçu pour fonctionner dans son écosystème et non comme un outil pour distribuer un programme dans d’autres écosystèmes ; il existe cependant des approches comme nix-bundle ou dockerTools pour cela ;
  • OpenGL (et à peu près tous les outils qui dépendent d’un pilote) est complexe à utiliser ; en effet, on ne peut pas compiler un programme en incluant toutes les versions possibles du pilote OpenGL. Sous NixOS, le problème n’existe pas, mais les utilisateurs de Nix sur d’autres systèmes se retrouvent face à des programmes OpenGL qui ne tournent pas directement. Le problème se contourne avec des outils comme nixGL ou avec des petites bidouilles.

Nouveautés de Nix 2.0

Nouvelle commande nix

La nouvelle commande nix simplifie et uniformise certaines commandes nix-*, et devrait à terme toutes les remplacer :

  • aide en ligne avec nix --help ;
  • sortie écran moins verbeuse, apparition d’une barre de progression ;
  • possibilité de formater la sortie en JSON ;
  • nix build remplace nix-build (construit un paquet) ;
  • nix run remplace nix-shell -p (exécute une commande avec les dépendances spécifiées) et ne charge plus un nouveau shell ;
  • nix search remplace nix-env -qa (recherche de paquets) et utilise désormais un système de cache pour accélérer les recherches ;
  • etc.

Extension des types de store

  • stores locaux : LocalStore, LocalBinaryCacheStore
  • stores http, ssh ou amazon S3 : HttpBinaryCacheStore, SSHStore, S3BinaryCacheStore
  • etc.

Améliorations de sécurité

  • ajout de signatures pour les stores locaux ;
  • commande nix verify pour vérifier les paquets ;
  • ajout d’une vérification par signature lors des basculements sur des paquets binaires ;
  • etc.

Autres

  • prise en charge de HTTP/2 ;
  • nettoyage automatique lors des constructions s’il y a un manque d’espace disque ;
  • prise en charge des nombres flottants dans le langage Nix ;
  • nouvelles fonctions prédéfinies : builtins.fetchGit (récupère un dépôt Git à l’évaluation d’une expression nix), builtins.split (découpe un texte selon une expression rationnelle POSIX), etc. ;
  • suppression de Perl et de tous ses composants dans le code de base de nix ;
  • etc.

Exemple pour développer en C++ avec Nix

Code source

Nous allons démontrer l’utilisation de nix sur un programme C++ complet :

#include <experimental/filesystem>
#include <boost/process.hpp>
#include <iostream>
#include <OpenImageIO/imageio.h>

int main() {
    for(auto &&p: std::experimental::filesystem::directory_iterator(".")) {
        OIIO::ImageInput *im = OIIO::ImageInput::open (p.path().c_str());
        if(!im) continue;
        std::string s;
        std::cout << "Editer le fichier: " << p << " (o/N) ? ";
        std::cin >> s;
        if(s == "o") {
            boost::process::system(boost::process::search_path("gimp"), p.path().c_str());
            break;
        }
    }
}

Ce programme semble simple à première vue, il liste tous les fichiers d’un répertoire (grâce à directory_iterator) puis, pour chaque fichier, il tente d’ouvrir celui‐ci grâce à la bibliothèque OpenImageIO OIIO. Si l’ouverture est réussie, il propose à l’utilisateur d’éditer le fichier et si celui‐ci accepte, il sera ouvert dans GIMP grâce à un appel à la bibliothèque boost::process.

Gestion des dépendances

Le programme précédent a cependant des dépendances complexes :

  • une version d’un compilateur C++ suffisamment récent pour prendre en charge le C++11 et au moins std::experimental::filesystem ;
  • boost ;
  • openimageio.

Nous pouvons commencer par créer un shell contenant les dépendances nécessaires à notre travail :

$ nix-shell -p openimageio -p gcc -p openexr -p boost
[nix-shell] $ g++ nix_test.cpp -std=c++14 -lstdc++fs -lOpenImageIO -lboost_filesystem -lboost_system
[nix-shell] $ ./a.out
Editer le fichier: "./passport2016.jpg" (o/N) ? o
... lancement de gimp

Comme vous pouvez l’observer, nix-shell prend en charge le approvisionnement de l’environnement de développement et il n’est pas nécessaire de spécifier de répertoire de recherche de fichiers d’en‐tête (includes) -I ni de recherche de bibliothèque -L.

Environnement de développement et empaquetage

La création d’un shell est une solution rapide pour l’exécution d’une tâche ponctuelle, mais il est souvent pratique de passer par un fichier default.nix, qui indique les directives de compilation en plus des dépendances :

with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "linuxfr_depeche_nix_20";

  buildInputs = [openimageio gcc openexr boost];

  src = ./.;

  buildPhase = ''
    g++ nix_test.cpp -std=c++14 -lstdc++fs -lOpenImageIO -lboost_filesystem -lboost_system
    '';

  installPhase = ''
    mkdir -p $out/bin;
    cp a.out $out/bin/linuxfr_depeche_nix_2.0
    '';
  }

with import <nixpkgs> {} décrit la version de nixpkgs à utiliser, nous y reviendrons. La suite décrit le nom name du paquet, sa liste de dépendances buildInputs, le répertoire où sont les sources src, ainsi que les commandes à effectuer pour construire le paquet buildPhase et installer celui‐ci avec installPhase.

La commande nix build construit le paquet et le stocke dans ./result :

$ ls -R ./result
./result:
bin

./result/bin:
linuxfr_depeche_nix_2.0

Le binaire est exécutable directement par le biais de ./result bin/linuxfr_depeche_nix_2.0, mais peut aussi être installé :

$ nix-env -i ./result
installing 'linuxfr_depeche_nix_20'
building '/nix/store/45l04x4nqidap70skizmvvmi7p48dgmz-user-environment.drv'...
created 4407 symlinks in user environment

$ linuxfr_depeche_nix_2.0
Editer le fichier: "./passport2016.jpg" (o/N) ? n
Editer le fichier: "./IMG_20170521_174328.jpg" (o/N) ? o
... lancement de gimp

Personnalisation des dépendances

Supposons que nous soyons dans un cas de dépendances bien plus complexes, avec les contraintes suivantes :

  • seul gcc6 peut être utilisé ;
  • seul boost-1.63 peut être utilisé ;
  • openimageio dépend aussi de boost et malheureusement la dérivation (i.e. « paquet » nix) par défaut est compilée avec boost-1.66, ce qui pose un conflit avec boost-1.63.

Ce scénario peut sembler dingue, mais c’est malheureusement une constante souvent rencontrée dans le développement logiciel. Traditionnellement, ce genre de situation est réglé par la compilation à la main de toutes les dépendances, impliquant un processus long et fragile.

Nix permet de surcharger tout cela très facilement. Éditons notre fichier default.nix :

with import <nixpkgs> {};
stdenv.mkDerivation rec {
  name = "linuxfr_depeche_nix_20";

  openimageioWithBoost163 = openimageio.override {
    boost = boost163;
  };

  buildInputs = [openimageioWithBoost163 gcc6 openexr boost163];

  src = ./.;

  buildPhase = ''
    g++ nix_test.cpp -std=c++14 -lstdc++fs -lOpenImageIO -lboost_filesystem -lboost_system
    '';

  installPhase = ''
    mkdir -p $out/bin;
    cp a.out $out/bin/linuxfr_depeche_nix_2.0
    '';
  }

On note l’utilisation des paquets gcc6 et boost163, fournis de base par nix. Cependant, il faut surcharger openimageio pour changer sa version de boost. Le prochain appel à nix build se chargera de créer la nouvelle version d’openimageio compilée avec la bonne version de boost.

On note toutefois que ce processus de compilation ne sera effectué qu’une seule fois, le résultat étant stocké dans le cache local de nix.

Reproductibilité parfaite en bloquant la version

Par défaut, notre fichier default.nix utilise with import <nixpkgs> {}, c’est‐à‐dire la définition de paquets actuellement configurés sur le système. Cette approche est souple mais fragile car, en cas de mise à jour de cette liste, notre projet peut ne plus fonctionner.

Il est toutefois possible de fixer cette version en se limitant à un commit précis du dépôt GitHub nixpkgs, par exemple avec :

with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/1bc5bf4beb759e563ffc7a8a3067f10a00b45a7d.tar.gz") {};

Cette approche garantit une construction parfaitement reproductible. Couplée à la précision sur les versions des dépendances, nix permet d’obtenir un processus de construction de paquet très robuste. Il est maintenant possible de construire n’importe quel projet de façon robuste en deux commandes :

$ (git|hg|svn|darcs|pujil|cvs) clone
$ nix build

Autres fonctionnalités

L’exemple précédent montre l’intérêt de Nix pour développer un projet C++. Nix apporte également de nombreuses fonctionnalités pour développer dans d’autres langages et même pour utiliser plusieurs langages dans un même projet.

Enfin, Nix apporte également d’autres fonctionnalités comme la gestion de services (nixos), la création et le déploiement d’environnements logiciels légers (nixops), etc.

Aller plus loin

  • # petite coquille

    Posté par  (site web personnel) . Évalué à 2.

    La prochaine version de Nixos est la 18.03 et non la 18.04 (même s'ils ne sont pas en avance pour la sortir…).
    Et sinon, le logo n'est pas un peu gros ?

  • # voir aussi… GNU Guix

    Posté par  . Évalué à 7.

    Merci pour la dépêche !

    Faut vraiment que je m'y mette. J'invite qui veut à comparer avec Docker.

    Petite mention de GNU Guix quand même: https://www.gnu.org/software/guix/ + GuixSD, issu des idées de Nix, donc même objectif mais tout avec un vrai language de programmation, en l'occurence Guile Scheme. Peut être que Nix est plus simple à installer.

    • [^] # Docker [was: voir aussi… GNU Guix]

      Posté par  (site web personnel) . Évalué à 4.

      Même si dans l'esprit ça part de la même logique (déclaratif, tout ça tout ça), à mon sens en terme d'usage ce n'est pas vraiment comparable avec Docker car ça ne fait pas de container : on reste sur une seule machine, un seul système…

    • [^] # Re: voir aussi… GNU Guix

      Posté par  (site web personnel) . Évalué à 3.

      Pour moi les deux sont totalement indépendant. Docker ne gere pas les programmes / librairies installées. Il n'y a rien de choquant d'avoir un nix dans NixOS (ou n'importe quelle autre distrib) dans un docker.

      Par contre, pour le dev tous les jours, nix est suffisant pour ne pas avoir besoin de docker.

  • # Nix et NixOS sont top

    Posté par  . Évalué à 3.

    Super article. Je recommande chaudement Nix et NixOS (ou GuixSD que je n'ai pas essayé mais qui utilise Nix). GNU/Linux Mag leur avait consacré un article dans le numéro 203.

  • # Quid des mises à jour de sécurité ?

    Posté par  . Évalué à 5.

    Je trouve l'idée de Nix absolument géniale.

    Quand on parle des alternatives existantes (ce que Nix vise à palier), on nous vante à raison la facilité avec laquelle on peut faire les mises à jour de sécurité: on met à jour la librairie partagée et hop c'est sécurisé. D'après la description du fonctionnement, on dirait qu'il faut mettre à jour toutes les dépendances une par une, comme avec la compilation statique.

    Est-ce que c'est la seule manière de faire, ou bien est-ce qu'il existe une sorte d'override global?

    • [^] # Re: Quid des mises à jour de sécurité ?

      Posté par  (site web personnel) . Évalué à 9.

      Je suis loin d'être un expert Nix mais, de ce que j'en ai compris, les mainteneurs n'ont pas besoin de déclarer explicitement la mise à jour sur tous les paquets qui dépendent de la bibliothèque à mettre à jour. Et les utilisateurs peuvent mettre à jour tous les paquets avec un nix-channel --update && nix-env -u (l'équivalent d'un apt update && apt upgrade). Ça va reconstruire tous les paquets qui dépendent de la bibliothèque mise à jour (sauf si le paquet avait indiqué une version particulière de la bibliothèque).

      Pour les utilisateurs de NixOS, il peut y avoir beaucoup de paquets et ça risque d'être long. Les développeurs ont mis en place un truc pour contourner ça : https://theshortlog.com/2016/10/01/Nix-security-updates-update/

  • # Et par utilisateur

    Posté par  (site web personnel) . Évalué à 3.

    Un des avantages aussi, c'est que chaque utilisateur peut installer les paquets qu'il a besoin, les mettre à jour sans impacter les collègues
    L'inconvénient, c'est évidemment la place sur le disque dur

    • [^] # Re: Et par utilisateur

      Posté par  . Évalué à 3.

      Mais du coup, ça doit être possible d'avoir un outil qui vérifie périodiquement quels outils sont majoritairement installés et le signale à l'administrateur du système, qui pourrait lui, si il considère l'outil fiable, l'installer sur le système?
      Autre piste: les systèmes de fichiers avec déduplication?

      • [^] # Re: Et par utilisateur

        Posté par  (site web personnel) . Évalué à 3.

        En fait l'installation est partagée entre chaque utilisateur ET nix gère la déduplication entre fichiers du store. C'est pratique si par exemple tu as plusieurs version d'un même programme.

        • [^] # Re: Et par utilisateur

          Posté par  . Évalué à 3.

          Hum… donc, tous les utilisateurs ont la possibilité d'écrire dans un dossier système, d'altérer le comportement du système pour les autres utilisateurs?

          • [^] # Re: Et par utilisateur

            Posté par  (site web personnel) . Évalué à 5.

            Non, ils ont juste le droit de lancer des commandes nix via sudo. Et nix fait en sorte que les commandes d'un utilisateur n'ait pas d'effets de bord pour les autres utilisateurs.

            • [^] # Re: Et par utilisateur

              Posté par  . Évalué à 3.

              D'accord, donc il y a un mécanisme de base de données derrière qui lie les utilisateurs à leurs programmes. À la base je pensais que ça installait juste dans le dossier utilisateur, par exemple dans $HOME/.local/bin (ce qui aurait évité de passer par sudo, pour le coup).

              • [^] # Re: Et par utilisateur

                Posté par  (site web personnel) . Évalué à 4.

                Justement non. Les utilisateurs peuvent installer sans sudo. Tout est stocké (sans duplication) dans le /nix/store en lecture seule donc pas de conflit possible. Le seul danger est qu'un utilisateur peut remplir le disque en installant plein de paquets différents.

            • [^] # Re: Et par utilisateur

              Posté par  (site web personnel) . Évalué à 3.

              Techniquement les commandes nix communiquent avec le "store" par le biais d'un daemon. Le demon est le seul à pouvoir écrire dans la store. Il est par contre tout à fait possible de lui faire écrire ce que l'on veut mais sans pouvoir écraser un autre répertoire. Par contre il est vrai que tu peux mettre un truc dangereux dans le store et demander à un utilisateur de l'exécuter. Mais c'est la même chose que lui demander d'exécuter un ficher reçu par mail…

  • # Commande nix

    Posté par  (site web personnel) . Évalué à 4.

    Le changelog annonce une commande nix unifiée ce qui semble une bonne idée, et en dessous aucun exemple de la dépêche ne s’en sert.

    Est-ce parce que ces choses là ne sont pas encore faisables avec la commande nix ou c’est une coquille ?

  • # Merci

    Posté par  . Évalué à 1.

    Super, merci pour la dépêche

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.