<metaname="viewport"content="width=device-width, initial-scale=1, user-scalable=no"><title>Mise en place de conteneurs systemd-nspawn - YannStatic</title>
<divclass="col-main cell cell--auto"><!-- start custom main top snippet --><divid="results-container"class="search-result js-search-result"></div><!-- end custom main top snippet -->
<articleitemscopeitemtype="http://schema.org/Article"><divclass="article__header"><header><h1style="color:Tomato;">Mise en place de conteneurs systemd-nspawn</h1></header></div><metaitemprop="headline"content="Mise en place de conteneurs systemd-nspawn"><divclass="article__info clearfix"><ulclass="left-col menu"><li>
<p><em>Depuis un certain temps déjà, les conteneurs font l’objet d’un grand intérêt. Souvent considérés comme des “VM light” (virtual machine light), ils permettent aux utilisateurs d’exécuter des services dans des environnements dédiés, en les isolant les uns des autres et du système hôte.<br/>
Comment faire fonctionner un simple conteneur en utilisant les outils fournis par systemd.</em></p>
<h3id="comment-les-conteneurs-fonctionnent-ils-">Comment les conteneurs fonctionnent-ils ?</h3>
<p>La meilleure façon d’imaginer ce que font les conteneurs est de penser en termes d’espaces de noms. Il existe plusieurs espaces de noms dans votre système auxquels vous pouvez restreindre l’accès. Examinons certains d’entre eux :</p>
<ul>
<li><strong>Système de fichiers</strong>. Une des étapes les plus importantes pour isoler un service est de restreindre l’accès au système de fichiers. Les conteneurs fournissent une fonctionnalité de type <strong>“chroot”</strong>, vous permettant de spécifier une racine de système de fichiers arbitraire. Cela signifie que vous pouvez décider quels programmes vous voulez installer dans votre conteneur de façon totalement indépendante du système hôte. Cependant, cela signifie également que vous devez mettre en place une seconde arborescence de système d’exploitation pour le conteneur contenant tous les outils dont un système d’exploitation a besoin pour fonctionner. Mais c’est facile, comme nous le verrons plus tard. En fin de compte, vous aurez une hiérarchie de système de fichiers Unix typique à l’intérieur de votre répertoire racine spécifié, contenant /bin, /etc, /home, /usr et ainsi de suite.</li>
<li><strong>Les ID de processus (PID)</strong>. Lorsque vous exécutez <codeclass="language-plaintext highlighter-rouge">ps -ef</code>, vous voyez chaque processus s’exécuter sur le système, avec leurs PID uniques. Cependant, nous ne voulons pas que quelque chose s’exécute dans le conteneur pour voir les processus de l’hôte ou d’autres conteneurs. En outre, nous voulons qu’un processus dans le conteneur puisse avoir par exemple le PID 1 (init), même si nous avons déjà un PID 1 sur l’hôte.</li>
<li><strong>Les ID utilisateur (UID)</strong>. Chaque utilisateur d’un système possède un ID utilisateur. L’utilisateur root a généralement l’ID 0, alors que les ID utilisateur normaux commencent à 1000. L’espacement des noms d’utilisateur est surtout une précaution ; si quelqu’un est en mesure de pénétrer dans le système hôte à partir de votre conteneur, il n’aura pas l’UID d’un utilisateur existant sur l’hôte. Sans espacement des noms d’utilisateur, par exemple, l’utilisateur root dans un conteneur apparaîtra également dans le système hôte sous l’UID 0. L’activation de cette fonctionnalité ajoute un nombre arbitraire élevé à tous les UID à l’intérieur d’un conteneur, de sorte que l’utilisateur root du conteneur (apparaissant comme UID 0 dans le conteneur) aura en fait l’UID 64789232 dans le système hôte (par exemple).</li>
<li><strong>Interfaces réseau</strong>. L’exécution de <codeclass="language-plaintext highlighter-rouge">ip addr</code> sur l’hôte vous montre toutes les interfaces réseau disponibles sur votre machine. Exécuter la même commande dans un conteneur vous donnera exactement le même résultat, ce qui signifie que le conteneur a les mêmes privilèges d’accès au réseau que l’hôte. Pour éviter cela, nous pouvons restreindre l’accès du conteneur aux interfaces réseau de l’hôte et ajouter un pont pour le conteneur afin de lui permettre de continuer à accéder à l’internet et au réseau local.</li>
</ul>
<blockquote>
<p>Tous les espaces de noms sont gérés par le noyau Linux. Il est possible d’activer ou de désactiver le support de ces espaces de noms au moment de la compilation du noyau. Si certains d’entre eux ne fonctionnent pas pour vous, peut-être ont-ils été désactivés ou votre noyau est trop vieux.</p>
</blockquote>
<blockquote>
<p><strong>Note sur les conteneurs à traitement unique</strong><br/>
Certaines personnes de la communauté Docker font la promotion du modèle “un processus par conteneur”, ce qui signifie que chaque conteneur que vous mettez en place est destiné à exécuter exactement un processus à l’intérieur de celui-ci. Cependant, comme le suggère également cet article, il est bon d’avoir au moins un système d’initialisation minimal dans votre conteneur. Le problème est que les processus peuvent bifurquer() des processus enfants, et lorsqu’un parent n’attend pas correctement() ses enfants, le processus avec le PID 1 (le processus init) devient responsable du devoir de nettoyage. Mais s’il n’y a pas de processus init, ces processus enfants orphelins deviendront des zombies. Certains processus s’appuient sur l’init comme faucheur de zombies, par exemple les démons lors de la double bifurcation. Dans ce guide, nous aurons systématisé le système init à l’intérieur de notre conteneur.</p>
</blockquote>
<h2id="créer-un-conteneur-étape-par-étape">Créer un conteneur étape par étape</h2>
<p>Je montrerai comment créer des conteneurs sur Debian, mais les étapes devraient être similaires pour les autres distributions Linux. Notez que vous avez besoin des privilèges de root pour exécuter les commandes des sections suivantes.</p>
<h3id="installation-des-paquets-nécessaires">Installation des paquets nécessaires</h3>
<p>Nous avons besoin de quelques paquets pour installer et faire fonctionner le conteneur.</p>
<p><em>debootstrap</em> installe un système Debian minimal dans un répertoire personnalisé.<br/>
<em>systemd-container</em> contient les outils systemd pour exécuter et configurer les conteneurs.<br/>
<em>bridge-utils</em> permet de configurer facilement un pont pour donner au conteneur un accès au réseau.</p>
<h3id="configurer-larborescence-du-système-dexploitation">Configurer l’arborescence du système d’exploitation</h3>
<p>Tout d’abord, nous devons mettre en place une arborescence d’OS dans un répertoire vide, qui servira de répertoire racine au conteneur. Pour ce faire, nous utiliserons <em>debootstrap</em>.<br/>
systemd s’attend à ce que les conteneurs soient situés dans le répertoire <strong>/var/lib/machines</strong>. Ils peuvent se trouver ailleurs, mais certains outils ne reconnaîtront pas automatiquement le conteneur. Mettons en place un conteneur nommé <em>helloworld</em> :</p>
<p>Cela permettra d’installer tout ce qui est nécessaire pour faire fonctionner Debian dans notre nouveau conteneur (cela peut prendre un certain temps).<br/>
Vous devez également vous assurer que seul root peut accéder au répertoire des machines :</p>
<h3id="un-aperçu-des-commandes-systemd-de-conteneurs">Un aperçu des commandes Systemd de conteneurs</h3>
<p>Maintenant que nous avons mis en place le système d’exploitation, c’est le bon moment pour introduire les commandes que vous utiliserez pour contrôler votre conteneur.<br/>
<codeclass="language-plaintext highlighter-rouge">systemd-nspawn</code> est utilisé pour démarrer directement un conteneur. Par défaut, il se contente d’engendrer un shell racine dans l’environnement du conteneur. L’utilisation du commutateur <codeclass="language-plaintext highlighter-rouge">-b</code> permet de démarrer le conteneur et de vous donner un shell de connexion. Cependant, dès que la commande se termine, le conteneur s’éteint également. Aussi, lorsque vous voulez utiliser l’espace de nommage utilisateur, je recommande fortement de toujours le lancer avec le commutateur <codeclass="language-plaintext highlighter-rouge">-U</code>, qui permet l’espace de nommage utilisateur. La première fois que vous faites cela, systemd ajustera toutes les permissions de fichiers à l’intérieur du conteneur, et désactiver l’espace de nommage utilisateur plus tard peut conduire à des erreurs très bizarres (et à des fichiers appartenant à nobody:nogroup). Enfin, avec le commutateur <codeclass="language-plaintext highlighter-rouge">-M nom_machine</code>, vous spécifiez le conteneur à lancer.<br/>
<codeclass="language-plaintext highlighter-rouge">machinectl</code> est très similaire au <codeclass="language-plaintext highlighter-rouge">systemctl</code> de systemd, mais est utilisé spécifiquement pour travailler avec des conteneurs. En fait, les conteneurs peuvent être lancés, arrêtés et activés comme n’importe quel autre service systemd. Le fichier modèle du service se trouve dans <strong>/lib/systemd/system/systemd-nspawn@.service</strong>.<br/>
Il vaut la peine d’y jeter un coup d’oeil, en particulier l’exécutable spécifié :</p>
<p>Comme vous pouvez le voir, il y a déjà un certain nombre d’arguments de ligne de commande spécifiés par défaut lorsque vous utilisez <codeclass="language-plaintext highlighter-rouge">machinectl</code> pour démarrer un conteneur. L’un d’entre eux est <codeclass="language-plaintext highlighter-rouge">--boot</code> (comme <codeclass="language-plaintext highlighter-rouge">-b</code>), de sorte que le conteneur démarre et ne se contente pas d’engendrer un shell root. <codeclass="language-plaintext highlighter-rouge">--network-veth</code> crée une interface ethernet virtuelle (sans passerelle) dans le conteneur. Notez que <codeclass="language-plaintext highlighter-rouge">-U</code> est spécifié, donc l’espacement des noms des utilisateurs sera activé.<br/>
<strong>/etc/systemd/nspawn</strong> est un répertoire qui peut contenir des fichiers de configuration pour les paramètres spécifiques au conteneur. Tous les paramètres peuvent également être spécifiés via la ligne de commande <codeclass="language-plaintext highlighter-rouge">systemd-nspawn</code>, mais cela vous évite de les taper à chaque fois. En outre, les fichiers de configuration sont également honorés lorsque vous utilisez <codeclass="language-plaintext highlighter-rouge">machinectl</code> pour démarrer les conteneurs.<br/>
Si le répertoire n’existe pas, il suffit de le créer en utilisant</p>
<h3id="configuration-du-conteneur">Configuration du conteneur</h3>
<p>Maintenant, nous avons mis en place une arborescence de systèmes d’exploitation et vous avez des connaissances de base sur les outils de Systemd. <br/>
La première chose que nous faisons est de définir un mot de passe root, nous obtiendrons un shell root sans avoir à démarrer le conteneur :</p>
<p>Vous devriez voir les messages habituels de démarrage du système. Si tout fonctionne, après quelques secondes, vous devriez voir un shell de connexion. Connectez-vous avec root et le mot de passe que vous venez de définir.<br/>
Ensuite, je vous recommande d’installer dbus. Il est nécessaire si vous voulez vous connecter à des conteneurs en cours d’exécution en utilisant <codeclass="language-plaintext highlighter-rouge">machinectl</code> sur l’hôte.</p>
<p>Pour l’instant, le conteneur devrait voir les mêmes interfaces réseau que l’hôte. Vous pouvez utiliser <codeclass="language-plaintext highlighter-rouge">ip addr</code> pour vérifier cela. Nous mettrons en place un réseau Ethernet virtuel dans la prochaine section. <br/>
Cependant, nous pouvons déjà dire au conteneur d’afficher automatiquement l’interface ethernet virtuelle :</p>
<p>Vous devez également changer le nom d’hôte du conteneur, sinon il aura le même nom que l’hôte. Je l’appellerai <strong>helloworld</strong>, mais vous pouvez utiliser ce que vous voulez :</p>
<h3id="configuration-de-lhôte">Configuration de l’hôte</h3>
<p>Maintenant, essayons de mettre en place un réseau pour le conteneur. Pour y parvenir, nous devons d’abord configurer le système hôte de manière appropriée.</p>
<p>Dans ce guide, nous allons mettre en place un pont réseau sur l’hôte et connecter l’interface ethernet virtuelle du conteneur au pont. Cela signifie que l’hôte et le conteneur seront visibles sur le réseau en tant que deux machines différentes. Si vous connectez votre ordinateur à un routeur, celui-ci attribuera une adresse IP distincte au conteneur.</p>
<blockquote>
<p>Notez que cette méthode ne fonctionnera probablement pas pour vous si vous êtes sur un serveur avec une adresse IP statique. Dans ce cas, vous devrez mettre en place une NAT sur votre hôte ou une sorte de proxy, qui ne sera pas traitée ici.</p>
</blockquote>
<p>Tout d’abord, disons à systemd que notre conteneur utilisera désormais un pont réseau. Cette fonction est prise en charge directement par systemd ; il se chargera de connecter l’ethernet virtuel du conteneur à l’interface de la passerelle de l’hôte au démarrage du conteneur. Pour configurer cela, ouvrez <strong>/etc/systemd/nspawn/helloworld.nspawn</strong> avec votre éditeur de texte préféré et ajoutez les lignes suivantes :</p>
<p>Cela demandera à systemd de connecter l’ethernet virtuel <em>host0</em> du conteneur à l’interface réseau <em>br0</em> de l’hôte.</p>
<p>Nous avons maintenant une connexion entre le conteneur et l’hôte, mais nous devons encore créer et configurer le pont <em>br0</em> pour que le conteneur puisse réellement accéder à l’internet. Assurez-vous que le transfert de paquets IPv4 est activé sur l’hôte : ouvrez le fichier <strong>/etc/sysctl.conf</strong> avec votre éditeur de texte préféré et cherchez <strong>net.ipv4.ip_forward</strong>. Assurez-vous qu’il est défini sur 1 et qu’il n’est pas commenté :</p>
<p>Dans la suite de ce guide, je suppose que votre interface réseau primaire sur l’hôte est appelée <em>eth0</em>. Si ce n’est pas le cas, remplacez <em>eth0</em> par le nom correct en conséquence.</p>
<p>Ouvrez maintenant <strong>/etc/network/interfaces</strong> et ajoutez les lignes suivantes :</p>
<p>Il devrait déjà y avoir une définition d’interface pour eth0 dans ce fichier. Assurez-vous qu’elle est réglée sur manuel en changeant sa définition en :</p>
<p>Notez que si vous avez installé un gestionnaire de réseau ou quelque chose de similaire, cela peut interférer avec la configuration de notre réseau. Vous devrez probablement le désactiver ou configurer le gestionnaire de réseau pour qu’il ne touche pas les interfaces que nous utilisons ici.</p>
</blockquote>
<p>Bien, maintenant, faites apparaître notre pont :</p>
<p>Utilisez l’adr ip pour voir si tout a fonctionné comme prévu. Le résultat doit être similaire à celui-ci (c’est moi qui souligne) :</p>
<divclass="language-plaintext highlighter-rouge"><divclass="highlight"><preclass="highlight"><code>1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
<h3id="conteneurs-en-marche-en-arrière-plan">Conteneurs en marche en arrière-plan</h3>
<p>Nous sommes maintenant en mesure de faire fonctionner des conteneurs et leur avons donné un accès à Internet via un pont réseau.<br/>
Si vous prévoyez d’utiliser le conteneur, par exemple pour faire fonctionner un serveur web isolé à l’intérieur, vous souhaitez très probablement que le conteneur fonctionne en arrière-plan à tout moment, et aussi qu’il démarre automatiquement chaque fois que la machine hôte est (re)démarrée.</p>
<p>Comme je l’ai déjà mentionné dans cet article, les conteneurs systemd fonctionnent comme les autres services systemd, ce qui signifie que nous pouvons les démarrer, les arrêter, les activer ou les désactiver.</p>
<p>Pour démarrer un conteneur en arrière-plan :</p>
<p>Vous pouvez quitter la session (comme systemd vous en informe) en appuyant sur <codeclass="language-plaintext highlighter-rouge">Ctrl + Alt Gr + ]</code> 3 fois en une seconde.</p>
<p>Pour fermer un conteneur en cours d’exécution :</p>
screen -dmS buster1 sudo systemd-nspawn -bM buster1 # Un petit hack nettement plus simple/rapide que configurer au propre systemd-nspawn@.service pour avoir du réseau dans le conteneur avec machinectl start
sudo machinectl shell buster1
</code></pre></div></div>
<p><ahref="https://www.freedesktop.org/software/systemd/man/machinectl.html">machinectl</a> va permettre de gérer les conteneurs et machines virtuelles (VM), voici les principales commandes utiles.</p>
<ul>
<li><codeclass="language-plaintext highlighter-rouge">machinectl</code> ou <codeclass="language-plaintext highlighter-rouge">machinectl list</code> # Lister les conteneurs actifs (running/online)</li>
<li><codeclass="language-plaintext highlighter-rouge">machinectl list-images</code> # Montrer une liste des images des conteneurs présentes dans <strong>/var/lib/machines/</strong></li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl start buster1</code> # Démarrer le conteneur buster1</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl poweroff buster1</code> ou <codeclass="language-plaintext highlighter-rouge">sudo machinectl stop buster1</code> # Éteindre le conteneur buster1 proprement (shut down cleanly), stop est un alias de poweroff</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl terminate buster1</code> # Éteindre le conteneur buster1 immédiatemment (immediately terminates a virtual machine or container, without cleanly shutting it down)</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl clone buster buster1</code> # Cloner le conteneur buster en buster1</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl remove buster1</code> # Supprimer le conteneur buster1</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl clean --all</code> # Supprimer toutes les images des conteneurs</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl login buster1</code> # Open an interactive terminal login session, à utiliser pour être connecté dans le conteneur, la commande exit déconnecte l’utilisateur mais ne le fait pas sortir du conteneur</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl shell buster1</code> # Open an interactive shell session, l’intérêt est d’être connecté en root sans avoir besoin de se loguer (taper le mot de passe) par contre la commande exit fait sortir du conteneur. À utiliser pour lancer quelques commandes rapidement</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl copy-to buster1 ~/Documents/proc.jpeg /root/image.jpg</code> # Copier un fichier dans le conteneur buster1</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl copy-to buster1 ~/Images /root/Images</code> # Copier un dossier dans le conteneur buster1</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl copy-from buster1 /root/xyz abc</code> # Récupérer le fichier /root/xyz du conteneur buster1 dans le dossier courant de l’hôte</li>
<li><codeclass="language-plaintext highlighter-rouge">sudo machinectl copy-from buster1 /root/xyz</code> # Récupérer le fichier /root/xyz du conteneur buster1 dans /root (par défaut) de l’hôte</li>