Journal Debian, VServer & OpenVZ : les conteneurs

Posté par .
34
22
oct.
2008
Howdy journal !

Aujourd'hui, je voudrais te parler d'un sujet qui m'est cher, sur mes deux serveurs (des vieilles babasses à base d'Athlon-XP... enfin, avec plein de RAM et de disques durs, quand même, hein) : les conteneurs ! Et plus particulièrement, les conteneurs avec OpenVZ, nouvel arrivant dans la prochaine Debian stable, ie Lenny.

Bon : j'annonce, c'est un _long_ journal (pas assez sensationnel pour en faire une dépêche pour autant... allez, mettons que c'est un article ). Maintenant, si vous voyez déjà l'intérêt de ce genre de solution, vous pouvez directement passer à la partie "2", si vous connaissez déjà VServer, vous pouvez directement passer à la partie "2-2", et si vous êtes un dessaïdeur pressé vous pouvez directement aller vous faire f à la conclusion, la partie "3".

Le sujet est plutôt vaste, et, malgré la longueur, je ne fais que l'effleurer, en donnant quand même pas mal de petits trucs que j'ai appris en touchant aux bousins. En espérant que ce journal serve à quelques uns, qui voudront donner leur chance aux conteneurs (et en ne doutant pas un instant qu'il m'a servi et me servira, à moi, respectivement, à mettre mes idées un peu plus au clair, et, de "pense-bête" occasionnel [oui, je sais, c'est gros pour un bookmark perso])...




0 - Vaccination

Histoire de faire une petite piqûre de rappel, les conteneurs sont un cas particulier de virtualisation, laquelle peut-être subdivisée en deux grands types majeurs (ceci n'engage que moi, et ergoter sur les différences n'est pas tant mon propos que de présenter l'isolation via conteneurs sous Debian) :

- paravirtualisation/hypervision/machine virtuelle : ici, on a un noyau "hôte" sur un OS "hôte", qui va permettre de lancer des noyaux "invités" pour des OS "invités" - on va parler de paravirtualisation quand les noyaux "invités" ont été modifiés pour (ont "conscience" de) être virtualisés, et d'hypervision quand ce n'est pas le cas. Selon les possibilités du matériel, et la solution technique retenue, les noyaux invités pourront fonctionner directement sur le processeur de l'hôte, ou passer via une couche de virtualisation dans le noyau de l'hôte, voire en espace utilisateur. En guise d'exemples, on pourra citer Xen, Qemu, VirtualBox OSE, ou encore UML (je l'ai annoncé : je grossis le trait).

- isolation par conteneur : dans ce cas, on n'a qu'un seul noyau (hôte, forcément), qui va embarquer de quoi isoler une arborescence, dans laquelle fonctionneront des systèmes, dont les ressources (espace disque, arborescence, réseau, matériel spécifique, ...) seront mutuellement isolées. Je parlerai essentiellement de VServer [1] et d'OpenVZ [2], pour ce qui est des solutions que je connais le mieux (j'élude volontairement le cas des chroots, jails et cie, car je ne sais pas dans quelle mesure ça dépend du noyau ; il me semble qu'il y a dans ce dernier de quoi isoler les numéros de processus et des choses du même genre, mais je ne saurais en jurer).




1 - Intérêts

Depuis que je suis sous Etch, j'ai utilisé un noyau modifié avec les patches VServer, supportés par Debian. Concrètement, cela m'a permis d'avoir plusieurs sous-arborescences sous "/var/lib/vservers/", chacune correspondant à une machine virtuelle, avec sa propre adresse IP, ses propres utilisateurs, ses propres paquets, ses propres scripts d'initialisation, ses propres partitions, et ses propres accès à du matériel spécifique (imprimantes, scanner, cartes TV et partitions, pour l'usage que j'en ai).

D'aucuns pourraient me demander : "mais, pourquoi ne pas avoir tout mis sous la même arborescence ?" ... il y a plusieurs raisons à cela :

- maintenabilité : si je fais le con avec l'un de mes conteneurs, je n'ai a priori pas à m'inquiéter pour les autres ; par exemple, l'un des conteneurs est dédié au SqueezeCenter de chez slimdeviceslogitech... et ils ont la mauvaise pratique de ne pas signer leurs paquets : et bien, que foutre ! Seul ce conteneur accepte les paquets non signés de chez eux - je suis beaucoup plus regardant avec les autres conteneurs, en revanche. On pourrait plus globalement citer la ségrégation des ressources, mais encore faudrait-il préciser lesquelles on peut distribuer, et comment, ce que je développe plus loin.

- sécurité : bon, je ne vais pas m'avancer en prétendant que rajouter une couche de complexité est la solution miracle à tout (il y a la poudre verte, pour cela). Cela dit, l'utilisateur "root" dans un conteneur n'est "root" que dans celui-ci, et dans aucun autre (et encore moins "root" sur l'hôte). On pourrait aussi citer les "bind mounts" en lecture seule (bon, là, je triche : ce n'est disponible que, je crois, à partir des noyau 2.6.26, ie à partir de Lenny, et donc, pas sous Etch... mais c'est pour l'exemple) : SqueezeCenter n'a certainement pas besoin d'écrire sur la partition qui contient mes fichiers musicaux, contrairement à mon serveur chrooted-SFTP (pareil, je triche, un peu : disponible à partir, je crois, d'OpenSSH 5.x, soit à partir de Lenny aussi) mais uniquement de les lire. Et bien, les partitions sont, classiquement, montées dans le "/mnt" de l'hôte, puis "bind-mountées" dans les conteneurs, avec les options dont ils ont besoin : pas plus, pas moins. Reste que si quelque chose fait planter l'hôte (par exemple, un problème dans le noyau), tout tombe avec.

- versatilité : si le noyau est unique sur la machine, on peut néanmoins utiliser plusieurs distributions conjointement ; par exemple, Lenny pour une imprimante, mais Etch pour une autre ; voire de la Fedora, du CentOS, de la Mandriva ou que sais-je encore, en guise de conteneurs, sous une arborescence et un noyau hôtes Debian (et combinatoirement). Par contre, pas de conteneur BSD ou d'autres choses qui nécessitent ipso facto un noyau différent. Itout pour les architectures, bien que j'imagine qu'avec un noyau amd64, on puisse avoir des conteneurs x86 (pas testé, puisque je rappelle que mes serveur sont des Athlon-XP).




2 - VServer sous Etch vs OpenVZ sous Lenny

Pour ce qui est de cette partie comparative, je tenais à préciser que je n'ai pas testé VServer sous Lenny, et que je ne sais donc pas dans quelle mesure il aura évolué d'ici là (comptant bien remplacer mon noyau VServer par un noyau OpenVZ sur la machine où j'en ai un) : d'une, par manque de temps (je ne suis pas du tout professionnel de la chose - j'utilise du libre pour mon utilisation personnelle avant tout), de deux, par manque de machines (je pourrais avoir, en plus, un noyau VServer sur la machine où je teste OpenVZ, pour comparer sur la même révision de la distribution, mais reste le problème du temps... dont acte).

J'ai cela dit testé OpenVZ sous Etch (avant le backport, qui est depuis disponible) [3], en utilisant le noyau binaire pour Debian de chez OpenVZ (très peu), et celui de Thorsten Schifferdecker (ce que j'ai arrêté de faire après avoir galéré comme un coing pour trouver une adresse où le contacter, et ne jamais avoir reçu de réponse, une fois essayé, puisque dans l'intervalle, non borné, OpenVZ était arrivé dans Sid, ce qui par la même à fini par résoudre mes problèmes).


2-1) VServer
2-1-1) Déploiement

D'abord, pour ce qui est de VServer, celui-ci est très bien intégré dans Debian grâce aux "vserver-debiantools" (dispo depuis Sarge ! Pour laquelle des backports de noyau VServer sont d'ailleurs disponibles... bon, c'est juste pour appuyer que VServer dans Debian, ça ne date pas d'hier), qui permettent de générer un conteneur, dans "/var/lib/vservers/${NAME}" par exemple, sur un espace LVM préparé à l'avance, via :

newvserver --hostname ${NOM} --domain ${DOMAINE} --ip ${IP} --conffile ${FICHIER_CONF}

où le "conffile" va, notamment, permettre d'installer des paquets lors du déploiement du conteneur, et où l'adresse IP sera l'une des adresses IP de l'hôte (arf...).

2-1-2) Réseau

Oui, parce que ça, c'est quelque chose d'assez ennuyeux, avec VServer : les adresses des conteneurs sont en fait des adresses de l'hôte ; ce qui a pour embêtante conséquence la nécessité de devoir forcer un daemon à se lier à une adresse particulière si on veut le faire tourner sur plusieurs machines virtuelles dans la même machine physique (comme OpenSSH), ou de créer un alias entre localhost et l'adresse déclarée (ici, 192.168.0.2), car le loopback n'existe pas en tant que tel dans le conteneur (à noter que le loopback de l'hôte permet aux conteneurs de dialoguer entre eux). Niveau réseau, c'est donc plutôt limité. On pourrait dire que la pile réseau est davantage isolée que virtualisée.

2-1-3) Limites

Pour ce qui est des limites de mémoire utilisable, on peut gérer ça soit au niveau d'un fichier rlimits "/etc/vservers/name/rlimits", pour la RAM, soit via l'utilisation de l'utilitaire "vdlimit", pour les partitions. Cela dit, VServer n'est pas très exigeant quant à la spécification de ces limites, et, si on oublie de les déclarer, par exemple parce qu'on a largement assez de RAM, ou que les quotas de disque ne nous intéressent pas, vu qu'il est conseillé d'utiliser un volume logique de LVM pour chaque conteneur VServer, d'expérience, ce dernier ne mouftera pas.

2-2-4) Ressources

En cas de besoin, on peut aussi rajouter des capacités sur le noyau aux conteneurs, dans "/etc/vservers/name/bcapabilities", par exemple, "SYS_RAW_TIME", pour qu'un serveur NTP puisse accéder à l'horloge matérielle (autant le faire tourner dans un conteneur plutôt que sur l'hôte, pour lequel, moins il y a de services, moins il est vulnérable, aux plantages, aux failles, aux conneries, ...), ou "MKNOD", si on veut lui permettre de gérer des choses comme une carte TV (auquel cas, toujours selon cet exemple, il faudra faire un "cp -a" du "/dev/dvb" de l'hôte dans l'arborescence du conteneur). Si on devait accéder à /proc/bus/usb depuis le conteneur, on utiliserait la capacité "SYS_RAWIO", et on devrait aussi rajouter, dans "/etc/vservers/nom/fstab" (qui permettra aussi de monter les partitions ou de faire les "bindmounts" que l'on souhaite dans le conteneur), la ligne :

none /proc/bus/usb usbfs defaults 0 0

Pour ce qui est de jeter un œil aux ressources, un classique "top" sur l'hôte ne montrera pas ce qui se passe dans les conteneurs, mais un "vtop", avec les droits "root" montrera l'ensemble des processus et ce qu'ils consomment. De manière analogue, on aura "vps", "vpstree", "vdu", ...

Enfin, l'utilitaire générique "vserver" permet quant à lui d'agir sur les conteneurs à partir de l'hôte ("vserver ${NOM} start", "vserver ${NOM} exec", ...).

Bref, VServer est assez versatile et puissant, mais la simple isolation du réseau, en lieu et place d'une virtualisation plus avancée, est clairement un point faible.


2-2) OpenVZ
2-2-1) Déploiement

Passons maintenant à OpenVZ, fraîchement arrivé dans Lenny, il doit y avoir quelque chose comme deux mois.

L'installation d'un conteneur utilise basiquement un "profil", en fait, une archive compressée d'une arborescence, pour déployer rapidement une machine. Je le dis tout de suite : je n'aime pas du tout ça. A fortiori sur Lenny, qui, malgré la période de "freeze" bouge quand même pas mal, il est difficile de tenir une archive d'arborescence à jour, et je préfère une solution plus manuelle, mais plus efficace à mon sens : se monter un conteneur "approx" [4] (ou assimilé) en premier lieu. Ainsi, via un :

debootstrap --arch ${ARCH} ${DIST} /var/lib/vz/private/${ID} ${MIRROR}

utilisant le serveur "local" de paquets, on obtient un conteneur ("${ID}" est son numéro, 0 étant réservé, et étant conseillé d'assigner les autres aux conteneurs à partir de 1000), à jour, très rapidement, et on peut le modifier ensuite en y entrant via :

"vserver enter ${ID}"

C'est d'ailleurs la méthode qui est derrière l'utilitaire "newvserver" ("debootstrap", j'entends).

2-2-2) Limites

Bon, reste qu'il faut le configurer, ce nouveau serveur "virtuel". Tout d'abord, OpenVZ est très exigeant quant aux limites qu'on fournit à une machine... pas question de lui dire : "démerde-toi avec la RAM et l'espace disque - tu en as large assez". "/etc/vz/conf/" contient, notamment, des fichiers de la forme "${ID}.conf", définissant ce qui est possible dans chaque conteneur, et de la forme "un_nom.conf-sample", qui servent à appliquer une configuration de base aux conteneurs.

L'exhaustivité de ces fichiers peut faire peur, mais heureusement, le fidèle "vzsplit" est là pour nous permettre de confectionner une base adaptée à son matériel. Ainsi, un :

vzsplit -f un_nom -n numves -s swap_size

va créer un fichier "/etc/vz/conf/un_nom.conf-sample" qui va diviser les ressources de l'hôte entre "numves" conteneurs, pour une utilisation totale autorisée de "swap_size" la taille du swap en Kio. Ainsi, je découpe généralement les ressources de la machine en des conteneurs de la taille du plus petit (essentiellement, pour la RAM, ie 48Mo de RAM physique maximum utilisée pour mes plus petits conteneurs) que je compte utiliser, puis, je multiplie les paramètres qui m'intéressent afin de confectionner des "samples" pour les plus grosses machines (par exemple, "Tor" ou "MLDonkey" sont infiniment plus gourmands que "ntpd" ou "approx"). On peut ainsi limiter la mémoire non-swappable, la mémoire partagée, la mémoire physique, ... Chaque paramètre à une barrière (à ne pas dépasser, normalement), et une limite (au delà de la barrière, cette dernière pouvant être exceptionnellement outrepassée, si les ressources de la machine le permettent).

Les paramètres les plus importants pour la gestion de la mémoire sont probablement "oomguarpages" (quantités de pages mémoires de 4Kio utilisées à partir desquelles OpenVZ commencera à tuer les processus si la mémoire de la machine est surchargée : ici, seule la barrière a un sens), "vmguarpages" (barrière, en pages de 4Kio, de mémoire utilisable par le conteneur - la limite n'a pas de sens non plus, mais, si la barrière de "oomguardpages" n'est pas dépassée, le dépassement de la barrière de "vmguardpages" n'entraînera pas d'échec d'allocation, tant que les ressources de la machine le permettront), et "privvmpages" (quantité de pages pouvant être allouées, sans pour autant être nécessairement utilisées).

"diskspace" (en octets) et "diskinodes" permettent également de définir l'espace disque alloué au conteneur dans "/var/lib/vz/private" (il est d'ailleurs conseillé de faire une partition séparée pour ce répertoire, car les quotas de disque OpenVZ peuvent, d'après la documentation, entrer en conflit avec l'espace réservé à l'utilisateur "root", ce qui serait très gênant dans le cas de la partition racine de l'hôte). Je trouve personnellement ce système de quotas plus pratique à gérer qu'un LVM, mais après, rien n'empêche de faire les deux, si on veut. Par contre, les quotas ne marchent que sur la racine principale du conteneur, et pas encore sur les partitions de l'hôte qu'on pourrait vouloir y monter ou y "bindmounter".

Il n'est pas inutile non plus de mentionner "tcpsndbuf", "tcprcvbuf" ou encore "othersockbuf", concernant les buffers tcp et autres, qu'il peut être utile d'augmenter sur une machine très sollicitée sur le réseau. Existant un certain nombre de conditions à remplir, et la documentation d'OpenVZ sur le sujet [5] expliquant tout cela bien mieux que moi, je vous laisse vous y référer au besoin, si ce que fait "vzsplit" ne vous suffit pas, ou qu'il vous laisse perplexe.

Une fois le fichier "/etc/vz/conf/un_nom.conf-sample" créé, on peut l'appliquer au conteneur via :

vzctl set ${ID} --applyconfig un_nom --save

Barrières et limites pourront être surveillées via "/proc/user_beancounters", afin de prévenir d'éventuelles catastrophes, ou de s'assurer que les conteneurs n'ont pas besoin de plus de ressources (par exemple, si des mauvais réglages des limites de mémoires peuvent engendrer de méchants plantages, de mauvais réglages de buffers TCP dégraderont "juste" la disponibilité du conteneur, sans a priori occasionner de "oops").

2-2-3) Réseau

Reste alors à configurer le réseau via, par exemple :

vzctl set ${ID} --ipadd ${IP_ADDRESS} --save
vzctl set ${ID} --nameserver ${DNS_IP_ADDRESS} --save


Dans ce cas, l'adresse IP peut-être choisie avec de grandes latitudes. En effet, dans le cas d'OpenVZ, l'hôte fonctionne comme un routeur ; on n'oubliera donc pas d'activer le routage dans son petit noyau via :

echo 'net.ipv4.ip_forward=1'>/etc/sysctl.d/openvz.conf
sysctl -w net.ipv4.ip_forward=1


ni de déclarer les routes qu'il faut dans le reste de son réseau pour accéder aux conteneurs.

À la base, chaque conteneur dispose d'un loopback, et d'une interface réseau, en point-à-point avec l'hôte, sur un /32, chaque conteneur étant sur son sous-réseau dédié. C'est l'interface réseau de base, qui est nommée "venet" ("virtual network device", ie "périphérique réseau virtuel"). Les adresses ne peuvent cependant être spécifiées qu'à partir de l'hôte, et aucune règle iptables ou table de routage ne peut être spécifiée par le conteneur lui-même.

Mais c'est loin de s'arrêter là. En effet, il existe un autre type d'interface, "veth" (pour "virtual ethernet device", ie "périphérique ethernet virtuel"). La différence étant que "veth" permet d'avoir une carte réseau virtuelle complète, avec adresse MAC, spécification d'adresse IP, de routes, ou encore de règle iptables, à l'intérieur même du conteneur, sans oublier la possibilité de donner le contrôle d'une carte réseau ethernet physique au conteneur (je n'ai pas essayé, mais la documentation stipule que, en ce cas, l'hôte perd le contrôle de la carte réseau), ou de rajouter le "veth" à un "bridge", ie un "switch" logique (à nuancer, toutefois - je n'ai pas essayé non plus, mais il semble qu'il faille une version de "vzctl" supérieure à la 3.0.22, cette dernière étant celle de Lenny).

Niveau fonctionnement, une nouvelle interface (virtuelle, ou physique, si le nom qu'on stipule, pour le côté hôte correspond à une interface existante) apparaît du côté hôte, reliée à celle du conteneur. Pour ce qui et de la syntaxe, on fait ça via :

vzctl set ${ID} --netif_add toto0,00:12:34:56:78:9A,veth101.0,00:12:34:56:78:9B --save

On a alors "toto0" comme nouvelle interface, côté hôte, avec l'adresse MAC qui suit, et "veth101.0", côté conteneur, avec l'adresse MAC qui suit. Les adresses MAC peuvent-être omises, pour être générées automatiquement par OpenVZ, mais dans ce cas, il faut laisser les virgules quand même, de ce que j'ai compris. Ne reste plus qu'à spécifier les adresses IP, comme on le ferait pour n'importe quelle carte ethernet habituelle, par exemple, via le "/etc/network/interfaces", ainsi que les éventuelles routes et cie.

En bref, niveau réseau OpenVZ poutre littéralement. Il paraît même qu'il gère IPv6, mais je n'ai pas encore essayé.

2-2-4) Debianeries

Faute d'un "newopenvz", à-la-"newvserver", et préférant le "bootstrap" aux profils figés et compressés, il est nécessaire de faire quelques petites choses à la main, avant de lancer le conteneur créé et de le personnaliser dans le rôle qui lui est destiné.

Plutôt que de longues explications, je vais juste donner le petit bout d'un de mes scripts, exécuté sur l'hôte Debian, pour un conteneur Debian, et qui permet de faire ça :

sed -i -e '/getty/d' /var/lib/vz/private/${ID}/etc/inittab
rm -f /var/lib/vz/private/${ID}/etc/mtab
ln -s /proc/mounts /var/lib/vz/private/${ID}/etc/mtab
echo 'Aptitude::Recommends-Important "false";'> \
/var/lib/vz/private/${ID}/etc/apt/apt.conf.d/666norecommends
cp /var/lib/vz/private/${ID}/usr/share/zoneinfo/Europe/Paris \
/var/lib/vz/private/${ID}/etc/localtime
vzctl start ${ID}
vzctl exec ${ID} usermod -L root
vzctl exec ${ID} dpkg --purge module-init-tools
vzctl stop ${ID}


On pourra se référer à "/usr/share/doc/vzctl/README.Debian.gz", sur l'hôte, pour plus d'informations.

Ne reste plus qu'à gérer les utilisateurs (un super-admin via sudo, plus d'autres, éventuellement, pour ma part), rajouter ssh, less, et la complétion bash, ainsi qu'installer les locales et les configurer, ce que je fais dans un autre script, une fois à l'intérieur du conteneur, via :

vzctl enter ${ID}

2-2-5) Ressources

L'ajout de points de montage est simple : il suffit de rajouter une ligne appelant la commande mount dans "/etc/vz/conf/${ID}.mount" (qu'il faudra rendre exécutable) pour effectuer le montage au démarrage de la machine. Cela dit, OpenVZ semblant avoir été développé d'abord sur d'autres "Linux" que Debian, il semble que la gestion de "mtab" ne soit pas forcément adaptée à cette dernière distribution ; ce pourquoi je rajoute le paramètre "-n" à "mount", pour éviter des messages agaçants (mais pas problématiques) à l'arrêt (et/ou au démarrage, je ne sais plus) des conteneurs ; soit par exemple:

mount -n --bind /mnt/sur_l_hôte /var/lib/vz/root/${ID}/mnt/dans_le_conteneur

Il est à noter que "/var/lib/vz/root/${ID}" est l'arborescence virtuellement utilisée par un conteneur une fois démarré, contrairement à "/var/lib/vz/private/${ID}", où est physiquement stockée son arborescence, qu'il soit démarré ou pas.

J'ai encore assez peu (dans le sens de la durée) testé l'adjonction de périphériques à un conteneur OpenVZ, mais ce que j'en ai vu m'a satisfait. Là encore, on fait appel à "vzctl", avec deux possibilités. La première est de connaître les numéros majeurs et mineurs des périphériques, ainsi que leur nature "caractère" (c) ou "bloc" (b). Par exemple :

ls -l /dev/usb/lp0

me retourne :

crw-rw-rw- 1 root lp 180, 0 oct 21 22:31 /dev/usb/lp0

Pour l'ajouter au conteneur, on utilise donc "vzctl" :

vzctl set ${ID} --devices c:180,0:rw --save

D'aucuns trouveront ça trop lourd ; heureusement, il existe une autre méthode. Par exemple, pour la même imprimante :

vzctl set ${ID} --devnodes usb/lp0:rw --save

Plusieurs choses sont cela dit à noter (et sont tout aussi valables pour VServer) : tout d'abord, il se peut qu'il y ait besoin de changer les permissions et les propriétaires des "nodes" que l'on rajoute aux conteneurs (comme cela peut arriver sur une machine "classique"), ce que l'on peut faire à l'intérieur même des conteneurs.

En outre, dans un conteneur, en général "udev suce". Plein de problèmes peuvent arriver quand on ne se sert pas d'un "/dev" statique, dans ceux-ci ; outre le fait que "udev" nécessite la capacité noyau "raw_sysfs" (d'ailleurs, pour ça, on ferait "vzctl set ${ID} --capability raw_sysfs:on", sous OpenVZ... enfin, pour imager : "raw_sysfs" n'est justement pas disponible sous OpenVZ, contrairement à VServer), il est en général assez mal géré, et risque d'écraser des périphériques créés au lancement du conteneur (tty, ou encore random, en guise d'exemple de choses indispensables). Si, des fois, particulièrement pour les périphériques qui exigent le chargement d'un firmware, "udev" est presque indispensable, ça fait partie des choses que je fais sur l'hôte. Par exemple, pour mon imprimante laser "HP Laser Jet 1018", qui requière le chargement d'un firmware, j'installe "foo2zjs" (qui fournit le script "udev" de chargement du firmware au branchement de l'imprimante... par flemme de ne copier que lui), sans "CUPS" (dont une instance tourne, à la place, dans un conteneur, qui ne s'occupe pas du chargement du microcode), et je copie le firmware, sur l'hôte, que je laisse gérer le branchement à chaud de la bête. Ça m'évite de devoir donner des capacités inutiles au conteneur, et de charcuter le script de post-installation de "udev" dans celui-ci (j'y étais arrivé sous Etch, mais sous Lenny, il bloque à la population de "/dev" lors de l'installation, même après un peu de charcutage), sans compter les problèmes d'interférence entre la création de périphériques par les scripts de démarrage de conteneur, et "udev", qui peut écraser ce qu'ont fait les-dits scripts (et là, c'est la cata).

Une note sur "saned", pour les scanners : dans Lenny, "libsane" est capable de dépendre de "makedev" en lieu et place de "udev"... sauf que "libsane" dépend de "libsane-extras", qui ne peut pas dépendre de "makedev"... et ça, c'est la loose. Heureusement, le bug [6] est en train d'être corrigé (il l'est déjà pour amd64, dans Sid ; plus qu'à attendre). Par contre, sur ma machine, il n'y a pas de "/dev/usb/scanner0" de créé ; et donc, on peut être tenté de se servir de "/proc/bus/usb", qu'il est malheureusement impossible de monter dans un conteneur OpenVZ (alors que sous VServer, ça roulait nickel). Pas grave : "/dev/bus/usb/..." sert à la même chose, et suffit à faire marcher la bête (en prime, en l'activant dans le conteneur, "lsusb" se met à y marcher, en ne montrant que ce à quoi on lui a donné accès) ;

Par contre, il faut quand même faire un peu gaffe avec les noms de périphériques qu'on donne au conteneur, car ils ont tendance à être donnés un peu au hasard, en fonction de l'ordre de branchement, voire, de la direction du vent. Pour éviter les problèmes inhérents à l'utilisation de plusieurs périphériques sur le même bus, dans des conteneurs différents, on pourra, sur l'hôte, rajouter des règles "udev" qui créeront des alias personnalisés, qui ne laisseront pas le moindre doute, et partager ces alias. Avec l'avenir laissant imaginer une omniprésence de "udev" et la disparition totale des "vieilles" alternatives comme "makedev" on peut néanmoins espérer qu'OpenVZ et les solutions de conteneurs viendront à avoir moins de problèmes avec les "/dev" dynamiques.

Quelques derniers petits mots sur OpenVZ : autant je n'ai eu aucun problème sous VServer avec les terminaux, via l'utilisation de "vserver ${ID} exec", autant avec "vzctl exec ${ID}", c'est la cata pour "ncurses" (dommage, "dpkg"), ou pour les trucs interactifs comme "adduser" (mignon, le mot de passe de l'utilisateur que je crée, et qui apparaît en clair... mais, comment dire ?) ; bref, "vzctl exec", pour l'instant, sous Debian, je ne saurais que trop déconseiller de l'utiliser : tant que possible. Personnellement, je "debootstrap", puis je "vzctl enter ${ID}", puis j'y lance un petit script-maison qui modifie la machine selon mes besoins (en installant ce qu'il faut pour la joindre via "ssh" par la suite), et zou.

On pourrait aussi évoquer qu'un simple "top" sur l'hôte permet de voir les processus des invités (les numéros de processus ne sont pas entièrement virtualisés, bien que les conteneurs voient quand même des choses comme leur processus "init" avec le numéro 1, soit, comme il faut que ce soit).

Je serais également malhonnête de passer sous silence un vilain bug dans la série 2.6.26 du noyau [7], sous Debian (mais probablement d'autres, puisque le bug a été considéré comme venant "d'upstream" par les devs OpenVZ, donc, de Linux Vanilla - résolu dans 2.6.27, et backporté dans 2.6.26-9, sous Debian, depuis samedi matin dans Sid), et qui a la fâcheuse tendance de faire un oops jusqu'au blocage de clavier, si on fait trop intensivement utilisation du réseau... Enfin, comme je l'ai dit, c'est résolu dans Sid, et ça devrait arriver "rapidement" dans Lenny.




3) Derniers mots (si-si : promis)

Après un an et demi de Vserver et 6 mois d'OpenVZ, je dois admettre que les conteneurs sont une solution de choix, en ce qui me concerne : pas besoin de processeurs doués d'instructions spéciales pour virtualiser (ce qui permet de recycler les vieilles machines en serveurs - néanmoins, prévoir pas mal de RAM : mes deux serveurs en ont respectivement 2Go et 3Go, soit le maximum que leurs cartes mères respectives peuvent embarquer, ce qui me permet de gérer très confortablement quelques dizaines de conteneurs ; mais, c'est probablement la ressource à laquelle je dois faire le plus attention), très bonnes performances, et beaucoup des avantages possibles de la ségrégation de tâches.

Cela dit, si je devais brièvement comparer VServer et OpenVZ, plus spécialement dans Debian, je dirais que le premier est bien intégré, robuste, pas prise de tête, mais vraiment limité au niveau du réseau, d'avantage isolé que virtualisé, ce qui peut être ennuyeux pour un serveur, dont la connectivité à d'autres machines est peut-être sa caractéristique essentielle ;

Pour le deuxième, je dirais qu'il est jeune (sous Debian), avec la rugosité occasionnelle que ça implique, mais extrêmement puissant, tant au niveau du réseau ("venet" et "veth"), que de la gestion des ressources ("beancounters", barrières et limites). OpenVZ, pendant libre de la suite de virtualisation Virtuozzo (dont je ne connais en tant que telle, pour ainsi dire, rien) permet quelques autres mignoneries, comme la migration en direct (ie sans coupure de disponibilité) d'un conteneur vers un hôte sur une autre machine physique, mais bon : on ne peut pas avoir besoin de tout, et donc, pas tout tester.

Dès que j'aurai le temps, je compte donc, en avance, passer mon VServer Etch (CUPS, MythBackend, SFTP, NFS [ah oui : là, par contre, c'est uniquement en espace utilisateur, donc, via unfsd, pour le moment, ce qui peut être gênant, vu qu'il ne gère pas les "locks", ie "verrous"], SqueezeCenter, NTP et j'en passe) en Lenny (la manière de passer de VServer à OpenVZ est documentée [8], et pour ceux que ça intéresse, un noyau binaire OpenVZ est "backporté" dans Etch), et sous OpenVZ, qui m'a véritablement, je dois bien le dire, conquis.

J'ai l'impression qu'on a plus souvent tendance à parler des solutions de virtualisation avec noyaux "invités", comme Xen ou Qemu, mais bon, je poste ça sur LinuxFr, et, justement, les conteneurs permettent de faire tourner plein-plein-plein de Linux, avec moins de complexité et, souvent, de contraintes, qu'en faisant tourner plein-plein-plein de noyaux. Aussi, n'hésitez pas à donner une chance à OpenVZ (ou VServer, qui est quand même 'achement bien), d'autant que sa doc en PDF [9], son wiki [10] et son forum [11] sont vraiment géniaux ; d'autant que ses développeurs sont très réactifs, et prompts à donner un coup de main. En comparaison, je dois admettre trouver la documentation de VServer [12] [13] peut-être un peu plus légère. Enfin bref, OpenVZ, et plus généralement les conteneurs, ça roxxxe !




[1] http://linux-vserver.org/Welcome_to_Linux-VServer.org
[2] http://wiki.openvz.org/Main_Page
[3] http://wiki.openvz.org/Installation_on_Debian
[4] http://packages.debian.org/lenny/approx
[5] http://wiki.openvz.org/UBC_parameters
[6] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=493698
[7] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=500963
[8] http://wiki.openvz.org/Migration_from_Linux-VServer_to_OpenV(...)
[9] http://download.openvz.org/doc/OpenVZ-Users-Guide.pdf
[10] http://wiki.openvz.org/Main_Page
[11] http://forum.openvz.org/
[12] http://linux-vserver.org/Documentation
[13] http://linux-vserver.org/util-vserver:Documentation
  • # vserver sur lenny

    Posté par (page perso) . Évalué à 4.

    Tu précises bien que tu n'as pas testé vserver sur Lenny. Voici la manipulation pour avoir un vserver opérationnel, y compris avec le loopback en source (une connexion vers l'ip de la machine aura comme source localhost) et en destination (un bind sur 127.0.0.1 ne bindera que sur le loopback et pas sur toutes les interfaces):

    newvserver --hostname ${NOM} --domain ${DOMAINE} --ip ${IP} --conffile ${FICHIER_CONF}

    pour avoir un loopback complet:
    echo "~SINGLE_IP" > /etc/vservers/${NOM}/nflags

    c'est pret! reste plus qu'à lancer:
    vserver ${NOM} start

    vserver reste quand même la solution de virtualisation (via containers) la plus aisée de nos jours.

    Une petite précision concernant ta remarque suivante:
    les adresses des conteneurs sont en fait des adresses de l'hôte ; ce qui a pour embêtante conséquence la nécessité de devoir forcer un daemon à se lier à une adresse particulière si on veut le faire tourner sur plusieurs machines virtuelles dans la même machine physique

    Si je ne me trompe pas, ce problème de bind ne se pose que pour les daemons tournant sur la machine hôte. En effet les interfaces des vservers ne sont pas des alias des interfaces physiques de la machine (mais plutôt des liens ip dans le sens de ip link et ip addr) et donc il n'y a aucun problème pour faire tourner une 10aine de serveurs web et autant de serveurs ssh sur la même machine physique tant que tu affectes une IP différentes à chaque vserver.
    • [^] # Re: vserver sur lenny

      Posté par . Évalué à 4.

      Sympa, pour le loopback...

      Sinon, oui, en effet, je me suis sans doute mal exprimé pour les liens IP... mais j'ai trouvé ça un peu saoulant pour SSH et Puppet : de ce côté, je préfère la manière d'OpenVZ, avec l'hôte qui devient un routeur.
  • # Bravo

    Posté par (page perso) . Évalué à 2.

    Bravo pour le travail, c'est super intéressant.
    Je vais lire ça avec attention!
    • [^] # Re: Bravo

      Posté par . Évalué à 2.

      Merci : résumer ce que j'avais appris sur les conteneurs est quelque chose que je voulais faire depuis un moment, mais je voulais prendre le temps...

      ... en tout cas, c'est la moindre des choses que je dois à la communauté libre, donc les diverses documentations (par exemple le merveilleux wiki Gentoo... down, en ce moment, tout comme l'excellent gentoo-portage, apparemment à cause d'un hébergeur qui ne paie pas le datacenter... faudrait d'ailleurs faire un journal là-dessus : ça pue grave de chez grave) m'ont appris tout ce que je sais sur le libre (bah oui : formation de physicien, à part "donc, là, tu commences par pirater matlab", malheureusement, la formation sur le libre...).
  • # L'accès au réseau? La sécurité?

    Posté par . Évalué à 2.

    Ton journal est super intéressant; en plus je compte bien me mettre aux vservers d'ici pas longtemps, donc je garde toutes ces infos.

    Ceci dit, j'ai deux questions.
    Puisque finalement, tous les invités accèdent au réseau, ils ont donc tous accès au cable physique.
    Si je fais un tcpdump dans un des invités, est-ce que je vois le trafic destiné aux autres invités?

    Ensuite, concernant la sécurité. Qu'est ce qui te garantit qu'en étant root sur un invité, tu ne puisse pas basculer root sur la machine principale, ou sur un autre invité?
    • [^] # Re: L'accès au réseau? La sécurité?

      Posté par . Évalué à 3.

      - Je ne parle que de Virtuozzo -

      * Niveau réseau :
      En venet, comme c'est du point à point, aucun soucis, tu ne reçois que ton trafic.

      En veth, les bridges se comportent comme des switchs, donc la encore tu ne reçois que ton trafic (sauf si tu change ton adresse IP, mais bon...)

      * Niveau sécurité :
      Parce que c'est prévu pour. C'est conçu de façon a ce que tu ne puisse pas sortir du conteneur. Bien sûr, on n'est jamais à l'abri d'une faille de sécurité, mais ça reste une faille (donc un défaut, pas une volonté)

      Sinon, il y a quelques commandes de raccourcis type vztop et vzps. Avec le paramètre -E {ID}, ça permet de ne voir que les processus d'un conteneur.
    • [^] # Re: L'accès au réseau? La sécurité?

      Posté par . Évalué à 3.

      Sous VServer, il me semble que tu ne devrais rien voir non plus (je me demande même si tcpdump fonctionne normalement, dans un VServer basique), à moins d'ajouter des capacités comme "net_raw" ou "net_admin" - je ne sais plus laquelle permet quoi, mais il me semble, de lointaine mémoire (je ne fais plus tourner que des choses "locales", sur mes VServer, ayant passé la machine qui fait des trucs avec le reste du Web en machine de test, sous OpenVZ), que l'une va te permettre de surveiller ton conteneur uniquement, et l'autre, la machine physique...

      ... cela dit, gaffe avec les capacités noyau : plus on en donne, moins il y a, justement, d'isolation.



      Pour l'isolation du root, même réponse que Romain : parce que c'est prévu pour... ce qui ne prémunit contre aucune faille non plus...

      D'ailleurs, des fois, un gros plantin sur un conteneur peut lourder toute la machine : exemple, l'installation de "gnunet" dans un conteneur OpenVZ sous Lenny... la dernière fois que j'ai essayé, pas de oops, ni de choses dans ce genre, mais quand même un gros blocage de la machine (les led du clavier marchaient encore... bizarre) : même plus de SSH...

      Je subodore un bouffage de tout le proc, par le script d'install qui tourne avec les droits root, ou quelque chose du genre, mais il faut que je prenne le temps de tester ça (depuis, le conteneur où je fais mes tests a une priorité assez faible au niveau des cycles CPU), et de bugreporter si ça continuait.
  • # Très bon retour d'experience

    Posté par (page perso) . Évalué à 2.

    Bravo pour ce journal,

    je voulais depuis quelques jours me mettre à OpenVZ, merci pour ces infos, cela va me donner un bon début pour commencer.

    Deux petites questions cependant:
    - Pour les mises à jour de tes conteneurs ("aptitute update && etc"), tu as développé des scripts pour automatiser tout cela ?
    - Tu as des conteneurs qui font tourner des services très simples, comme par exemple NTPD. Quelle est la taille approximative sur le disque dur que cela prend ?
    • [^] # Re: Très bon retour d'experience

      Posté par . Évalué à 2.

      > - Pour les mises à jour de tes conteneurs ("aptitute update && etc"), tu as développé des scripts pour automatiser tout cela ?

      "cron-apt", en personnalisant un peu les fichiers pour utiliser "aptitude" au lieu de "apt", et faire un petit "autoclean", pour éviter de surcharger le "/var" ; les paquets à mettre à jour sont donc automatiquement sur la machine virtuelle (bon, en même temps, pour l'instant, il n'y a qu'une machine physique OpenVZ, donc, même en direct, ça va de disque dur à disque dur, même si seul l'hôte en a "conscience" ; l'arbre partiel est monté sur lui, sur une partition dédiée, puis "bind-mounté" en "rw" dans le conteneur "approx") : plus qu'un "safe-upgrade" voire un "full-upgrade" à faire (ça, par contre, j'aime bien faire à la main ; quoique j'investigue pour faire ça via des triggers, manuels mais centralisés, dans Puppet, pour ne pas avoir à ouvrir 36000 shells via SSH - bon, à la limite, ce qui vient de security.debian.org, je pourrais le faire passer automatiquement via "cron-apt", mais même ça, je me méfie).

      Il ne m'envoie pas encore de mail, puisque j'attends que le bug sur le noyau 2.6.26-8 dont je parle soit corrigé dans Lenny (par flemme de mettre celui qui est déjà dans Sid), pour m'occuper de leur gestion ; mais c'est prévu et a déjà été testé (probablement "exim-daemon-light" dans les conteneurs, ainsi qu'un "exim-daemon-heavy" et un "dovecot-imapd", centraux).



      > Tu as des conteneurs qui font tourner des services très simples, comme par exemple NTPD. Quelle est la taille approximative sur le disque dur que cela prend ?

      Pour le conteneur NTP (le "gros" "ntp", pas "openntpd"), par exemple :

      $ du -h
      ...
      270M /


      après un debootstrap sans rien de particulier, soit seulement les paquets tagués "essential", soit ce que donne un "aptitude search ~prequired ~pimportant" (quelques uns, comme module-init-tools, sont superflus dans un conteneur), mais pas les "~pstandard" (de ceux-là, je ne fais pour l'instant venir que "openssh-client", "locales", "less" et "bash-completion" ; un petit "mutt" et un "exim4-daemon-light" ne seraient peut-être pas complètement idiots) ; et bien sûr ntp, ainsi que deux ou trois machins propres à mes habitudes d'administration (sudo, puppet, apt-show-version, ...).

      C'est cela dit peu ou prou le minimum : le serveur chrooted-SFTP, qui doit vraiment être mon plus petit, hors "bind-mounts", fait 269Mio. Vu que je peux faire tourner une cinquantaine de machines de 48Mo de RAM (le minimum, arbitraire et empirique, bien que largement confortable, que je mette... bon, c'est une base : pour les trucs comme MLDonkey ou Tor, je n'hésite pas à mettre 240Mo ou 120Mo), avec mes 3Go de RAM (2,5Go dispatchés dans les "oomguardpages"), avec un "/var/lib/vz" d'environ 200Gio (4Gio par conteneur), niveau espace disque, je suis très-très-très large.
  • # Petit complément

    Posté par . Évalué à 2.

    En ce qui concerne les périphériques branchables à chaud (je vais reprendre l'exemple du scanner), il peut y avoir un petit problème.

    En cas de débranchement/rebranchement, sur l'hôte, le périphérique peut changer de numéros d'identification, au moins mineur.

    Même si on utilise "--devnodes" au lieu de "--devices" avec vzctl, pour "démasquer" ce périphérique, si le nœud est déjà créé dans le conteneur, même en redémarrant ce dernier, il ne sera pas recréé... et puis, c'est moche de redémarrer un conteneur juste pour ça...

    J'ai donc fait un petit script pour effacer les nœuds d'un conteneur, et les réassigner :

    #!/bin/bash
    #
    # Script to remove DEVNODES from an OpenVZ VE and add them back then.
    #
    # Released under the WTFPL (http://sam.zoy.org/wtfpl/COPYING)
    #
    # Made to be run by a "+RUN" udev stanza on the HN ; let us call
    # this a "hack-ish fractal udev for the VE, running on the HN" :p
    #
    # udev is generally ran as root, so the script should then have
    # the permissions it needs
    #
    # It is a generic script : it picks the ID of the VE
    # from its own name, ie "${ID}.dev"
    #
    # Particularly useful for hotplugable devices that may change of
    # major/minor numbers on unplug/replug
    #
    # There is no need to restart the VE with this one



    # This variable is going to be used in this script
    # You should not use spaces in nodes names, as they are treated
    # as separators... but you should not be able to do so in VE conf
    # files anyway...
    unset DEVNODES

    # The ID of the VE, recovered from the name of this file
    ID=`basename $0|cut -d. -f1`

    # Checks the VE configuration, so to pick the DEVNODES variable
    source /etc/vz/conf/${ID}.conf

    # If there are DEVNODES, remove those from the VE's private directory
    # and add them back
    if [[ ! -z "${DEVNODES}" ]]
    then
    for i in "${DEVNODES}"
    do
    if [[ -e "/var/lib/vz/private/${ID}/dev/`echo $i|cut -d: -f1`" ]]
    then
    rm "/var/lib/vz/private/${ID}/dev/`echo $i|cut -d: -f1`"
    vzctl set ${ID} --devnodes $i --save
    fi
    done
    fi


    Veillez juste à le rendre exécutable, et à lui donner un nom correspondant à l'ID du conteneur ; j'ai pris ".dev" pour l'extension, mais a priori, l'extension, le script s'en fout.

    Ensuite, j'inclus ce script dans la règle udev qui me permet de donner un symlink constant à mon scanner, par exemple :

    ATTRS{idVendor}=="04b8", ATTRS{idProduct}=="011f", SYMLINK+="containers_dev/scanner_1670", RUN+="/etc/vz/conf/1500.dev"

    Et ainsi, si je débranche la bête, dès qu'elle est de nouveau détectée, le nœud matériel accessible au conteneur est mis à jour, que celui-ci soit en train de fonctionner ou pas (dans ce cas, le nœud est juste effacé, et sera rajouté au conteneur dès son prochain démarrage).

    Sinon, dernière astuce, si vous voulez regrouper les périphériques pour les conteneurs dans un sous-répertoire particulier de "/dev", comme je le fais avec mon "/dev/containers_dev", sachez que ça ne suffira pas pour les logiciels comme "saned", qui recherchent "/dev/bus/usb", à moins de directement partager ce qu'il y a dans ce dernier, et pas un simple "symlink" (même en le spécifiant dans "/etc/saned/snapscan.conf", par exemple, dans mon cas).

    Ainsi, sur l'hôte, si avec ma règle "udev", "/dev/containers_dev/epson_1670" n'est qu'un symlink vers, en ce moment (mais ça change au moindre débranchement/rebranchement), "/dev/bus/usb/003/017", en partageant "/dev/containers_dev/epson_1670", ie le "symlink", ça ne suffit pas au conteneur pour voir ce qu'il faut. Pas grave - dans le conteneur, il suffit de faire un :

    ln -s /dev/containers_dev/epson_1670 /dev/bus/usb/001/001"

    en créant à la main le répertoire "/dev/bus/usb/001", et "saned" n'y verra que du feu (par contre, ça ne suffit pas à faire marcher "lsusb" : il lui faut le vrai "/dev/bus/usb/...", et pas un "symlink" avec des répertoires, dans le conteneur).
    • [^] # Re: Petit complément

      Posté par . Évalué à 2.

      Il y a juste un petit souci avec mon script : si on a changé les permissions et les propriétaires des nœuds de périphériques, ils seront réinitialisés...

      ... je n'ai pas encore fini de modifier le script pour prendre ça en compte, mais bon, au pire, ce n'est pas très compliqué comme modification : trivial dans le cas où le conteneur fonctionne, et au pire, ça peut se mettre dans un script d'init pour le cas où le conteneur est arrêté quand le périphérique est branché/débranché...

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.