Exécuter systemd dans un conteneur

Nous suivons depuis longtemps le sujet de l'utilisation de systemd dans les conteneurs. En 2014, notre ingénieur en sécurité Daniel Walsh a écrit un article Exécuter systemd dans un conteneur Docker, et quelques années plus tard - un autre, qui s'appelait Exécuter systemd dans un conteneur non privilégié, dans lequel il a déclaré que la situation ne s'était pas beaucoup améliorée. Il a notamment écrit que « malheureusement, même deux ans plus tard, si vous recherchez « Système Docker » sur Google, la première chose qui apparaît est son même vieil article. Il est donc temps de changer quelque chose. De plus, nous avons déjà parlé conflit entre Docker et les développeurs systemd.

Exécuter systemd dans un conteneur

Dans cet article, nous montrerons ce qui a changé au fil du temps et comment Podman peut nous aider dans ce domaine.

Il existe de nombreuses raisons d'exécuter systemd dans un conteneur, telles que :

  1. Conteneurs multiservices – de nombreuses personnes souhaitent extraire leurs applications multiservices de machines virtuelles et les exécuter dans des conteneurs. Il vaudrait bien sûr mieux diviser ces applications en microservices, mais tout le monde ne sait pas encore comment le faire ou n’a tout simplement pas le temps. Par conséquent, exécuter des applications telles que des services lancés par systemd à partir de fichiers unitaires est parfaitement logique.
  2. Fichiers d'unité Systemd – La plupart des applications exécutées dans des conteneurs sont créées à partir de code précédemment exécuté sur des machines virtuelles ou physiques. Ces applications disposent d'un fichier unité qui a été écrit pour ces applications et qui comprend comment elles doivent être lancées. Il est donc toujours préférable de démarrer les services en utilisant les méthodes prises en charge, plutôt que de pirater votre propre service d'initialisation.
  3. Systemd est un gestionnaire de processus. Il gère les services (arrête, redémarre les services ou tue les processus zombies) mieux que tout autre outil.

Cela dit, il existe de nombreuses raisons de 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 attendez-vous à ce que les conteneurs écrivent le journal directement sur stdout et stderr. Par conséquent, si vous envisagez de gérer des conteneurs via des outils d'orchestration tels que ceux mentionnés ci-dessus, vous devriez sérieusement envisager d'utiliser des conteneurs basés sur systemd. De plus, les développeurs Docker et Moby se sont 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 progressé. L'équipe responsable de l'exécution des conteneurs chez Red Hat a décidé de développer votre propre moteur de conteneur. Il a un nom Podman et offre la même interface de ligne de commande (CLI) que Docker. Et presque toutes les commandes Docker peuvent être utilisées dans Podman de la même manière. Nous organisons souvent des séminaires, désormais appelés Changer Docker en Podman, et la toute première diapositive appelle à l'écriture : 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 sous-système d'initialisation Linux le plus couramment utilisé, et ne pas lui permettre de fonctionner correctement 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 un conteneur. Il a besoin de choses comme monter tmpfs sur /run et /tmp. Elle aime que l'environnement « conteneurisé » soit activé et attend des autorisations d'écriture sur sa partie du répertoire cgroup et sur le dossier /var/log/journald.

Lorsque vous démarrez un conteneur dans lequel la première commande est init ou systemd, Podman configure automatiquement tmpfs et Cgroups pour garantir que systemd démarre sans problème. Pour bloquer ce mode de lancement automatique, utilisez l'option --systemd=false. Veuillez noter que Podman n'utilise le mode systemd que lorsqu'il constate qu'il doit exécuter une commande systemd ou init.

Voici un extrait du manuel :

homme podman courir
...

–systemd = vrai | faux

Exécuter un conteneur en mode systemd. Activé par défaut.

Si vous exécutez une commande systemd ou init dans un conteneur, Podman configurera les points de montage tmpfs dans les répertoires suivants :

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

Le signal d'arrêt par défaut sera également SIGRTMIN+3.

Tout cela permet à systemd de s'exécuter dans un conteneur fermé sans aucune modification.

REMARQUE : systemd tente d'écrire sur le système de fichiers du groupe de contrôle. Cependant, SELinux empêche les conteneurs de le faire par défaut. Pour activer l'écriture, activez le paramètre booléen containers_manage_cgroup :

setsebool -P conteneur_manage_cgroup vrai

Regardez maintenant à quoi ressemble le Dockerfile pour exécuter systemd dans un conteneur à l'aide 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, nous assemblons le conteneur :

# podman build -t systemd .

Nous disons à SELinux d'autoriser systemd à modifier la configuration des Cgroups :

# setsebool -P container_manage_cgroup true

D’ailleurs, beaucoup de gens oublient cette étape. Heureusement, cela ne doit être fait qu'une seule fois et le paramètre est enregistré après le redémarrage du système.

Maintenant, nous démarrons 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.

Ça y est, le service est opérationnel :

$ curl localhost

<html  xml_lang="en" lang="en">

…

</html>

REMARQUE : n'essayez pas ceci sur Docker ! Là, il faut encore danser avec un tambourin pour lancer ce genre de conteneurs via le démon. (Des champs et packages supplémentaires seront requis pour que tout cela fonctionne de manière transparente dans Docker, ou il devra être exécuté dans un conteneur privilégié. Pour plus de détails, voir article.)

Quelques autres 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, qui démarrera le service et le surveillera. Podman utilise le modèle fork-exec standard. En d’autres termes, les processus conteneurs sont des enfants du processus Podman, donc systemd peut facilement les surveiller.

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

Activation de systemd via socket

Podman gère correctement l'activation via socket. Étant donné que Podman utilise le modèle fork-exec, il peut transmettre le socket à ses processus conteneurs enfants. Docker ne peut pas faire cela car il utilise un modèle client-serveur.

Le service varlink que Podman utilise pour communiquer avec les clients distants vers les conteneurs est en fait activé via un 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 exécutant cockpit-podman envoie des messages à un socket varlink sur lequel systemd écoute. Systemd active ensuite le programme Podman pour recevoir des messages et commencer à gérer les conteneurs. L'activation de systemd sur un socket élimine le besoin d'un démon exécuté en permanence lors de l'implémentation d'API distantes.

De plus, nous développons un autre client Podman appelé podman-remote, qui implémente la même CLI Podman mais appelle varlink pour exécuter des conteneurs. Podman-remote peut s'exécuter sur les sessions SSH, vous permettant d'interagir en toute sécurité avec des conteneurs sur différentes machines. Au fil du temps, nous prévoyons de permettre à podman-remote de prendre en charge MacOS et Windows aux côtés de Linux, afin que les développeurs sur ces plates-formes puissent exécuter une machine virtuelle Linux avec Podman varlink en cours d'exécution et bénéficier de l'expérience complète de l'exécution des conteneurs sur la machine locale.

SD_NOTIFY

Systemd vous permet de différer le lancement des services auxiliaires jusqu'au démarrage du service conteneurisé dont ils ont besoin. Podman peut transmettre le socket SD_NOTIFY au service conteneurisé afin que le service informe systemd qu'il est prêt à fonctionner. Et encore une fois, Docker, qui utilise un modèle client-serveur, ne peut pas le faire.

les plans

Nous prévoyons d'ajouter la commande podman generate systemd CONTAINERID, qui générera un fichier unité systemd pour gérer un conteneur spécifique spécifié. Cela devrait fonctionner à la fois en mode root et sans racine pour les conteneurs non privilégiés. Nous avons même vu une demande pour un runtime systemd-nspawn compatible OCI.

Conclusion

Exécuter systemd dans un conteneur est un besoin compréhensible. Et grâce à Podman, nous disposons enfin d'un runtime de conteneur qui n'entre pas en conflit avec systemd, mais qui le rend facile à utiliser.

Source: habr.com

Ajouter un commentaire