Exécutez systemd dans le conteneur

Nous suivons depuis longtemps le sujet de l'utilisation de systemd dans des conteneurs. En 2014, notre ingénieur en sécurité Daniel Walsh a écrit un article intitulé Running systemd dans un Docker Container , et quelques années plus tard, un autre article intitulé Running systemd dans un conteneur non privilégié , dans lequel il a déclaré que la situation n'était pas très amélioré. En particulier, il a écrit que «malheureusement, et deux ans plus tard, si vous utilisez le système Docker sur Google, la première chose qui apparaît est le même vieil article. Il est donc temps de changer quelque chose. " De plus, nous avons déjà parlé du conflit entre les développeurs de Docker et systemd .



Dans cet article, nous montrerons ce qui a changé au cours du temps passé et comment Podman peut nous aider dans cette affaire.

Il existe de nombreuses raisons pour exécuter systemd à l'intérieur d'un conteneur, telles que:

  1. Conteneurs multiservices - de nombreuses personnes souhaitent retirer leurs applications multiservices des machines virtuelles et les exécuter dans des conteneurs. Il serait préférable, bien sûr, de diviser ces applications en microservices, mais tout le monde ne peut pas encore le faire ou il n'y a tout simplement pas de temps. Par conséquent, le lancement de telles applications sous la forme de services lancés par systemd à partir de fichiers unitaires est parfaitement logique.
  2. Fichiers d'unité Systemd - la plupart des applications exécutées à l'intérieur de conteneurs sont compilées à partir de code qui s'exécutait auparavant sur des machines virtuelles ou physiques. Ces applications ont un fichier unité qui a été écrit pour ces applications et comprend comment les exécuter. Il est donc préférable de démarrer les services à l'aide des méthodes prises en charge, plutôt que de pirater votre propre service init.
  3. Systemd est un gestionnaire de processus. Il gère les services (arrête, redémarre les services ou analyse les processus zombies) mieux que tout autre outil.

Il y a plusieurs raisons pour ne pas exécuter systemd dans des conteneurs. Le principal est que systemd / journald contrôle la sortie des conteneurs, et des outils comme Kubernetes ou OpenShift attendent des conteneurs qu'ils écrivent le journal directement sur stdout et stderr. Par conséquent, si vous avez l'intention de gérer des conteneurs via des outils d'orchestration tels que ceux mentionnés ci-dessus, vous devez sérieusement envisager l'utilisation de conteneurs basés sur systemd. De plus, les développeurs de Docker et Moby étaient souvent fortement opposés à l'utilisation de systemd dans des conteneurs.

L'arrivée de Podman


Nous sommes heureux d'annoncer que la situation a enfin décollé. L'équipe responsable du lancement des conteneurs chez Red Hat a décidé de développer leur propre moteur de conteneurs . Il a obtenu le nom Podman et propose la même interface de ligne de commande (CLI) que Docker. Et presque toutes les commandes Docker peuvent être utilisées de la même manière dans Podman. Nous organisons souvent des séminaires, qui s'appellent désormais Changer Docker en Podman , et la première diapositive vous encourage à vous inscrire: alias docker = podman.

Beaucoup le font.

Mon Podman et moi ne sommes en aucun cas contre les conteneurs basés sur systemd. Après tout, Systemd est le plus souvent utilisé comme sous-système d'initialisation Linux, et ne pas le laisser fonctionner normalement dans des conteneurs signifie ignorer la façon dont des milliers de personnes sont habituées à exécuter des conteneurs.

Podman sait quoi faire pour que systemd fonctionne correctement dans le conteneur. Elle a besoin de choses comme monter tmpfs sur / run et / tmp. Elle aime quand l'environnement «conteneur» est activé, et elle attend les autorisations d'écriture dans sa partie du répertoire cgroup et dans le dossier / var / log / journald.

Lors du démarrage d'un conteneur dans lequel init ou systemd est la première commande, Podman configure automatiquement tmpfs et Cgroups afin que systemd démarre sans problème. Pour bloquer ce mode de démarrage automatique, utilisez l'option --systemd = false. Veuillez noter que Podman utilise le mode systemd uniquement lorsqu'il voit qu'il est nécessaire d'exécuter la commande systemd ou init.

Voici un extrait du manuel:

homme podman courir
...

–Systemd = true | false

Exécution du conteneur en mode systemd. Activé par défaut.

Si une commande systemd ou init est exécutée à l'intérieur du conteneur, Podman configurera les points de montage tmpfs dans les répertoires suivants:

/ run, / run / lock, / tmp, / sys / fs / cgroup / systemd, / var / lib / journal

De plus, SIGRTMIN + 3 sera utilisé comme signal d'arrêt par défaut.

Tout cela permet à systemd de fonctionner dans un conteneur fermé sans aucune modification.

REMARQUE: systemd tente d'écrire dans le système de fichiers cgroup. Cependant, SELinux empêche par défaut les conteneurs de le faire. Pour activer l'écriture, activez le paramètre batch container_manage_cgroup:

setsebool -P container_manage_cgroup true

Maintenant, regardez à quoi ressemble le Dockerfile pour exécuter systemd dans le conteneur lors de l'utilisation de Podman:

# cat Dockerfile FROM fedora RUN dnf -y install httpd; dnf clean all; systemctl enable httpd EXPOSE 80 CMD [ "/sbin/init" ] 

C’est tout.

Maintenant, récupérez le conteneur:

 # podman build -t systemd . 

Nous demandons à SELinux d'autoriser systemd à modifier la configuration de Cgroups:

 # setsebool -P container_manage_cgroup true 

Beaucoup, au fait, oublient cette étape. Heureusement, il suffit de le faire une seule fois et le paramètre est enregistré après un redémarrage du système.

Maintenant, lancez simplement le conteneur:

 # podman run -ti -p 80:80 systemd systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid) Detected virtualization container-other. Detected architecture x86-64. Welcome to Fedora 29 (Container Image)! Set hostname to <1b51b684bc99>. Failed to install release agent, ignoring: Read-only file system File /usr/lib/systemd/system/systemd-journald.service:26 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling. Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.) [ OK ] Listening on initctl Compatibility Named Pipe. [ OK ] Listening on Journal Socket (/dev/log). [ OK ] Started Forward Password Requests to Wall Directory Watch. [ OK ] Started Dispatch Password Requests to Console Directory Watch. [ OK ] Reached target Slices. … [ OK ] Started The Apache HTTP Server. 

Tout, le service a commencé et fonctionne:

 $ curl localhost <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> … </html> 

REMARQUE: n'essayez pas de répéter cela sur Docker! Là, des danses avec un tambourin sont encore nécessaires pour lancer de tels conteneurs à travers un démon. (Des champs et des packages supplémentaires seront nécessaires pour que cela fonctionne de manière transparente dans Docker, ou devront être exécutés dans un conteneur privilégié. Voir l' article pour plus de détails .)

Un peu plus de choses intéressantes sur Podman et systemd


Podman fonctionne mieux que docker dans les fichiers d'unité systemd


Si les conteneurs doivent être démarrés au démarrage du système, vous pouvez simplement insérer les commandes Podman appropriées dans le fichier d'unité systemd, ce qui lancera le service et le surveillera. Podman utilise le modèle fork-exec standard. En d'autres termes, les processus de conteneur sont affiliés au processus Podman, donc systemd peut facilement les surveiller.

Docker utilise le modèle client-serveur et les commandes CLI Docker peuvent également être placées directement dans le fichier d'unité. Cependant, une fois que le client Docker s'est connecté au démon Docker, il (le client) devient juste un autre processus qui traite stdin et stdout. À son tour, systemd n'a aucune idée de la connexion entre le client Docker et le conteneur qui exécute le démon Docker, et par conséquent, sous ce modèle, systemd ne peut pas fondamentalement surveiller le service.

Activation de Systemd via socket


Podman remplit correctement l'activation via une prise. Étant donné que Podman utilise le modèle fork-exec, il peut transférer un socket vers ses processus de conteneur enfant. Docker ne sait pas comment, car il utilise un modèle client-serveur.

Le service varlink que Podman utilise pour interagir avec des clients distants avec des conteneurs est en fait activé via le socket. Le package cockpit-podman, écrit en Node.js et faisant partie du projet cockpit, permet aux utilisateurs d'interagir avec les conteneurs Podman via une interface Web. Le démon Web sur lequel cockpit-podman s'exécute envoie des messages au socket varlink que systemd écoute. Après cela, systemd active le programme Podman pour recevoir des messages et commencer à gérer les conteneurs. L'activation de systemd via un socket vous permet de vous passer d'un démon fonctionnant en permanence lors de l'implémentation d'API distantes.

De plus, nous développons un autre client pour Podman, appelé podman-remote, qui implémente la même CLI Podman, mais appelle varlink pour lancer des conteneurs. Podman-remote peut fonctionner en plus des sessions SSH, ce qui vous permet d'interagir en toute sécurité avec des conteneurs sur différentes machines. Au fil du temps, nous prévoyons d'utiliser podman-remote pour prendre en charge MacOS et Windows avec Linux, afin que les développeurs sur ces plates-formes puissent exécuter la machine virtuelle Linux avec Podman varlink en cours d'exécution et avoir le sentiment que les conteneurs s'exécutent sur la machine locale.

SD_NOTIFY


Systemd vous permet de retarder le lancement des services auxiliaires jusqu'au démarrage du service conteneurisé requis. Podman peut transmettre la socket SD_NOTIFY au service conteneurisé afin que le service informe systemd de sa disponibilité au travail. Et encore une fois, Docker, utilisant le modèle client-serveur, ne sait pas comment.

Dans les plans


Nous prévoyons d'ajouter la commande podman generate systemd CONTAINERID, qui générera le fichier d'unité systemd pour gérer un conteneur spécifique. Cela devrait fonctionner dans les modes root et rootless pour les conteneurs non privilégiés. Nous avons même vu une demande de création d'un runtime systemd-nspawn compatible OCI.

Conclusion


L'exécution de systemd dans un conteneur est un besoin compréhensible. Et grâce à Podman, nous avons enfin un environnement de lancement de conteneurs qui n'est pas hostile à systemd, mais qui le rend facile à utiliser.

Source: https://habr.com/ru/post/fr468931/


All Articles