LXC sur Debian Wheezy

sam. 31 août 2013 by Marmotte

LXC, pour Linux Containers, permet de lancer plusieurs environnements Linux sur une même machine, de manière isolée, comme le fait OpenVZ. Par rapport à des machines virtuelles, les performances sont donc bien meilleures, puisque les différents systèmes fonctionnent directement sur le noyau, sans couche de virtualisation intermédiaire. Cela implique aussi qu'on ne peut pas utiliser d'autre système que Linux dans LXC.

Il y a de nombreuses manières de mettre en place des containers LXC. Dans cette note, j'explique comment mettre en place des containers connectés à l'hôte par une interface réseau bridge virtuelle, et installés dans des volumes logiques lvm.

Attention : N'ayant encore jamais configuré de machine en utilisant le protocole IPv6, cette note d'installation se limite pour le moment à IPv4.

Préparation du système hôte

Installation

Sous Debian, il faut installer les paquets suivants :

$ sudo apt-get install --no-install-recommends lxc bridge-utils debootstrap iptables-persistent

Note : Le paquet iptables-persistent permet de charger automatiquement des règles iptables lors du démarrage de la machine. Cela nous servira pour définir les règles de NAT indispensables pour permettre la communication entre les containers et l'extérieur.

Configuration du réseau

La configuration du réseau au niveau du système hôte se fait en trois étapes :

  • Création des interfaces bridge, sur lesquels les containers se connecteront
  • Autorisation du NAT, afin de permettre aux containers de dialoguer avec l'extérieur du serveur
  • Configuration d'un serveur DHCP, pour simplifier l'attribution d'adresses IP aux containers

Création des interfaces bridge

Pour permettre la création d'interfaces réseau bridge virtuelles, il faut activer le module dummy du noyau, en précisant le nombre d'interfaces factices voulues. Il suffit pour ça d'ajouter une ligne dans le fichier /etc/modules.

dummy numdummies=1

Il faut ensuite ajouter une interface bridge virtuelle, dans le fichier /etc/network/interfaces.

auto br0
iface br0 inet static
    address 10.0.0.254
    netmask 255.255.255.0
    network 10.0.0.0
    broadcast 10.0.0.255
    bridge_ports dummy0
    bridge_fd 0
    bridge_maxwait 0

Note : S'il doit y avoir plusieurs réseaux LXC différents, il faut ajouter une interface bridge par réseau, en adaptant le nom des interfaces (brX et dummyX), et les adresses IP. Il faut aussi augmenter le nombre d'interfaces factices en incrémentant la valeur du paramètre numdummies dans le fichier /etc/modules.

Note : Pour modifier le nombre d'interfaces factices à chaud, il est nécessaire de décharger, puis recharger le module dummy. Cette opération n'impacte pas les containers déjà lancés, puisque la présence de l'interface dummy est uniquement nécessaire lors de la création de l'interface bridge associée.

$ sudo rmmod dummy
$ sudo modprobe dummy numdummies=2

Une fois le réseau configuré, il faut charger le module dummy, puis monter la ou les interfaces bridge. En cas de redémarrage du serveur, ces opérations seront évidemment exécutées automatiquement.

$ sudo modprobe dummy numdummies=1
$ sudo ifup br0

Autorisation du NAT

Il ne reste plus qu'à autoriser le NAT, ce qui permettra aux containers de dialoguer avec le reste du réseau. Pour autoriser le NAT, il faut créer un nouveau fichier dans le répertoire /etc/sysctl.d/, que je nomme habituellement lxc.conf, contenant :

net.ipv4.conf.default.rp_filter = 1
net.ipv4.ip_forward = 1

Note : Le nom du fichier n'a pas d'importance, il doit juste se terminer par .conf pour être pris en compte.

Il faut ensuite recharger la configuration de sysctl pour prendre en compte ces modifications à chaud.

$ sudo sysctl --system

Note : Les modifications ajoutées dans le fichier de configuration doivent être affichées dans la console. Si elles n'apparaissent pas, c'est qu'elles n'ont pas été prises en compte. L'erreur la plus courante est l'oubli de l'extension .conf dans le nom du fichier.

Il faut ensuite ajouter une règle iptables pour permettre les connexions sortantes des containers, dans le fichier /etc/iptables/rules.v4.

-A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

Note : L'interface eth0 est ici l'interface par laquelle sortiront les requêtes des containers vers l'extérieur du serveur.

Pour activer cette règle iptables, il suffit de lancer le script iptables-persistent.

$ sudo service iptables-persistent start

Attribution des adresses IP aux containers

Pour simplifier la gestion des adresses IP des containers, je mets en place Dnsmasq sur le système hôte. Cela me permet de définir les IP des containers dans le fichier de configuration de DNSMasq, ce qui rend leur modification très simple lorsqu'un changement doit être apporté au niveau du réseau. La configuration de Dnsmasq pour LXC contient principalement une plage d'adresses IP par interface bridge, ainsi qu'une attribution d'IP pour chaque container.

interface=br0

domain=server
expand-hosts

dhcp-authoritative
dhcp-leasefile=/tmp/dhcp.leases

dhcp-range=interface:br0,10.0.0.1,10.0.0.200,255.255.255.0,12h
dhcp-host=00:00:00:00:00:01,10.0.0.1,frontend
dhcp-host=00:00:00:00:00:02,10.0.0.2,db

Configuration des cgroups

LXC utilise les cgroups pour l'isolation des ressources. Il faut donc monter le système de fichiers spécifique cgroup, en ajoutant une ligne dans le fichier /etc/fstab.

cgroup  /sys/fs/cgroup  cgroup  defaults    0 0

On monte ensuite ce point de montage pour une prise en compte à chaud :

$ sudo mount /sys/fs/cgroup

Récupération du script de création de container

Le script de création de container distribué avec LXC sous Debian Wheezy est bugué, et crée des containers non fonctionnels.

Je me suis donc basé sur le script proposé par défaut par le projet LXC, auquel j'ai apporté quelques modifications.

  • Le script par défaut de LXC est disponible ici
  • Le script que j'utilise est disponible ici

Pour qu'il soit utilisable, il faut le déposer dans le répertoire /usr/share/lxc/templates/, le nommer lxc-<template-name> et le rendre exécutable.

Création d'un groupe de volumes lvm

Si on ne précise pas de nom, LXC utilise le groupe nommé lxc par défaut. Si vous créez un groupe de volumes spécifiquement pour LXC, il est donc préférable de lui donner ce nom.

Création d'un container

Le script de création de containers peut prendre un fichier de configuration en paramètre. Le contenu de ce fichier est ajouté au fichier de configuration du container pendant l'installation. Ça permet de prédéfinir une partie de la configuration.

Dans le fichier de configuration de base, je mets habituellement la configuration réseau, qui est absente du fichier de configuration par défaut, puisqu'elle diffère selon la manière de gérer le réseau dans les containers. Dans ce fichier, il faut principalement modifier le nom de l'interface bridge utilisée, et l'adresse matérielle du container.

# Network configuration
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 00:00:00:00:00:00

Note : Si vous avez oublié d'ajuster les paramètres dans ce fichier avant de créer le container, il est bien sûr possible de modifier le fichier de configuration du container après son installation.

Nous pouvons maintenant lancer la création du container proprement dite.

Attention : Il ne faut pas mettre d'underscore dans le nom du container. Je ne sais pas si c'est le résultat d'un bug, mais quand j'ai essayé, les containers dont le nom contenait un underscore étaient inutilisables.

$ sudo lxc-create -t debian-marmotte -n <container-name> -f <BaseConfigFilePath> -B lvm --fstype ext4 --fssize 5G

Note : La valeur à donner au paramètre -t est le nom du template, sans le préfix lxc-.

Note : Si vous n'avez pas de groupe de volumes lvm nommé lxc, il faut préciser le nom du groupe de volumes à utiliser avec le paramètre --vgname.

Note : Si vous n'utilisez pas lvm, vous pouvez retirer tous les paramètres à partir de -B lvm. Le système sera alors placé directement dans le répertoire /var/lib/lxc/<container-name>/rootfs/.

Une fois le container créé, vous pouvez vérifier la configuration, qui se trouve dans le fichier /var/lib/lxc/<container-name>/config, et l'adapter si nécessaire.

Configurations supplémentaires

  • Pour rediriger un port du serveur vers une machine LXC, ajouter une règle dans le fichier /etc/iptables/rules.v4
  • Pour que le container soit lancé automatiquement lors du démarrage de la machine, il faut ajouter un lien symbolique vers son fichier de conf dans le répertoire /etc/lxc/auto/.

    $ sudo ln -s /var/lib/lxc/<container-name>/config /etc/lxc/auto/<container-name>.conf
    
  • Dans le cas de plusieurs réseaux LXC, s'ils ne doivent pas pouvoir se contacter entre eux, il est possible de bloquer les communications avec de simples règles iptables, dans le fichier /etc/iptables/rules.v4.

    -A FORWARD -i br0 -o br1 -j DROP
    -A FORWARD -i br1 -o br0 -j DROP
    

    Attention : Il faut bien bloquer les paquets dans les deux sens. Bloquer la réponse à une requête n'empêche pas le traitement de cette requête par le serveur qui la recevrait.

Manipulation des containers

LXC propose quelques commandes permettant de manipuler les containers. J'ai présenté la commande lxc-create dans la partie précédente, qui permet de créer des containers. Ce paragraphe passe en revue les autres commandes lxc souvent utilisées.

Démarrage

$ sudo lxc start <container-name> -d

Note : Le paramètre -d démarre le container en mode démon. Sans ce paramètre, le container démarrera dans le shell courant, et il sera lié. La fermeture du shell entraînera donc l'arrêt du container.

Arrêt

$ sudo lxc stop <container-name>

Connexion en console

$ sudo lxc console <container-name>

Note : La connexion en console sur un container est bien pratique quand ce container ne répond pas sur le réseau.

Problèmes rencontrés

Pas de réseau sur un container Debian Squeeze

Problème observé sur un container Debian Squeeze créé sur un hôte Debian Wheezy. Un autre serveur ayant déjà des containers Debian Squeeze dont l'hôte a été migré de Squeeze vers Wheezy ne présente pas ce dysfonctionnement. Je n'ai pas encore créé de nouveau container Debian Squeeze depuis la migration de l'hôte pour vérifier si ce problème survient.

Si le réseau ne fonctionne pas dans le container, le script /etc/init.d/networking est peut être erroné. Il suffit d'ajouter ces lignes au début du script pour créer le répertoire /dev/shm/network dans le container, juste après le chargement du fichier /lib/lsb/init-functions :

# Ensure /dev/shm/network exists
[ -d /dev/shm/network ] || mkdir /dev/shm/network

GNU/screen ne fonctionne pas dans le container

Problème observé sur un container Debian Wheezy créé sur un hôte Debian Wheezy. Sur un autre serveur, dont l'hôte et des containers ont été migrés de Squeeze vers Wheezy ne présente pas ce dysfonctionnement. Un nouveau container Debian Wheezy créé depuis la migration de l'hôte présente le même problème.

Si screen se ferme juste après avoir été lancé, c'est certainement que les droits du périphérique /dev/ptmx sont erronés. Il suffit de donner accès en lecture et écriture à ce périphérique pour résoudre le problème.

$ sudo chmod a+rw /dev/ptmx

Note : Cette manipulation n'est pas définitive, et doit être refaite après chaque relancement du container.

Note : Ce comportement semble résulter du paramètre ptmxmode=000 présent dans le fichier /etc/mtab, mais je n'ai pas encore trouvé comment le modifier. Dans un container Debian Wheezy migré depuis Debian Squeeze, le fichier /etc/mtab contient ptmxmode=666 à la place.