Systemd in einem Container ausführen

Wir verfolgen das Thema der Verwendung von systemd in Containern schon seit längerem. Bereits 2014 schrieb unser Sicherheitsingenieur Daniel Walsh einen Artikel Ausführen von systemd in einem Docker-Container, und ein paar Jahre später - ein anderer, der hieß Systemd in einem nicht privilegierten Container ausführen, in dem er erklärte, dass sich die Situation nicht wesentlich verbessert habe. Insbesondere schrieb er, dass „leider auch zwei Jahre später, wenn man nach „Docker-System“ googelt, das erste, was auftaucht, derselbe alte Artikel ist.“ Es ist also an der Zeit, etwas zu ändern.“ Darüber hinaus haben wir bereits darüber gesprochen Konflikt zwischen Docker- und Systemd-Entwicklern.

Systemd in einem Container ausführen

In diesem Artikel zeigen wir, was sich im Laufe der Zeit verändert hat und wie Podman uns dabei helfen kann.

Es gibt viele Gründe, systemd in einem Container auszuführen, wie zum Beispiel:

  1. Multiservice-Container – Viele Menschen möchten ihre Multi-Service-Anwendungen aus virtuellen Maschinen ziehen und in Containern ausführen. Natürlich wäre es besser, solche Anwendungen in Microservices aufzuteilen, aber nicht jeder weiß noch, wie das geht, oder hat einfach keine Zeit. Daher ist es absolut sinnvoll, solche Anwendungen als von systemd aus Unit-Dateien gestartete Dienste auszuführen.
  2. Systemd-Unit-Dateien – Die meisten Anwendungen, die in Containern ausgeführt werden, werden aus Code erstellt, der zuvor auf virtuellen oder physischen Maschinen ausgeführt wurde. Diese Anwendungen verfügen über eine Unit-Datei, die für diese Anwendungen geschrieben wurde und versteht, wie sie gestartet werden sollen. Daher ist es immer noch besser, Dienste mit unterstützten Methoden zu starten, als Ihren eigenen Init-Dienst zu hacken.
  3. Systemd ist ein Prozessmanager. Es verwaltet Dienste besser als jedes andere Tool (fährt Dienste herunter, startet sie neu oder beendet Zombie-Prozesse).

Allerdings gibt es viele Gründe, systemd nicht in Containern auszuführen. Der wichtigste ist, dass systemd/journald die Ausgabe von Containern und ähnlichen Tools steuert Kubernetes oder OpenShift Erwarten Sie, dass Container das Protokoll direkt in stdout und stderr schreiben. Wenn Sie Container über Orchestrierungstools wie die oben genannten verwalten möchten, sollten Sie daher ernsthaft über die Verwendung systemd-basierter Container nachdenken. Darüber hinaus haben sich Docker- und Moby-Entwickler oft strikt gegen die Verwendung von systemd in Containern ausgesprochen.

Das Kommen von Podman

Wir freuen uns, Ihnen mitteilen zu können, dass sich die Situation endlich verbessert hat. Das Team, das bei Red Hat für den Betrieb von Containern verantwortlich ist, hat sich für die Entwicklung entschieden Ihre eigene Container-Engine. Er hat einen Namen bekommen Podman und bietet die gleiche Befehlszeilenschnittstelle (CLI) wie Docker. Und fast alle Docker-Befehle können in Podman auf die gleiche Weise verwendet werden. Wir führen häufig Seminare durch, die jetzt aufgerufen werden Docker in Podman ändern, und die allererste Folie ruft zum Schreiben auf: alias docker=podman.

Viele Menschen tun genau das.

Mein Podman und ich sind in keiner Weise gegen systemd-basierte Container. Schließlich ist Systemd das am häufigsten verwendete Linux-Init-Subsystem, und wenn es nicht ordnungsgemäß in Containern funktioniert, muss ignoriert werden, wie Tausende von Menschen an die Ausführung von Containern gewöhnt sind.

Podman weiß, was zu tun ist, damit systemd in einem Container ordnungsgemäß funktioniert. Es braucht Dinge wie das Mounten von tmpfs auf /run und /tmp. Sie möchte die „containerisierte“ Umgebung aktiviert haben und erwartet Schreibberechtigungen für ihren Teil des cgroup-Verzeichnisses und für den Ordner /var/log/journald.

Wenn Sie einen Container starten, in dem der erste Befehl init oder systemd lautet, konfiguriert Podman automatisch tmpfs und Cgroups, um sicherzustellen, dass systemd ohne Probleme startet. Um diesen automatischen Startmodus zu blockieren, verwenden Sie die Option --systemd=false. Bitte beachten Sie, dass Podman den Systemd-Modus nur verwendet, wenn es erkennt, dass ein Systemd- oder Init-Befehl ausgeführt werden muss.

Hier ein Auszug aus dem Handbuch:

Mann Podman laufen
...

–systemd=true|false

Ausführen eines Containers im Systemd-Modus. Standardmäßig aktiviert.

Wenn Sie einen systemd- oder init-Befehl in einem Container ausführen, konfiguriert Podman tmpfs-Mount-Punkte in den folgenden Verzeichnissen:

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

Außerdem ist das Standardstoppsignal SIGRTMIN+3.

All dies ermöglicht, dass systemd ohne Änderungen in einem geschlossenen Container ausgeführt werden kann.

HINWEIS: systemd versucht, in das cgroup-Dateisystem zu schreiben. SELinux verhindert jedoch standardmäßig, dass Container dies tun. Um das Schreiben zu ermöglichen, aktivieren Sie den booleschen Parameter „container_manage_cgroup“:

setsebool -P container_manage_cgroup true

Schauen Sie sich nun an, wie die Docker-Datei für die Ausführung von systemd in einem Container mit Podman aussieht:

# cat Dockerfile

FROM fedora

RUN dnf -y install httpd; dnf clean all; systemctl enable httpd

EXPOSE 80

CMD [ "/sbin/init" ]

Das ist alles.

Jetzt bauen wir den Container zusammen:

# podman build -t systemd .

Wir weisen SELinux an, systemd zu erlauben, die Cgroups-Konfiguration zu ändern:

# setsebool -P container_manage_cgroup true

Übrigens vergessen viele Leute diesen Schritt. Glücklicherweise muss dies nur einmal durchgeführt werden und die Einstellung wird nach dem Neustart des Systems gespeichert.

Jetzt starten wir einfach den Container:

# 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.

Das war's, der Dienst ist betriebsbereit:

$ curl localhost

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

…

</html>

HINWEIS: Versuchen Sie dies nicht auf Docker! Dort müssen Sie noch mit einem Tamburin tanzen, um diese Art von Containern durch den Daemon zu schleusen. (Zusätzliche Felder und Pakete sind erforderlich, damit dies alles reibungslos in Docker funktioniert, oder es muss in einem privilegierten Container ausgeführt werden. Einzelheiten finden Sie unter Artikel.)

Noch ein paar coole Dinge über Podman und systemd

Podman funktioniert in Systemd-Unit-Dateien besser als Docker

Wenn Container beim Systemstart gestartet werden müssen, können Sie einfach die entsprechenden Podman-Befehle in die Systemd-Unit-Datei einfügen, wodurch der Dienst gestartet und überwacht wird. Podman verwendet das Standard-Fork-Exec-Modell. Mit anderen Worten: Containerprozesse sind untergeordnete Elemente des Podman-Prozesses, sodass systemd sie problemlos überwachen kann.

Docker verwendet ein Client-Server-Modell und Docker-CLI-Befehle können auch direkt in einer Unit-Datei platziert werden. Sobald der Docker-Client jedoch eine Verbindung zum Docker-Daemon herstellt, wird er (der Client) zu einem weiteren Prozess, der stdin und stdout verarbeitet. Systemd wiederum hat keine Ahnung von der Verbindung zwischen dem Docker-Client und dem Container, der unter der Kontrolle des Docker-Daemons läuft, und kann daher in diesem Modell den Dienst grundsätzlich nicht überwachen.

Systemd über Socket aktivieren

Podman handhabt die Aktivierung über den Socket korrekt. Da Podman das Fork-Exec-Modell verwendet, kann es den Socket an seine untergeordneten Containerprozesse weiterleiten. Docker kann dies nicht, da es ein Client-Server-Modell verwendet.

Der Varlink-Dienst, den Podman verwendet, um mit Remote-Clients an Container zu kommunizieren, wird tatsächlich über einen Socket aktiviert. Das in Node.js geschriebene Paket „cockpit-podman“ ist Teil des Cockpit-Projekts und ermöglicht es Benutzern, über eine Weboberfläche mit Podman-Containern zu interagieren. Der Web-Daemon, auf dem „cockpit-podman“ ausgeführt wird, sendet Nachrichten an einen Varlink-Socket, den systemd abhört. Systemd aktiviert dann das Podman-Programm, um Nachrichten zu empfangen und mit der Verwaltung von Containern zu beginnen. Durch die Aktivierung von systemd über einen Socket entfällt die Notwendigkeit eines ständig laufenden Daemons bei der Implementierung von Remote-APIs.

Darüber hinaus entwickeln wir einen weiteren Podman-Client namens podman-remote, der dieselbe Podman-CLI implementiert, aber varlink aufruft, um Container auszuführen. Podman-remote kann zusätzlich zu SSH-Sitzungen ausgeführt werden, sodass Sie sicher mit Containern auf verschiedenen Computern interagieren können. Im Laufe der Zeit planen wir, podman-remote so zu aktivieren, dass es neben Linux auch MacOS und Windows unterstützt, damit Entwickler auf diesen Plattformen eine virtuelle Linux-Maschine mit laufendem Podman-Varlink ausführen können und das volle Erlebnis haben, dass Container auf der lokalen Maschine ausgeführt werden.

SD_NOTIFY

Mit Systemd können Sie den Start von Hilfsdiensten verschieben, bis der von ihnen benötigte Containerdienst gestartet wird. Podman kann den SD_NOTIFY-Socket an den Containerdienst weiterleiten, sodass der Dienst systemd benachrichtigt, dass er betriebsbereit ist. Und wiederum ist Docker, das ein Client-Server-Modell verwendet, nicht dazu in der Lage.

In den Plänen

Wir planen, den Befehl podman generic systemd CONTAINERID hinzuzufügen, der eine Systemd-Unit-Datei generiert, um einen bestimmten angegebenen Container zu verwalten. Dies sollte sowohl im Root- als auch im Rootless-Modus für nicht privilegierte Container funktionieren. Wir haben sogar eine Anfrage nach einer OCI-kompatiblen systemd-nspawn-Laufzeitumgebung gesehen.

Abschluss

Das Ausführen von systemd in einem Container ist eine verständliche Notwendigkeit. Und dank Podman haben wir endlich eine Container-Laufzeitumgebung, die nicht mit systemd in Konflikt steht, aber einfach zu verwenden ist.

Source: habr.com

Kommentar hinzufügen