Was ist das Besondere an der Trennung der Ausführungsumgebung von Containern in separate Instrumente? Insbesondere, dass diese Werkzeuge kombiniert werden können, um sich gegenseitig zu schützen.

Viele sind an der Idee interessiert, OCI-Container-Images innerhalb von oder einem ähnlichen System zu erstellen. Angenommen, wir haben ein CI/CD, das ständig Images baut, dann etwas wie /Kubernetes было бы весьма полезно с точки зрения распределения нагрузки при сборке. До недавних пор большинство людей просто давали контейнерам доступ к Docker-сокету и разрешали выполнять команду docker build. , dass dies sehr unsicher ist, tatsächlich ist es sogar schlimmer, als einem root oder sudo ohne Passwort zu geben.
Deshalb versuchen die Leute ständig, Buildah in einem Container auszuführen. Kurz gesagt, wir haben erstellt wie, unserer Meinung nach, Buildah am besten innerhalb eines Containers ausgeführt wird, und haben die entsprechenden Images auf . Lass uns anfangen…
Einstellungen
Diese Images wurden aus Dockerfiles erstellt, die im Buildah-Repository im Ordner .
Hier betrachten wir .
# stable/Dockerfile
#
# Build a Buildah container image from the latest
# stable version of Buildah on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=buildah
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM fedora:latest
# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
RUN yum -y install buildah fuse-overlayfs --exclude container-selinux; rm -rf /var/cache /var/log/dnf* /var/log/yum.*
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
Anstelle von OverlayFS, das auf der Host-Linux-Kernel-Ebene implementiert ist, verwenden wir innerhalb des Containers das Programm , da OverlayFS derzeit nur dann montieren kann, wenn sie die Berechtigungen SYS_ADMIN über Linux-Capabilities erhält. Und wir möchten unsere Buildah-Container ohne root-Berechtigungen ausführen. Fuse-overlay funktioniert recht schnell und bietet bessere Leistung als der Storage-Treiber VFS. Beachte, dass beim Starten eines Buildah-Containers, der Fuse verwendet, das Gerät /dev/fuse bereitgestellt werden muss.
podman run --device /dev/fuse quay.io/buildahctr ...
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
Als nächstes erstellen wir ein Verzeichnis für zusätzliche Speicher. unterstützt das Konzept des Anbindens zusätzlicher schreibgeschützter Speicher für Images. Beispielsweise kann ein Overlay-Speicherbereich auf einer Maschine konfiguriert und dann über NFS auf einer anderen Maschine gemountet werden, um die Images ohne Download durch Pull zu verwenden. Wir benötigen diesen Speicher, um in einem Container auf einen Host-Image-Speicher als Volume zugreifen zu können.
# Set up environment variables to note that this is
# not starting with user namespace and default to
# isolate the filesystem with chroot.
ENV _BUILDAH_STARTED_IN_USERNS="" BUILDAH_ISOLATION=chroot
Und schließlich, indem wir die Umgebungsvariable BUILDAH_ISOLATION verwenden, geben wir an, dass standardmäßig der Buildah-Container mit chroot-Isolation gestartet werden soll. Zusätzliche Isolation ist hier nicht erforderlich, da wir bereits in einem Container arbeiten. Damit Buildah eigene Container mit Namensraum-Trennung erstellen kann, ist die SYS_ADMIN-Befugnis erforderlich, und dafür müssen die SELinux- und SECCOMP-Richtlinien für den Container gelockert werden, was unserer Installation widerspricht, die den Aufbau aus einem sicheren Container heraus durchführen soll.
Starten wir Buildah innerhalb des Containers
Das oben beschriebene Schema des Buildah-Container-Images ermöglicht eine flexible Variierung der Startmethoden solcher Container.
Geschwindigkeit gegen Sicherheit
Computersicherheit ist immer ein Kompromiss zwischen der Geschwindigkeit des Prozesses und der Menge an Schutz, die darum herum aufgebaut ist. Diese Aussage gilt auch für das Erstellen von Containern, weshalb wir im Folgenden die Möglichkeiten eines solchen Kompromisses betrachten werden.
Das oben beschriebene Container-Image wird seinen Speicher in /var/lib/containers haben. Daher müssen wir den Inhalt in diesen Ordner einhängen, und die Art und Weise, wie wir dies tun, wird sich erheblich auf die Geschwindigkeit des Baus von Container-Images auswirken.
Betrachten wir drei Möglichkeiten.
Option 1. Wenn maximale Sicherheit erforderlich ist, kann für jeden Container ein eigenes Verzeichnis für containers/image erstellt und über Volume-Mount an den Container angeschlossen werden. Darüber hinaus kann das Kontextverzeichnis im Container selbst im Verzeichnis /build platziert werden:
# mkdir /var/lib/containers1
# podman run -v ./build:/build:z -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable
buildah -t image1 bud /build
# podman run -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable buildah push image1 registry.company.com/myuser
# rm -rf /var/lib/containers1
Sicherheit. Der in einem solchen Container arbeitende Buildah hat maximale Sicherheit: Ihm werden keinerlei Root-Rechte über die Möglichkeiten gegeben, und alle SECOMP- und SELinux-Einschränkungen werden auf ihn angewendet. Ein solcher Container kann sogar mit User Namespace Isolation gestartet werden, indem eine Option wie —uidmap 0:100000:10000 hinzugefügt wird.
Leistung. Die Leistung ist hier jedoch minimal, da jede Abbildung aus Container-Registrierungen jedes Mal auf den Host kopiert wird und Caching überhaupt nicht funktioniert. Wenn Buildah seine Arbeit beendet, muss der Container das Image in das Repository hochladen und den Inhalt auf dem Host löschen. Wenn das Container-Image das nächste Mal erstellt wird, muss es erneut aus dem Repository heruntergeladen werden, da zu diesem Zeitpunkt auf dem Host nichts mehr vorhanden sein wird.
Option 2. Wenn die Leistung auf Docker-Niveau benötigt wird, kann der container/storage des Hosts direkt im Container eingebunden werden.
# podman run -v ./build:/build:z -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah -t image2 bud /build
# podman run -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah push image2 registry.company.com/myuser
Sicherheit. Dies ist die sicherheitsmäßig schwächste Methode zum Erstellen von Containern, da der Container hier berechtigt ist, das Speicherverzeichnis auf dem Host zu modifizieren, und potenziell ein bösartiges Image an Podman oder CRI-O einspeisen kann. Darüber hinaus muss die SELinux-Trennung deaktiviert werden, damit die Prozesse im Buildah-Container mit dem Speicher des Hosts interagieren können. Beachten Sie, dass diese Option immer noch besser ist als der Docker-Socket, da der Container durch die verbleibenden Sicherheitsfunktionen blockiert wird und nicht einfach einen beliebigen Container auf dem Host starten kann.
Leistung. Hier ist sie maximal, da das Caching vollständig genutzt wird. Wenn Podman oder CRI-O das benötigte Image bereits auf dem Host heruntergeladen haben, muss der Buildah-Prozess im Container es nicht erneut herunterladen, und nachfolgende Builds basierend auf diesem Image können ebenfalls das Nötige aus dem Cache beziehen.
Option 3. Das Wesen dieses Verfahrens besteht darin, mehrere Images zu einem Projekt mit einem gemeinsamen Verzeichnis für Container-Images zu kombinieren.
# mkdir /var/lib/project3
# podman run --security-opt label_level=s0:C100, C200 -v ./build:/build:z
-v /var/lib/project3:/var/lib/containers:Z quay.io/buildah/stable buildah -t image3 bud /build
# podman run --security-opt label_level=s0:C100, C200
-v /var/lib/project3:/var/lib/containers quay.io/buildah/stable buildah push image3 registry.company.com/myuser
In diesem Beispiel löschen wir das Projektverzeichnis (/var/lib/project3) zwischen den Ausführungen nicht, sodass alle nachfolgenden Builds im Rahmen des Projekts von den Vorteilen des Caching profitieren.
Sicherheit. Eine Art Mischung zwischen den Optionen 1 und 2. Einerseits haben die Container keinen Zugang zum Inhalt auf dem Host und können dementsprechend nichts Schlechtes im Podman/CRI-O-Image-Speicher einspeisen. Andererseits kann der Container im Rahmen seines Projekts in die Erstellung anderer Container eingreifen.
Leistung. Hier ist die Situation schlechter als bei der Verwendung eines gemeinsamen Caches auf Host-Ebene, da Bilder, die zuvor mit Podman/CRI-O heruntergeladen wurden, nicht verwendet werden können. Nachdem Buildah jedoch ein Image heruntergeladen hat, kann dieses in allen nachfolgenden Builds im Rahmen des Projekts verwendet werden.
Zusätzliche Speicher
Der Es gibt so etwas Tolles wie zusätzliche Speicher (additional stores), dank dem Container-Engines bei der Bereitstellung und dem Erstellen von Containern externe Speicher für Images im read-only Overlay-Modus nutzen können. Im Grunde genommen kann in die Datei storage.conf ein oder mehrere "Nur-Lesen"-Speicher hinzugefügt werden, so dass der Container-Engine beim Start des Containers in diesen nach dem benötigten Image sucht. Dabei lädt er das Image nur aus dem Registry herunter, wenn er es in keinem dieser Speicher findet. Der Container-Engine kann nur in schreibbare Speicher schreiben...
Wenn wir nach oben scrollen und die Dockerfile ansehen, die wir zum Erstellen des Images quay.io/buildah/stable verwenden, finden wir folgende Zeilen:
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
In der ersten Zeile modifizieren wir /etc/containers/storage.conf innerhalb des Container-Images und sagen dem Storage-Treiber, dass er "additionalimagestores" im Ordner /var/lib/shared verwenden soll. In der nächsten Zeile erstellen wir einen gemeinsamen Ordner und fügen ein paar Lock-Dateien hinzu, um Konflikte mit containers/storage zu vermeiden. Im Grunde genommen erstellen wir einfach einen leeren Container-Image-Speicher.
Wenn containers/storage einen Level über diesem Ordner gemountet wird, kann Buildah die Images verwenden.
Kommen wir nun zurück zur zuvor betrachteten Variante 2, wenn der Buildah-Container in containers/store auf Hosts lesen und schreiben kann und somit maximale Leistung durch Caching der Images auf Podman/CRI-O-Ebene erzielt, aber nur minimale Sicherheit bietet, da er direkt in die Speicher schreiben kann. Und jetzt fügen wir hier zusätzliche Speicher hinzu und erhalten das Beste aus beiden Welten.
# mkdir /var/lib/containers4
# podman run -v ./build:/build:z -v /var/lib/containers/storage:/var/lib/shared:ro -v /var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable
buildah -t image4 bud /build
# podman run -v /var/lib/containers/storage:/var/lib/shared:ro
-v >/var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable buildah push image4 registry.company.com/myuser
# rm -rf /var/lib/continers4
Beachten Sie, dass /var/lib/containers/storage des Hosts im Container in /var/lib/shared im read-only Modus gemountet ist. Daher kann Buildah im Container alle zuvor mit Podman/CRI-O heruntergeladenen Images verwenden (Hallo, Geschwindigkeit), kann jedoch dabei nur in seinen eigenen Speicher schreiben (Hallo, Sicherheit). Beachten Sie auch, dass dies ohne Deaktivierung der SELinux-Trennung für den Container geschieht.
Wichtiger Hinweis
Es dürfen keinesfalls Images aus dem darunter liegenden Speicher gelöscht werden. Andernfalls könnte der Buildah-Container abstürzen.
Und das sind längst nicht alle Vorteile
Die Möglichkeiten zusätzlicher Speicherorte beschränken sich nicht nur auf das oben beschriebene Szenario. Beispielsweise können alle Container-Images in einem gemeinsamen Netzwerk-Speicher abgelegt werden, auf den alle Buildah-Container zugreifen können. Angenommen, wir haben Hunderte von Images, die unser CI/CD-System regelmäßig zum Erstellen von Container-Images verwendet. Wir zentralisieren all diese Images auf einem einzigen Host-Speicher und eröffnen dann mittels bevorzugter Netzwerkstorage-Mittel (NFS, Gluster, Ceph, ISCSI, S3…) den allgemeinen Zugriff auf diesen Speicher für alle Buildah- oder Kubernetes-Knoten.
Jetzt reicht es aus, diesen Netzwerkspeicher im Buildah-Container unter /var/lib/shared zu mounten, und schon müssen die Buildah-Container keine Images mehr über Pull herunterladen. Damit fällt die Phase der Vorbefüllung (Pre-Population) weg, und wir sind sofort bereit, die Container auszurollen.
Und natürlich kann dies im Rahmen eines bestehenden Kubernetes-Systems oder einer Container-Infrastruktur genutzt werden, um Container überall auszuführen und zu betreiben, ohne Images über Pull herunterzuladen. Darüber hinaus kann das Container-Registry, das einen Push-Befehl zum Hochladen eines aktualisierten Images erhält, dieses Image automatisch in den gemeinsamen Netzwerkspeicher senden, wo es sofort für alle Knoten verfügbar wird.
Die Größen von Container-Images können manchmal mehrere Gigabyte erreichen. Die Funktionalität zusätzlicher Speicherorte ermöglicht es, auf die Klonierung solcher Images bei den Knoten zu verzichten und macht das Starten von Containern nahezu sofort.
Darüber hinaus arbeiten wir derzeit an einer neuen Funktion für Overlay-Volume-Mounts, die den Aufbau von Containern noch schneller machen wird.
Fazit
Es ist durchaus möglich, Buildah innerhalb eines Containers in einer Umgebung mit Kubernetes/CRI-O, Podman oder sogar Docker auszuführen. Außerdem ist es einfach und deutlich sicherer als die Verwendung von docker.socket. Wir haben die Flexibilität bei der Arbeit mit Images erheblich erhöht, und jetzt können Sie diese auf verschiedene Weisen starten, um das optimale Gleichgewicht zwischen Sicherheit und Leistung zu erreichen.
Die Funktionalität zusätzlicher Speicherorte ermöglicht es, das Herunterladen von Images auf die Knoten zu beschleunigen oder sogar vollständig zu eliminieren.
Quelle: habr.com
