L’écosystème Nix

Gestionnaire de paquets Nix

Une révolution dans la gestion des dépendances

  • Gestionnaire de paquets purement fonctionnel développé par Eelco Dolstra
  • Première version en 2003
  • Approche révolutionnaire: immutabilité et reproductibilité
  • Différent des gestionnaires traditionnels (apt, yum, homebrew)
  • Stockage avec des chemins uniques basés sur des hashes

Le langage Nix

# Une exemple simple
let
  pkgs = import <nixpkgs> {};
in
{
  myPackage = pkgs.stdenv.mkDerivation {
    name = "hello-world";
    src = ./.;

    buildPhase = ''
      gcc -o hello hello.c
    '';

    installPhase = ''
      mkdir -p $out/bin
      cp hello $out/bin/
    '';
  };
}

Langage fonctionnel pure et lazy

Code non touché n’est pas évalué

No “side effects”

  • Même input = même résultat
  • Identique “bit for bit”

Syntaxe simple mais puissante

Similaire à

  • Haskell (ex: let)
  • JSON/Javascript (ex: attrsets { })

Bases du langage Nix

# Types de base
nix-repl> 42
42

nix-repl> "Hello, Nix!"
"Hello, Nix!"

nix-repl> true
true

# Listes
nix-repl> [ 1 2 3 "hello" ]
[ 1 2 3 "hello" ]

# Sets (objets)
nix-repl> { name = "John"; age = 30; }
{ age = 30; name = "John"; }

# Fonctions
nix-repl> add = x: y: x + y
«lambda @ (string):1:1»

nix-repl> add 5 3
8

# Let expressions
nix-repl> let greeting = "Hello"; name = "World"; in "${greeting}, ${name}!"
"Hello, World!"

Nix comme langage de programmation fonctionnelle

Contrairement aux formats de configuration statiques tels que YAML ou JSON, Nix est un langage de programmation fonctionnel complet. Voici un exemple utilisant la fonction map pour démontrer ses capacités de calcul

Exemple

nix-repl> pkgs = import <nixpkgs> {}

nix-repl> map (x: x * 2) [1 2 3 4 5]
[ 2 4 6 8 10 ]

nix-repl> map (name: pkgs.${name}) ["hello" "git" "vim"]
[ «derivation /nix/store/...» «derivation /nix/store/...» «derivation /nix/store/...» ]

nix-repl> map (pkg: pkg.name) (map (name: pkgs.${name}) ["hello" "git" "vim"])
[ "hello-2.12.1" "git-2.42.0" "vim-9.0.1677" ]

Utilisation du langage pour un packet

let
  pkgs = import <nixpkgs> {};
in
myPackage = pkgs.stdenv.mkDerivation {
  name = "hello-world";
  src = ./.;

  buildPhase = ''
    gcc -o hello hello.c
  '';

  installPhase = ''
    mkdir -p $out/bin
    cp hello $out/bin/
  '';
}

Utilisation de base et commandes

# Utiliser un paquet temporairement
nix-shell -p gcc python3

# Chercher des paquets
nix search nixpkgs vim

# Entrer dans un shell de développement
nix-shell

# Construire un paquet
nix-build

# Sort les poubelles!
nix-collect-garbage

Chemins dans le magasin Nix (nix-store)

Tout va dans /nix/store

Fichier simple

/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-hello.txt

Paquet (package) complet (Git)

/nix/store/bcdqax4p8xww4s7z4v7n2k8x9m5j6h2l-git-2.41.0
├── bin/git
├── lib/
├── share/man/
└── libexec/git-core/

Bibliothèques (OpenSSL)

/nix/store/f8k2h9x3m4p6n7q9r1s5t8v2w4y6z9b3-openssl-1.1.1u
├── lib/libssl.so.1.1
├── lib/libcrypto.so.1.1
├── include/openssl/
└── bin/openssl

Caractéristiques clés de Nix

  • Builds reproductibles: même input = même output, toujours
  • Rollbacks atomiques: retour en arrière sans casser le système
  • Environnements isolés: pas de “dependency hell”
  • Multi-utilisateur et multi-version: plusieurs versions du même paquet
  • Garbage collection: nettoyage automatique des paquets/dérivations inutilisés
  • Cross-platform: Linux, macOS, et même WSL

Avantages d’utiliser Nix

  • Fini les “ça marche sur ma machine”
  • Configuration déclarative : infrastructure as code
  • Uniformité des builds entre projets
  • Environnements de développement reproductibles
  • Déploiements fiables et prévisibles
  • Isolation complète entre projets
  • Rollback instantané en cas de problème
  • Partage facile d’environnements entre équipes

Inconvénients de Nix

Courbe d’apprentissage abrupte

Solution:

Commencer avec des cas d’utilisation simples. Documentation communautaire riche. Voir les configurations des gens sur GitHub!

Langage Nix parfois déroutant

Solution:

Nix Dev contient une série de tutoriels très bien fait et clair.

Consommation d’espace disque

Solution:

Garbage collection régulièrement avec nix-collect-garbage -d

Dans la configuration NixOS:

nix = {
  gc = {
    automatic = true;
    dates = "weekly";
  };
};

Sortie de nix-collect-garbage

[...]
deleting '/nix/store/pv760cyimzrs9lps2p37fg7cyyk0swp4-HTTP-Server-Simple-PSGI-0.16.tar.gz.drv'
deleting '/nix/store/p8nvy5qydxr5kxmil9hi3gpjjih8k0iz-source.drv'
deleting unused links...
note: currently hard linking saves 31583.58 MiB
84971 store paths deleted, 63118.27 MiB freed

Paquets pas toujours à jour

Relatif puisque nixpkgs contient le plus grand nombre de paquets (plus de 120 000).

Solution:

  • “Fork” corporatif de nixpkgs.
  • Contributions communautaires, overlays personnalisés:

Exemple d’un overlay:

nixpkgs.overlays = [
  (final: prev: {
    hyprpaper = prev.hyprpaper.overrideAttrs (old: rec {
        version = "0.8.1";
        src = prev.fetchFromGitHub {
        owner = "hyprwm";
        repo = "hyprpaper";
        rev = "v${version}";
        hash = "sha256-RDeDDRgmMnmEKJalIovpXKu9urHuCvH8tHP4RsplyXs=";
        };
    });
  })
];

Builds initiaux lents

Solutions:

  • Cache binaire Hydra, builds incrémentaux
  • Utilisé une machine de build comme cache
  • Utilisé d’autres stations de travail pour les builds locaux

Tooling

nix-shell : Environnements temporaires

Problème initial : pas de GCC installé

$ gcc --version
bash: gcc: command not found

$ which gcc
# Aucun résultat - gcc n'est pas disponible

Solution avec nix-shell

# Ouvrir un shell temporaire avec GCC
$ nix-shell -p gcc

[nix-shell]$ gcc --version
gcc (GCC) 12.3.0
Copyright (C) 2022 Free Software Foundation, Inc.

[nix-shell]$ which gcc
/nix/store/...-gcc-12.3.0/bin/gcc

# Quitter le shell
[nix-shell]$ exit

$ gcc --version
bash: gcc: command not found  # Plus disponible !

Versions multiples concurrentes

# Terminal 1 : GCC 12
$ nix-shell -p gcc
[nix-shell]$ gcc --version
gcc (GCC) 12.3.0

# Terminal 2 : GCC 11 (version spécifique)
$ nix-shell -p gcc11
[nix-shell]$ gcc --version
gcc (GCC) 11.4.0

# Terminal 3 : GCC 13 (version récente)
$ nix-shell -p gcc13
[nix-shell]$ gcc --version
gcc (GCC) 13.2.0

Toutes les versions coexistent sans conflit

Environnements plus complexes

# Plusieurs outils simultanément
$ nix-shell -p gcc python3 nodejs go rust

[nix-shell]$ gcc --version && python --version && node --version
gcc (GCC) 12.3.0
Python 3.11.6
v18.18.2

# Versions spécifiques
$ nix-shell -p gcc11 python39 nodejs-16_x

[nix-shell]$ python --version && node --version
Python 3.9.18
v16.20.2

Exemples : Base de code C/C++

# shell.nix pour un projet C++
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    gcc
    cmake
    gdb
    valgrind
    boost
    sqlite
    openssl
    pkg-config
  ];

  shellHook = ''
    echo "Environnement C++ prêt!"
    echo "GCC version: $(gcc --version | head -1)"
  '';
}

Utilisation: nix-shell dans le dossier du projet

Paquets Python, Rust et Go

Python

# Package Python avec des dépendances spécifiques
{ pkgs ? import <nixpkgs> {} }:

pkgs.python3Packages.buildPythonApplication {
  pname = "mon-app-python";
  version = "1.0.0";

  propagatedBuildInputs = with pkgs.python3Packages; [
    numpy
    flask
    requests
  ];
}

Rust

# Package Rust avec cargo
{ pkgs ? import <nixpkgs> {} }:

pkgs.rustPlatform.buildRustPackage {
  pname = "mon-app-rust";
  version = "1.0.0";

  src = ./.;
  cargoHash = "sha256-XYZ9876543210fedcba9876543210XYZ9876543210=";
}

Go

# Package Go
{ pkgs ? import <nixpkgs> {} }:

pkgs.buildGoModule {
  pname = "mon-app-go";
  version = "1.0.0";

  src = ./.;
  vendorHash = "sha256-ABC1234567890abcdef1234567890ABC1234567890=";
}

NixOS et sa relation avec Nix

  • NixOS : distribution Linux basée sur Nix Package Manager
  • Configuration déclarative complète du système d’exploitation
  • Rollback du système entier possible de façon atomique
  • Même philosophie : reproductibilité et immutabilité
  • Configuration dans /etc/nixos/configuration.nix
  • Rebuild du système : nixos-rebuild switch

Exemple de configuration.nix avec services et paquets

{ config, pkgs, ... }: {
  # Configuration réseau
  networking.hostName = "mon-serveur";
  networking.firewall.enable = true;

  # Services système
  services.openssh = {
    enable = true;
    settings.PasswordAuthentication = false;
  };

  services.nginx = {
    enable = true;
    virtualHosts."localhost" = {
      root = "/var/www";
    };
  };

  # Packages installés globalement
  environment.systemPackages = with pkgs; [
    vim
    git
    curl
  ];

  # Utilisateurs
  users.users.alice = {
    isNormalUser = true;
    extraGroups = [ "wheel" "networkmanager" ];
    openssh.authorizedKeys.keys = [
      "ssh-rsa AAAAB3NzaC1yc2E... alice@example.com"
    ];
  };

  system.stateVersion = "25.11";
}

nixpkgs : Le cœur de l’écosystème Nix

Qu’est-ce que nixpkgs ?

  • Dépôt Git géant contenant plus de 100 000 paquets
  • Définitions de paquets: comment construire chaque logiciel
  • Modules NixOS: configuration des services système
  • Entièrement sur GitHub: https://github.com/NixOS/nixpkgs
  • Développement ouvert: n’importe qui peut contribuer

Contributions communautaires

Types de contributions courantes

  • Ajout de nouveaux paquets
  • Mise à jour de versions existantes
  • Correction de bugs durant les builds
  • Amélioration de modules NixOS
  • Documentation

Plus de 5 000 contributeurs actifs!

Système de modules NixOS

  • Modularité: découpage logique de la configuration
  • Réutilisabilité: partage entre systèmes
  • Options et services prédéfinis
  • Composition facile de fonctionnalités
  • Type safety: validation automatique

Exemple de configuration avec module

# Structure modulaire
{
  imports = [
    ./hardware-configuration.nix
    ./modules/web-server.nix
    ./modules/monitoring.nix
  ];

  # Configuration spécifique
  networking.hostName = "mon-serveur";
}

Exemples de modules NixOS

{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.services.presentation-server;
in {
  options.services.presentation-server = {
    enable = mkEnableOption "presentation server";

    port = mkOption {
      type = types.port;
      default = 8080;
      description = "Port for the presentation server";
    };

    domain = mkOption {
      type = types.str;
      default = "localhost";
      description = "Domain name for the presentation";
    };
  };

  config = mkIf cfg.enable {
    services.nginx = {
      enable = true;
      virtualHosts."${cfg.domain}" = {
        listen = [{ addr = "0.0.0.0"; port = cfg.port; }];
        root = pkgs.writeTextDir "index.html" ''
          <!DOCTYPE html>
          <html>
            <head>Présentation NixOS</head>
            <body>
              <h1>Bienvenue à la présentation NixOS</h1>
              <p>Cette présentation est servie par un module personnalisé !</p>
            </body>
          </html>
        '';
        locations."/" = {
          tryFiles = "$uri $uri/ /index.html";
        };
      };
    };

    networking.firewall.allowedTCPPorts = [ cfg.port ];
  };
}

Utilisation du module

{ config, pkgs, ... }: {
  services.presentation-server = {
    enable = true;
    port = 8080;
    domain = "presentation.local";
  };
}

Et beaucoup plus!

Plus de 1000 modules disponibles dans nixpkgs!

https://search.nixos.org/options

Trucs intéressant

L’appliance-controller doit pouvoir repartir des services

let
  cfg = config.services.appliance-controller;
in
{
  options.services.appliance-controller = {
    enable = lib.mkEnableOption "Appliance Controller service";

    user = lib.mkOption {
      type = lib.types.str;
      default = "appliance-controller";
      description = "User account under which the appliance controller runs";
    };

    config = lib.mkIf cfg.enable {
        security.polkit = {
            enable = true;
            extraConfig = ''
            polkit.addRule(function(action, subject) {
                var units = ["irxv-processor.service", "stats-aggregator.service", "promtail.service", "prometheus.service"];

                if (action.id == "org.freedesktop.systemd1.manage-units" && subject.user == "${cfg.user}") {
                    var unit = action.lookup("unit");
                    for (var i = 0; i < units.length; i++) {
                        if (unit == units[i]) {
                            return polkit.Result.YES;
                        }
                    }
                }
            });
            '';
        };
    };
  };
}

Conclusion et ressources supplémentaires

Pourquoi adopter Nix?

  • Résout définitivement les problèmes de reproductibilité
  • Simplifie la gestion d’environnements complexes
  • Améliore la collaboration en équipe
  • Réduit les bugs liés à l’environnement

Ressources utiles

Conseil: Commencez petit avec nix-shell pour vos projets!

Questions?

Merci de votre attention!