NixOS 20.09 - Présentation, installation, configuration, utilisation
Voir aussi : dépôt de code - video youtube - video peertube
NixOS est une distribution linux, plutôt atypique, basée sur le gestionnaire de paquets Nix. Elle utilise une approche déclarative pour définir des environnements logiciels, de façon reproductible et composable.
À l’occasion de la sortie récente de la version 20.09, cet article propose un aperçu de NixOS, de son installation jusqu’à son utilisation, d’un point de vue de développeur.
Présentation
Nix, Nixpkgs, NixOS…
Au commencement était Nix. Il s’agit à la fois d’un langage de programmation, permettant de décrire un environnement logiciel, et à la fois d’un programme, permettant de gérer de tels environnements logiciels.
Puis vint Nixpkgs. Il s’agit d’un ensemble de fichiers Nix formant une logithèque complète, comprenant des programmes, bibliothèques, modules pour de nombreux langages, etc.
Et enfin NixOS fût. Il s’agit d’une distribution linux basée sur Nix et Nixpkgs. Ainsi le système peut être entièrement décrit et géré de manière déclarative, via des fichiers Nix : noyau et modules, système de boot, services, utilisateurs, logiciels, etc.
En fait, l’écosystème Nix est encore plus riche. Il comporte également le système de construction-continue Hydra, l’outil de déploiement de machines Nixops, l’outil de déploiement de services distribués Disnix, le service de cache Cachix, le service d’intégration continue Hercules CI, etc. À noter également que Nix a inspiré le projet GNU Guix, qui suit une approche assez similaire.
Nouveautés de NixOS 20.09
Heu… non. Cette nouvelle version n’apporte pas de révolution particulière mais plutôt des classiques mises à jour (Gcc 9.3.0, Glibc 2.31, Mesa 20.1.7, Plasma 5.18.5, Gnome 3.36…), un nouvel environnement graphique (Cinnamon), des nouveaux modules (Jitsi Meet, Podman…), des nouveaux services (Steam, Openvpn…), etc. Tout cela est détaillé dans l’annonce de sortie et dans les notes de version.
En fait, la plus grosse nouveauté du projet est peut-être une refonte de son site web, avec de nouvelles ressources, des vidéos thématiques, etc.
Pourquoi utiliser NixOS ?
Avant de répondre à la question “Pourquoi utiliser NixOS ?”, il faut se poser la question “Quand utiliser NixOS ?”. Et la réponse est : jamais.
Oui bon à la limite, si vous êtes développeur ou administrateur système, que vous jonglez entre les projets et que vous avez une bonne centaine de Go à consacrer à votre partition système, alors là oui, peut-être que NixOS peut changer votre vie (notez que je n’ai pas précisé si c’était en bien ou en mal…). Pour une utilisation “bureautique classique”, NixOS n’a pas vraiment d’intérêt et sera certainement inutilement compliqué.
Et donc “Pourquoi utiliser NixOS dans les cas qui peuvent s’y prêter ?” : parce que Nix permet de définir et reproduire des configurations de machines et de projets. Typiquement, il suffit d’ajouter à un projet un fichier Nix, décrivant ses dépendances et sa procédure de construction, pour que ça-juste-marche™.
Il est également possible d’installer Nix + Nixpkgs sur une distribution Linux classique (Debian, Fedora, etc). Cette méthode permet de profiter de l’écosystème Nix sans avoir à installer NixOS. Cependant, elle ne permet pas d’utiliser tout l’écosystème et peut nécessiter un peu de configuration supplémentaire, notamment pour lancer des applications graphiques.
Installation de NixOS
NixOS s’installe via quelques lignes de commande, un peu comme ArchLinux mais en beaucoup plus simple. Différents supports sont proposés pour installer, tester ou déployer un système : ISO graphique ou minimale, image VirtualBox, image Amazon EC2.
Ici, je considère qu’on a téléchargé l’image ISO minimale et qu’on fait une installation complète sur une machine virtuelle. La procédure pour installer sur une machine réelle est quasiment la même.
Installer le système de base
Booter sur le support d’installation.
Passer en root, configurer le clavier et lancer l’outil de partionnement :
sudo su
loadkeys fr
cfdisk /dev/sda
Par exemple, pour une table GPT, on peut faire le partitionnement suivant :
- /dev/sda1 : BIOS boot (1M)
- /dev/sda2 : Linux swap
- /dev/sda3 : Linux filesystem
Formater et activer les partitions :
mkswap -L swap /dev/sda2
swapon /dev/sda2
mkfs.ext4 -L nixos /dev/sda3
mount /dev/sda3 /mnt
- Générer une configuration et l’éditer :
nixos-generate-config --root /mnt
nano /mnt/etc/nixos/configuration.nix
- Exemple de configuration système (voir aussi le dépôt fourni) :
{ config, pkgs, ... }: {
imports = [ ./hardware-configuration.nix ];
boot.loader.grub = {
enable = true;
device = "/dev/sda";
version = 2;
};
i18n.defaultLocale = "fr_FR.UTF-8";
system.stateVersion = "20.09";
time.timeZone = "Europe/Paris";
virtualisation.docker.enable = true;
console = {
font = "Lat2-Terminus16";
keyMap = "fr";
};
environment.systemPackages = with pkgs; [
firefox
git
vim];
networking = {
hostName = "nixos";
useDHCP = false;
interfaces.enp0s3.useDHCP = true;
};
services = {
xserver = {
enable = true;
layout = "fr";
displayManager.lightdm.enable = true;
desktopManager.xfce.enable = true;
};
};
users.users.toto = {
isNormalUser = true;
extraGroups = [
"docker"
"wheel"
];
};
}
- Lancer l’installation :
nixos-install
Le fichier de configuration est alors vérifié puis le système s’installe. À la fin de l’installation :
- entrer un mot de passe pour le root
- éteindre la machine
- retirer l’ISO
- redémarrer
- se connecter en root
- changer le mot de passe de l’utilisateur (par exemple, avec la commande
passwd toto
) - se déconnecter et se reconnecter avec le compte utilisateur
Modifier la configuration système
Attention, avec NixOS, on a rarement besoin de modifier la configuration système. Par exemple, il vaut mieux installer les logiciels via la configuration utilisateur. Modifier la configuration système n’est généralement nécessaire que pour configurer des services systèmes, configurer le matériel (drivers vidéo, réseau, etc), changer de version de NixOS… La procédure est la suivante.
- Éditer le fichier de configuration système :
$ sudo nano /etc/nixos/configuration.nix
- Mettre à jour le système :
$ sudo nixos-rebuild switch
À noter que les configurations sont reproductibles, ce qui signifie qu’on peut très facilement annuler une mise à jour, revenir ponctuellement sur une configuration précise ou même reprendre une configuration pour une autre machine.
Configuration utilisateur avec home-manager
Une des grosses différences avec les distributions Linux classiques est que NixOS permet aux utilisateurs d’installer eux-mêmes des logiciels de la logithèque, sans passer par root
ni par sudo
.
Il y a plusieurs méthodes possibles pour configurer son environnement utilisateur. Généralement, on utilise une approche déclarative, qui se résume à mettre un ou plusieurs fichiers Nix dans le dossier ~/.config/nixpkgs/
et à lancer une commande de mise à jour. Depuis quelques temps, l’outil home-manager est assez populaire pour gérer sa configuration utilisateur avec Nix.
Installer home-manager
- Ajouter le canal de paquets fourni par home-manager :
$ nix-channel --add https://github.com/nix-community/home-manager/archive/release-20.09.tar.gz home-manager
$ nix-channel --update
- Se déconnecter/reconnecter puis lancer l’installation de home-manager :
$ nix-shell '<home-manager>' -A install
Modifier sa configuration utilisateur
Avec home-manager, le fichier de configuration principal est ~/.config/nixpkgs/home.nix
. Ce fichier permet non seulement de spécifier les logiciels à installer mais également de les configurer.
- Exemple de configuration (voir aussi le dépôt fourni) :
{ pkgs, ... }: {
home = {
username = "toto";
homeDirectory = "/home/toto";
stateVersion = "20.09";
packages = with pkgs; [
cabal-install
geany(python3.withPackages (ps: with ps; [ numpy ]))
];
};
programs = {
home-manager.enable = true;
git = {
enable = true;
userName = "toto";
userEmail = "toto@example.com";
};
};
}
- Pour mettre à jour la configuration :
$ home-manager switch
Comme pour les configurations systèmes, les configurations utilisateurs sont reproductibles; on peut donc facilement annuler une configuration ou revenir à une configuration précise.
Personnaliser la logithèque avec les overlays
Les overlays permettent de modifier la logithèque pour, par exemple, ajouter des nouveaux logiciels, recompiler automatiquement des logiciels existants avec d’autres options ou patchs, etc.
Ceci est très facile à faire avec Nix car son approche déclarative permet de composer les environnements logiciels. Plus exactement, un “paquet” est en fait une fonction qui modifie l’environnement courant de façon à ajouter le logiciel. On peut donc écrire un “paquet” qui modifie simplement un logiciel déjà présent. C’est ce que permettent de faire les overlays, pour l’environnement utilisateur.
À titre d’exemple, on va écrire un overlay pour ajouter le programme GNU Hello 2.9 dans la logithèque sous le nom myhello
.
- Écrire un fichier
~/.config/nixpkgs/overlays/myhello.nix
:
self: super: {
myhello = self.stdenv.mkDerivation rec {
pname = "hello";
version = "2.9";
src = self.fetchurl {
url = "mirror://gnu/hello/${pname}-${version}.tar.gz";
sha256 = "19qy37gkasc4csb1d3bdiz9snn8mir2p3aj0jgzmfv0r2hi7mfzc";
};
};
}
Ajouter le paquet
myhello
dans les paquets à installer (fichier~/.config/nixpkgs/home.nix
) :Mettre à jour la configuration utilisateur avec
home-manager switch
.
Autre exemple, pour modifier l’option enableNls
du paquet nano
, on peut écrire l’overlay suivant (puis ajouter le paquet et mettre à jour) :
self: super: {
nano = super.nano.override {
enableNls = false;
};
}
Environnements de développement
Lorsqu’on développe des logiciels, on a souvent différents projets avec différentes dépendances ou versions de dépendances. Il n’est pas toujours facile d’installer un environnement satisfaisant ces contraintes, et encore moins de reproduire cet environnement pour un nouveau projet ou pour un déploiement. C’est dans ce genre de situation que Nix est particulièrement intéressant.
Environnements temporaires
Nix permet d’exécuter des environnements temporaires. Par exemple, la commande suivante lance un environnement temporaire contenant le paquet vlc
et y exécute la commande vlc
.
$ nix run nixpkgs.vlc -c vlc
Ainsi le logiciel s’exécute et, une fois quitté, on revient dans l’environnement de base, non modifié.
Les environnements temporaires sont également utiles pour lancer un interpréteur avec des modules particuliers, qui ne sont pas forcément déjà installés dans l’environnnement de base. Par exemple, la commande suivante lance python3
dans un environnement contenant le module flask
.
$ nix-shell --run python3 -p "python3.withPackages (ps: with ps; [ flask ])"
Packager un projet
Les environnements temporaires sont pratiques pour des petits travaux rapides. Mais pour travailler sur un vrai projet, on préfèrera écrire un fichier Nix décrivant le projet.
Par exemple, considérons le code python myserver.py
suivant (voir le dépôt fourni) :
#!/usr/bin/env python3
from flask import Flask
import sys
= Flask(__name__)
app
@app.route("/")
def hello():
return "Hello, Nix!"
if __name__ == "__main__":
print("using {}".format(sys.version))
="0.0.0.0") app.run(host
Et soit le fichier de configuration setup.py
suivant :
from setuptools import setup
setup(='myserver',
name='0.1',
version= ['myserver.py']) scripts
On peut alors écrire un paquet Nix de notre projet avec le fichier default.nix
suivant :
{ pkgs ? import <nixpkgs> {} }:
with pkgs.python3Packages; buildPythonApplication {
pname = "myserver";
src = ./.;
version = "0.1";
propagatedBuildInputs = [ flask ];
}
Ceci permet de construire notre projet et d’exécuter le programme résultat :
$ nix-build
$ ./result/bin/myserver.py
Et même de l’installer dans l’environnement utilisateur :
$ nix-env -i -f .
$ myserver.py
Ainsi, on a un paquet Nix de notre projet, qui fonctionnera automatiquement sur une machine disposant de Nix. Et avec quelques modifications mineures (url du code source…), on pourrait même intégrer notre paquet à la logithèque Nixpkgs.
Environnement de développement
Le fichier default.nix
permet également d’exécuter un environnement de développement adapté au projet, via la commande nix-shell
. Cette commande ne construit pas le paquet mais charge un environnement contenant toutes les dépendances nécessaires pour travailler sur le projet.
$ nix-shell
$ python3 myserver.py
Le fichier default.nix
et la commande nix-shell
sont les outils principaux pour développer un projet avec Nix.
Fixer les versions
Le fichier default.nix
précédent utilise la logithèque disponible par défaut. Cependant, lorsqu’on veut déployer un projet, on préfère parfois fixer complètement les versions des dépendances utilisées. Pas de soucis avec Nix, il suffit d’écrire un fichier release.nix
qui réutilise la configuration default.nix
mais en fixant la version précise de la logithèque à utiliser :
let
rev = "cd63096";
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
pkgs = import (fetchTarball url) {};
in import ./default.nix { inherit pkgs; }
Pour construire le paquet correspondant, il suffit alors de préciser le fichier à utiliser :
$ nix-build nix/release.nix
$ ./result/bin/myserver
Construire une image Docker
Nix permet également de construire des images Docker à partir de nos fichiers Nix. Par exemple, à partir de notre paquet de release, on peut écrire le fichier docker.nix
suivant :
{ pkgs ? import <nixpkgs> {} }:
let
app = import ./release.nix;
entrypoint = pkgs.writeScript "entrypoint.sh" ''
#!${pkgs.stdenv.shell}
$@
'';
in
pkgs.dockerTools.buildLayeredImage {
name = "myserver-py";
tag = "latest";
config = {
WorkingDir = "${app}";
Entrypoint = [ entrypoint ];
Cmd = [ "${app}/bin/myserver.py" ];
};
}
On peut alors, construire l’image correspondante, la charger dans le service docker
et la lancer comme n’importe quelle image Docker classique :
$ nix-build nix/docker.nix
$ docker load -i result
$ docker run --rm -it -p 5000:5000 myserver-py
Environnements automatiques avec nix-direnv
Comme expliqué précédemment, l’outil nix-shell
permet de lancer un environnement de développement adapté au projet. L’outil nix-direnv est complémentaire et permet de charger automatiquement l’environnement, de le garder en cache et de garder les dépendances lors des nettoyages du système.
Installer nix-direnv
- En
sudo
, ajouter dans le fichier de configuration système/etc/nixos/configuration.nix
:
# nix-direnv
nix.extraOptions = ''
keep-outputs = true
keep-derivations = true
'';
Mettre à jour la configuration système avec la commande
sudo nixos-rebuild switch
.Activer
nix-direnv
dans la configuration utilisateur~/.config/nixpkgs/home.nix
:
programs = {
bash.enable = true;
direnv = {
enable = true;
enableNixDirenvIntegration = true;
};
home-manager.enable = true;
};
- Mettre à jour la configuration utilisateur avec la commande
home-manager switch
.
Utiliser nix-direnv
Par exemple pour le projet précédent, on avait notre code et ses fichiers de configuration dans un dossier myserver-py
. À partir du fichier default.nix
, il suffit d’écrire un fichier .envrc
et d’activer direnv
pour ce dossier :
$ echo "use nix" > .envrc
$ allow direnv
Ainsi, l’environnement est automatiquement chargé lorsqu’on va dans le dossier du projet :
$ cd myserver-py/
direnv: loading ~/gitlab/nixos-20.09_config/myserver-py/.envrc
direnv: using nix
direnv: using cached derivation
...
$ python3 myserver.py
...
Bien entendu, si on modifie la configuration (default.nix
), l’environnement est automatiquement reconstruit et rechargé.
Conclusion
Avec son approche déclarative, Nix permet de gérer des environnements logiciels de façon reproductible et composable. Si l’idée initiale vient de travaux de recherche du début des années 2000, l’écosystème n’a cessé d’évoluer depuis. Aujourd’hui, la distribution NixOS, associée à des outils comme home-manager et nix-direnv, fournit un système très pratique pour un développeur ou pour un administrateur système.
Cependant, cette approche necessité une phase de prise en main non négligeable et n’est pas forcément très intéressante pour une utilisation plus “classique” de l’informatique.