Post

Une introduction à Nix

Une introduction à Nix

⚠️ Bien que ce soit un outil que j’utilise quotidiennement dans mon travail, je suis loin d’être un expert Nix. Cet article est plus un retour de mon expérience avec l’outil qu’un véritable tutoriel.

C’est quoi Nix ?

En informatique on aime faire les choses simplement, Nix est un langage de programmation, un gestionnaire de package et un système d’exploitation.

Oui dans un but de clarté et afin d’éviter toute confusion il a été décidé de donner le même nom à 3 choses différentes 🤔.

Ici je vais surtout parler de la partie gestion de package et de comment elle me sert dans mon travail quotidien.

À propos de moi

Je suis ce que l’on pourrait appeler un devops (on discutera de la validité de ce terme une autre fois) mon quotidien consiste à automatiser des taches pour des équipes de développement de la simple compilation et déploiement d’un projet dans une infrastructure donnée à comment setup cette dîte infrastructure en un clic.

Pour cela j’ai à ma disposition tout un pannel d’outil, disponibles pour la plupart en open-source (ansible, opentofu, helm…) d’autres que je dois développer moi même (exemple une api de gestion des certificats internes en python).

Je travaille exclusivement dans mon terminal sur Ubuntu et j’alterne entre plusieurs projets qui vont être des mélanges et combinaisons de mes différents outils.

Ok c’est cool ta vie! Mais Nix ?!

Parlons plutôt gestion de packages avant!

Sous ubuntu les deux gestionnaires de packages principaux sont apt et snap. L’un comme l’autre, l’installation qu’ils vont faire sera globale, c’est à dire qu’un package une fois installé sera accessible de partout dans mon OS.

Cela me pose deux problèmes.

La plupart de mes projets vont être amenés à tourner en CI/CD ou dans un environnement autre que ma machine. J’aurais donc besoin de construire cet environnement. Seulement si le projet tourne sur mon poste à l’aide d’un package installé globalement 3 mois auparavant il se peut que je ne m’en rende pas compte au moment de développer ma CI/CD et cela va donc me faire perdre du temps.

Le deuxième c’est que je développe en python, je ne suis pas spécialement un fan des venv, j’oublie toujours de les activer, j’installe mon requirements en global et ça me fait planter le projet d’à côté parce qu’il récupère la dépendance en global, en bref ce n’est pas l’outil le plus pratique à utiliser pour moi.

Nix et la gestion des packages déclarative

Le gestionnaire de package nix fonctionne de manière déclarative.

Mes deux commandes linux préférées sont fortune et cowsay la première m’affiche une citation de fortune cookie au hasard, la seconde permet d’avoir une vache en ascii art qui cite le texte qui lui est passé en paramètre, ainsi quand je tape fortune | cowsay dans mon terminal j’obtiens

1
2
3
4
5
6
❯ fortune | cowsay

Command 'fortune' not found, but can be installed with:
sudo apt install fortune-mod
Command 'cowsay' not found, but can be installed with:
sudo apt install cowsay

Ah oui pardon! Excusez moi!

1
2
3
4
5
6
7
8
9
10
11
12
❯ nix-shell -p fortune cowsay
❯ fortune | cowsay

 _________________________________________
/ "He flung himself on his horse and rode \
\ madly off in all directions."           /
 -----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Qu’est ce qui s’est passé ici?! Les packages fortune et cowsay ne sont pas installés sur ma machine donc quand je lance fortune | cowsay j’ai en retour un message d’erreur indiquant que les deux commandes ne sont pas présentes ainsi que la démarche à suivre pour les obtenir, lancer sudo apt install fortune-mod et sudo apt install cowsay.

Dans la suite, j’ai lancé au préalable la commande nix-shell -p fortune cowsay qui me permet de créer un shell dans lequel je souhaite avoir les deux packages fortune et cowsay de disponibles, suite à cette commande, disons simplement que ✨ nix fait sa magie ✨ et me fourni donc un shell avec les deux packages de disponibles.

On parle alors de gestionnaire de package déclaratif car je déclare à nix les packages dont je vais avoir besoin dans mon environnement de travail (ici cowsay et fortune) et à lui de se débrouiller pour me fournir un contexte de travail avec ces deux packages.

Alors ok je vois l’idée, mais moi je ne déploie pas des vaches en prod!

Et bien vous devriez très cher camarade!

Mais dans le cas où votre besoin serait moins important que celui de faire réciter des proverbes à une vache, l’intêret que j’ai à cette gestion en déclaratif est que pour chacun de mes projets je peux lui définir un environnement de travail dédié.

Si au niveau de mon ubuntu la dépendance package A sur mon projet 1 peut rentrer en conflit avec la dépendance package B sur mon projet 2 la gestion déclarative de nix me permet de setup un environnement de travail sur projet 1 indépendant de celui de projet 2 et donc limiter ce conflit.

Par exemple, si j’ai deux projets nodeJS, un qui tourne sur node 24 et l’autre sur node 20 et que je dois les faire tourner en parallèle je devrais me débrouiller avec nvm pour setup une current version de node sur chacun de mes projets. Avec nix je lancerai un simple nix-shell -p nodejs_24 dans le premier et nix-shell -p nodejs_20 dans le second, chacun aura donc son contexte nodejs propre qui sera indépendant de l’autre.

ℹ️ La liste des packages ainsi que leur nom est disponible sur search.nixos.org

Mais je vous rassure tout de suite, je ne retiens pas toutes les dépendances de tête pour chacun de mes projets et je ne retape pas la commande nix-shell -p ... à chaque fois.

Dans chacun de mes projets j’ai un fichier shell.nix qui va contenir l’ensemble des opérations à lancer pour setup mon projet et la commande nix-shell va alors lire ce fichier pour me construire mon environnement à partir de la.

Le fichier shell.nix est lui écrit en langage de programmation nix en voici un exemple pour un de mes projets en python.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
let # Je déclare un ensemble de variable dans un bloc `let ... in`

pkgs = import <nixpkgs> {}; # Le gestionnaire de package nix

myPython = pkgs.python312; # Le package python que je souhaite utiliser pour mon projet

pythonWithPkgs = myPython.withPackages (pythonPkgs: with pythonPkgs; [ # La liste de mes dépendances python, liée à ma version de python
  black
  httpx
  flask
  regex
  cryptography
]);

# La description du contexte shell que je souhaite utiliser

shell = pkgs.mkShell { 
  name = "Mon Api Python";
  
  nativeBuildInputs = [ 
    pythonWithPkgs # Mon shell doit contenir l'ensemble de mes dépendances python
    pkgs.cowsay # Mon shell doit contenir le package cowsay
    pkgs.fortune # Mon shell doit contenir le package fortune
  ];

  shellHook = ''
    # Ici des commandes que je souhaite lancer en amont de mon shell, je peux y définir certain alias par exemple
    alias start_webapp = 'flask ...'
    
    fortune | cowsay
  '';
};

in

shell # Ce que je souhaite retourner en sortie de mon shell.nix

Ce fichier est placé à la racine de mon projet python, ainsi en exécutant ma commande nix-shell à la racine de mon projet j’obtiens un contexte shell dédié à faire tourner celui-ci.

Le fichier shell.nix est également commité et pushé sur un repository git, ainsi n’importe qui ayant nix d’installé sur son poste de travail pourra setup un environnement de travail pour ce projet en lançant la commande nix-shell à sa racine.

ℹ️ Il existe plein d’autres applications au fichier .nix on peut par exemple l’utiliser pour construire des images de conteneur avec, voir l’article de xeiaso 🇬🇧 sur ce sujet.

ℹ️ Le fichier shell.nix peut sembler un peu barbare à première vue, mais on s’habitue rapidement à sa syntaxe, de même il n’est pas difficile d’en générer un avec un prompt IA.

L’inconvénient

De mon retour d’expérience voici le meilleur conseil que je peux vous donner!

Limitez son utilisation en CI/CD!

Il est necessaire à chaque fois que vous voulez récupérer un package pour la première fois d’avoir un petit temps de téléchargement pour le récupérer et dans le cas d’une CI/CD qui repars d’un environnement vierge à chaque lancement il faudra retélécharger le package, ses dépendances et nix relancera sa compilation.

De même vous n’êtes pas à l’abris d’un commit un peu foireux sur les repo’ nixpkgs qui ferait tomber la compilation et donc planterait votre job lamentablement (j’ai déjà eu le cas la veille de noël avant de partir en congé…)

Pour votre CI/CD je vous recommande donc une installation plus traditionnelle des outils requis pour votre projet.

Une solution est d’avoir en place un cache cachix mais à voir si votre organisation le permet.

En revanche si votre déploiement / compilation requiert certains packages un peu obscures non disponibles sur des gestionnaires plus traditionnels il peut alors être interessant de passer par nix.

En conclusion

Voilà, une introduction à nix en espérant avoir un peu démystifié l’outil et donnez à certains l’envie de l’essayer dans son travail. J’essaie de regrouper des shell.nix de base me servant dans mon travail dans ce repository khantzen/my-shell-nix n’hésitez pas à vous en inspirer.

This post is licensed under CC BY 4.0 by the author.