LXC : un conteneur nixos

Rédigé par Alexandre le 06/05/2022

Mis à jour le 24/12/2022

#nixos #lxc #autohébergement

Après plusieurs mois d'utilisation sur mon poste professionnel puis quelques jours sur mon poste personnel, je crois que je deviens complètement accro à NixOS.

Ce matin en me levant, je me suis penché sur les conteneurs LXC sous NixOS et me suis rendu compte que je n'avais pas réellement compris comment fonctionnait nixos-generate pour LXC. En effet, il ne s'agit pas de préparer un conteneur mais d'en déployer un. La différence n'est pas subtile, mais pour ceux qui ne la voit pas, il s'agit de déployer un conteneur sur lequel il y a déjà toute la configuration souhaitée. Je pense qu'il est possible de voir ça comme une sorte de conteneur Docker.

Du coup dans cet article, je vais expliquer comment préparer puis déployer un conteneur LXC sous NixOS.

Pré-requis

La réalisation de cet article nécessite de disposer d'une machine sous NixOS. A l'origine j'avais utilisée une VM sous gnome-boxes, mais maintenant que NixOS est mon système d'exploitation, tout est plus simple.

Au lieu d'installer le paquet au niveau système, je vais directement l'utiliser dans un shell :

nix-shell -p nixos-generators

À partir de là, le nouveau shell qui a été créé dispose de la commande nixos-generators.

Créer la configuration

Le fichier de configuration que j'ai décidé de jouer ici est volontairement très simple. En effet, je vais simplement ajouter le paquet tmux à l'image du conteneur.

Le contenu du fichier /tmp/lxc-nixos/configuration.nix :

{ config, pkgs, ... }:

{
  imports = [
    ./conf.d/packages.nix
  ];

  # Tell NixOS that we are a container
  boot.isContainer = true;

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "22.11"; # Did you read the comment?
}

Le contenu du fichier ./conf.d/packages.nix :

{ config, pkgs, ... }:

{
  environment.systemPackages = with pkgs; [
    tmux
  ];
}

Dans le cas d'un exemple aussi simple que celui, avoir séparé les fichiers de configuration n'est pas utile, cependant cela permet de voir qu'il est possible de le faire. Je recommande vivement de le faire sur des configurations un peu plus complexe, notamment pour avoir un fichier de configuration par service (wireguard, nginx, etc.

Générer

L'import d'une image dans LXC nécessite de disposer d'une image avec les données et les métadonnées qui y sont associées. Ces deux éléments nécessitent deux commandes distinctes. A la fin de chacune des commandes suivantes, le chemin vers le résultat est fourni.

Créer les données (le rootfs du conteneur) :

nixos-generate -f lxc -c /tmp/lxc-nixos/configuration.nix

Créer les métadonnées :

nixos-generate -f lxc-metadata

À titre d'information, la documentation de nixos-generate fourni une commande LXC pour importer directement l'image en générant les éléments qui vont bien. Je n'utilise pas cette commande parce que mes hôtes LXD sont sous Debian et que cela restera comme cela parce que l'installation de NixOS sur ceux-ci me semble périlleuse.

Importer

Avant de pouvoir importer l'image, je dois les envoyer sur un de mes hôtes LXD. Je profite de cette étape pour renommer les fichiers qui malheureusement portent le même nom.

Lors de l'import de l'image, je spécifie un alias afin de pouvoir la retrouver facilement :

lxc image import --alias nixos-test nixos-system-x86_64-linux_test-metadata.tar.xz nixos-system-x86_64-linux_test-rootfs.tar.xz

La liste de toutes les images importées est visualisable via :

lxc image list

Les éléments ayant été importés, supprimer les fichiers.

Déployer

À partir de là, c'est du grand classique :

lxc launch nixos-test ct-nixos-test

Se connecter au conteneur directement depuis l'hôte :

lxc exec ct-nixos-test -- bash

Vérifier la présence de tmux :

[root@nixos:~]# which tmux
/run/current-system/sw/bin/tmux

Voilà, nous sommes arrivés à nos fins !

Compléments

Cycle de vie des images

Ne souhaitant pas maintenir une image NixOS, je prends le parti de remplacer le conteneur pour le mettre à jour ou changer sa configuration. J'industrialiserais le processus afin de ne pas avoir à effectuer toutes les étapes à la main. Ce mode de fonctionnement me pousse à établir un cycle de vie court des images. Je pense avoir deux images à la fois pour un même conteneur afin de pouvoir revenir à l'image précédente si la nouvelle est problématique.

Autres architectures

Cet article montre la création d'une image ayant la même architecture que le système sur lequel elle est générée (ici x86_64). Les hôtes LXD que j'utilise ont pour architecture aarch64. Heureusement, tout est prévu.

Emuler l'architecture sur la machine générant les images en ajoutant la ligne suivante au fichier /etc/nixos/configuration.nix :

boot.binfmt.emulatedSystems = [ "aarch64-linux" ];

Redémarrer puis générer les images en spécifiant l'architecture :

nixos-generate -f lxc --system aarch64-linux -c /tmp/lxc-nixos/configuration.nix 

Références

  • https://nixos.wiki/wiki/LXD
  • https://github.com/nix-community/nixos-generators
  • https://sleeplessbeastie.eu/2021/08/16/how-to-create-lxd-image-from-an-existing-snapshot/
  • https://nixos.wiki/wiki/Storage_optimization