{"id":3947,"date":"2024-09-30T07:00:00","date_gmt":"2024-09-30T05:00:00","guid":{"rendered":"https:\/\/www.my-it-brain.de\/wordpress\/?p=3947"},"modified":"2024-09-23T20:09:17","modified_gmt":"2024-09-23T18:09:17","slug":"buildah-baut-meine-container-images","status":"publish","type":"post","link":"https:\/\/www.my-it-brain.de\/wordpress\/buildah-baut-meine-container-images\/","title":{"rendered":"Buildah baut meine Container-Images"},"content":{"rendered":"\n<p>Dieser Artikel gibt meine Motivation f\u00fcr den Bau von Container-Images und die Vorgehensweise wieder und zeigt, wie ich mit <a href=\"https:\/\/buildah.io\/\">Buildah<\/a> meine OCI-kompatiblen Container-Images erstelle.<\/p>\n\n\n\n<p>Es handelt sich dabei mehr um einen Erfahrungsbericht als ein Tutorial und ich erhebe keinen Anspruch auf Vollst\u00e4ndigkeit. Das behandelte Beispiel ist jedoch zum Einstieg und zur Nachahmung f\u00fcr all jene geeignet, die Container ausf\u00fchren k\u00f6nnen und diese gerne ohne Verwendung von Containerfiles bauen m\u00f6chten.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Motivation<\/h2>\n\n\n\n<p>Ich m\u00f6chte die Ansible-Rollen aus meiner Collection <a href=\"https:\/\/codeberg.org\/tronde\/nextcloud\">tronde.nextcloud<\/a> mit <a href=\"https:\/\/ansible.readthedocs.io\/projects\/molecule\/examples\/podman\/\">Molecule und Podman-Containern<\/a> testen. Als Zielplattform f\u00fcr das Deployment der Nextcloud unterst\u00fctze ich zun\u00e4chst Debian und RHEL.<\/p>\n\n\n\n<p>Die Tests sollen verifizieren, dass <a href=\"https:\/\/www.my-it-brain.de\/wordpress\/nextcloud-im-container-teil-1-der-plan\/\" data-type=\"post\" data-id=\"2993\">Nextcloud im Container<\/a> in einer rootless-Podman-Umgebung bereitgestellt werden kann. Da der Test unter Verwendung von Podman-Containern durchgef\u00fchrt werden soll, m\u00fcssen diese Container eine solche rootless-Podman-Umgebung bereitstellen.<\/p>\n\n\n\n<p>F\u00fcr RHEL 8 und RHEL 9 habe ich entsprechende Container-Images <a href=\"https:\/\/catalog.redhat.com\/search?q=podman&amp;searchType=containers&amp;product_listings_names=Red%20Hat%20Universal%20Base%20Image%208|Red%20Hat%20Universal%20Base%20Image%209&amp;p=1\">gefunden<\/a>. F\u00fcr Debian bin ich nicht f\u00fcndig geworden und habe daher beschlossen, diese Container-Images selbst zu erstellen.<\/p>\n\n\n\n<p>Buildah ist das Werkzeug meiner Wahl, da:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Container-Images damit interaktiv erstellt werden k\u00f6nnen,<\/li>\n\n\n\n<li>die verwendeten Befehle am Ende in einem Bash-Skript gesammelt werden k\u00f6nnen,<\/li>\n\n\n\n<li>sich damit sogar interaktive Abfragen mit <a href=\"https:\/\/de.wikipedia.org\/wiki\/Heredoc\">Heredocs<\/a> beantworten lassen,<\/li>\n\n\n\n<li>man kein <code>containerfile(5)<\/code> ben\u00f6tigt und<\/li>\n\n\n\n<li>ich das Werkzeug noch nicht kenne und es gerne kennenlernen m\u00f6chte.<\/li>\n<\/ul>\n\n\n\n<p>F\u00fcr mich sind dies ausreichend Gr\u00fcnde, um mich kopf\u00fcber in ein neues Container-Projekt zu st\u00fcrzen. Wer mehr \u00fcber die Beziehung von Buildah zu Podman erfahren m\u00f6chte, dem empfehle ich den englischsprachigen Artikel: <a href=\"https:\/\/podman.io\/blogs\/2018\/10\/31\/podman-buildah-relationship.html\">Buildah and Podman Relationship<\/a> von Tom Sweeney.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Mein Weg zum Image<\/h2>\n\n\n\n<p>Um rootless Podman in einem Container zum Laufen zu bekommen, habe ich mich an dem englischsprachigen Artikel <a href=\"https:\/\/www.redhat.com\/sysadmin\/podman-inside-container\">How to use Podman inside of a container<\/a> von Dan Walsh orientiert. Das Ergebnis findet ihr in meinem GitHub-Repo <a href=\"https:\/\/github.com\/Tronde\/container-image-forge\">tronde\/container-image-forge<\/a>.<\/p>\n\n\n\n<p>Die folgenden Code-Bl\u00f6cke zeigen Ausz\u00fcge aus dem Skript <a href=\"https:\/\/github.com\/Tronde\/container-image-forge\/blob\/main\/buildah_create_debian_bookworm_with_rootless_podman.sh\">buildah_create_debian_bookworm_with_rootless_podman.sh<\/a> (Commit <a href=\"https:\/\/github.com\/Tronde\/container-image-forge\/commit\/7634ed8cb048e3c59e07743b8d7ca58f1c37e19c\">7634ed8<\/a>). Die enthaltenen Befehle werden unter dem jeweiligen Code-Block erl\u00e4utert. Alle Befehle werden als normaler Benutzer ohne Root-Rechte ausgef\u00fchrt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Name of target container image\ntctri=debian_rootless_podman\n\n# Get a base image\nctr=$(buildah from --pull=newer docker:\/\/docker.io\/library\/debian:bookworm)<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Die Variable <code>tctri<\/code> nimmt den Namen des Container-Images auf, welches ich erzeugen werde<\/li>\n\n\n\n<li>Die Variable <code>ctr<\/code> nimmt den Namen des Containers auf, welcher durch den <code>buildah-from(1)<\/code>-Befehl erzeugt wird; mit diesem Container wird im Folgenden gearbeitet<\/li>\n\n\n\n<li>Die Option <code>--pull=newer<\/code> sorgt daf\u00fcr, dass das Image nur dann aus der angegebenen Registry heruntergeladen wird, wenn es aktueller als das evtl. lokal gespeicherte Image ist<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>buildah run -- $ctr apt -y update\nbuildah run -- $ctr apt -y upgrade\nbuildah run -- $ctr apt -y install podman fuse-overlayfs libvshadow-utils libcap2-bin ca-certificates<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mit <code>buildah-run(1)<\/code> werden Befehle innerhalb des Arbeits-Containers ausgef\u00fchrt<\/li>\n\n\n\n<li>Ich aktualisiere die im Basis-Image enthaltenen Pakte und<\/li>\n\n\n\n<li>installiere die f\u00fcr rootless Podman ben\u00f6tigten Pakete<\/li>\n\n\n\n<li>Das Paket <code>ca-certificates<\/code> wird ben\u00f6tigt, um sp\u00e4ter Container-Images aus einer Registry herunterladen zu k\u00f6nnen<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>buildah run -- $ctr useradd podman\nbuildah run -- $ctr sh -c \"echo podman:1:999 &gt; \/etc\/subuid\"\nbuildah run -- $ctr sh -c \"echo podman:1001:64535 &gt;&gt; \/etc\/subuid\"\nbuildah run -- $ctr sh -c \"echo podman:1:999 &gt; \/etc\/subgid\"\nbuildah run -- $ctr sh -c \"echo podman:1001:64535 &gt;&gt; \/etc\/subgid\"\nbuildah run -- $ctr setcap cap_setuid+epi \/usr\/bin\/newuidmap\nbuildah run -- $ctr setcap cap_setgid+epi \/usr\/bin\/newgidmap<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mit den hier dargestellten Befehlen wird der nicht-privilegierte Benutzer <code>podm<\/code>an erstellt<\/li>\n\n\n\n<li>Die IDs f\u00fcr <code>\/etc\/sub[g,u]id<\/code> habe ich mir aus dem ubi9\/podman-Image abgeschaut<\/li>\n\n\n\n<li>Die <code>setcap<\/code>-Befehle sind notwendig, um rootless Podman ausf\u00fchren zu k\u00f6nnen; ich habe sie durch Internetrecherche und Trial-and-Error zusammengestellt<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>buildah config -v \/var\/lib\/containers $ctr\nbuildah config -v \/home\/podman\/.local\/share\/containers $ctr<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Das Image wird in der Lage sein, rootful und rootless Podman auszuf\u00fchren<\/li>\n\n\n\n<li>Mit den beiden Befehlen werden die Volumes hinzugef\u00fcgt, die Podman als Container Storage nutzt\n<ul class=\"wp-block-list\">\n<li>Rootful Podman verwendet <code>\/var\/lib\/containers<\/code><\/li>\n\n\n\n<li>Rootless Podman verwendet <code>\/home\/podman\/.local\/share\/containers<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>buildah run -- $ctr chown -R podman:podman \/home\/podman\nbuildah run -- $ctr sh -c \"mkdir -p \/var\/lib\/shared\/overlay-images \/var\/lib\/shared\/overlay-layers \/var\/lib\/shared\/vfs-images \/var\/lib\/shared\/vfs-layers; touch \/var\/lib\/shared\/overlay-images\/images.lock; touch \/var\/lib\/shared\/overlay-layers\/layers.lock; touch \/var\/lib\/shared\/vfs-images\/images.lock; touch \/var\/lib\/shared\/vfs-layers\/layers.lock\"\nbuildah config --env _CONTAINERS_USERNS_CONFIGURED=\"\" $ctr<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Der Benutzer <code>podman<\/code> bekommt ein HOME-Verzeichnis<\/li>\n\n\n\n<li>Ich erstelle die Verzeichnisse, die ich im Artikel [7] gefunden habe<\/li>\n\n\n\n<li>Ich erstelle die Environment-Variable, die ich ebenfalls in genanntem Artikel [7] gefunden habe<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>buildah run -- $ctr apt -y reinstall uidmap\nbuildah run -- $ctr apt -y clean\nbuildah run -- $ctr rm -rf \/var\/lib\/apt\/lists\/*<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Aus mir nicht bekannter Ursache muss das Paket <code>uidmap<\/code> neu installiert werden, um ein UID\/GID-Mapping sicherzustellen; dies scheint analog zur Neuinstallation der <code>shadow-utils<\/code> in Artikel [7] notwendig zu sein<\/li>\n\n\n\n<li>Die beiden folgenden Befehle sollen den Paket-Cache aufr\u00e4umen, um die Gr\u00f6\u00dfe des resultierenden Images zu verkleinern, zeigen jedoch keinen sichtbaren Effekt<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code># Commit to an image\nbuildah commit --rm $ctr $tctri\n# Alternative: Use this and add GPG fingerprint for image signing\n# buildah commit --sign-by &lt;fingerprint&gt; --rm $ctr $tctri\n\n# Tag the image just created\nbuildah tag $tctri $tctri:bookworm-$(date --iso)<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mit <code>buildah-commit(1)<\/code> wird der Inhalt des Arbeits-Containers <code>$ctr<\/code> in ein Container-Image namens <code>$tctri<\/code> geschrieben<\/li>\n\n\n\n<li>Durch Angabe der Option <code>--rm<\/code> wird der Arbeits-Container entfernt<\/li>\n\n\n\n<li>Die Kommentarzeile l\u00e4sst erkennen, dass ich zuk\u00fcnftig beabsichtige, meine Images digital zu signieren<\/li>\n\n\n\n<li><code>buildah-tag(1)<\/code> f\u00fcgt dem Image einen Tag mit Datumsstempel hinzu; siehe auch: <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/container-registry\/container-registry-image-tag-version\">Recommendations for tagging and versioning container images<\/a><\/li>\n<\/ul>\n\n\n\n<p>Der Befehl <code>buildah-commit(1)<\/code> f\u00fcgt dem neuen Image \u00fcbrigens nur einen weiteren Layer hinzu, egal wie viele Befehle zuvor im Arbeits-Container ausgef\u00fchrt wurden. Das erzeugte Image umfasst also die Layer des Basis-Image plus einen weiteren.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Zwischenfazit<\/h2>\n\n\n\n<p>An diesem Punkt habe ich ein Basis-Image ausgew\u00e4hlt, mithilfe von <code>buildah<\/code> zus\u00e4tzliche Software installiert, einen Benutzer hinzugef\u00fcgt und ein neues Image erzeugt.<\/p>\n\n\n\n<p>Um den Build-Prozess zu automatisieren, habe ich die notwendigen Befehle in Bash-Skripte geschrieben und unter <a href=\"https:\/\/github.com\/Tronde\/container-image-forge\">https:\/\/github.com\/Tronde\/container-image-forge<\/a> abgelegt.<\/p>\n\n\n\n<p>Die fertigen Images halte ich in der Registry <a href=\"https:\/\/quay.io\/repository\/rhn-support-jkastnin\/debian_rootless_podman\">https:\/\/quay.io\/repository\/rhn-support-jkastnin\/debian_rootless_podman<\/a> vor. F\u00fchlt euch frei, diese f\u00fcr eigene Experimente zu benutzen, doch verwendet sie nur mit Vorsicht in Produktion. Ich erzeuge diese Images nur nach Bedarf neu, so dass die ver\u00f6ffentlichen Versionen veraltet und voller Sicherheitsl\u00fccken sein k\u00f6nnen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Validierung<\/h2>\n\n\n\n<p>Jetzt, wo die Images fertig sind, kann ich pr\u00fcfen, ob sich rootless Podman darin auch wie gew\u00fcnscht ausf\u00fchren l\u00e4sst.<\/p>\n\n\n\n<p>Die Prozesse innerhalb des von meinem Container-Image instanziierten Containers laufen als Benutzer <em>root<\/em>. Um die Prozesse als Benutzer <em>podman<\/em> auszuf\u00fchren, ist dies beim Aufruf von <code>podman run<\/code> explizit mit anzugeben. Der folgende Code-Block verdeutlicht dies und zeigt zugleich den ersten Fehler beim Versuch rootless Podman auszuf\u00fchren.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>]$ podman run --rm localhost\/debian_rootless_podman:bookworm-2024-09-21 id\nuid=0(root) gid=0(root) groups=0(root)\n]$ podman run --rm --user podman localhost\/debian_rootless_podman:bookworm-2024-09-21 id\nuid=1000(podman) gid=1000(podman) groups=1000(podman)\n]$ podman run --rm --security-opt label=disable --user podman --device \/dev\/fuse localhost\/debian_rootless_podman:bookworm-2024-09-21 podman info\ntime=\"2024-09-21T18:43:35Z\" level=error msg=\"running `\/usr\/bin\/newuidmap 15 0 1000 1 1 1 999 1000 1001 64535`: newuidmap: write to uid_map failed: Operation not permitted\\n\"\nError: cannot set up namespace using \"\/usr\/bin\/newuidmap\": exit status 1<\/code><\/pre>\n\n\n\n<p>Der Fehler deutet auf fehlende <code>capabilities(7)<\/code> hin. Um diese Hypothese zu testen, wiederhole ich den letzten Befehl mit der Option <code>--privileged<\/code> (siehe dazu <code>podman-run(1)<\/code>):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>]$ podman run --rm --security-opt label=disable --user podman --device \/dev\/fuse --privileged localhost\/debian_rootless_podman:bookworm-2024-09-21 podman info\nhost:\n\u2026<\/code><\/pre>\n\n\n\n<p>Damit funktioniert es. Leider geben sich viele Menschen an dieser Stelle mit dem Ergebnis zufrieden. Doch ich m\u00f6chte diese Container nicht einfach mit <code>--privileged<\/code> ausf\u00fchren. Also studiere ich die Manpage <code>capabilities(7)<\/code> und teste mich St\u00fcck f\u00fcr St\u00fcck heran, bis ich mit dem folgenden Kommando ebenfalls erfolgreich bin:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>]$ podman run  --rm --user podman --security-opt label=disable --device \/dev\/fuse --cap-add=setuid,setgid,sys_admin,chown localhost\/debian_rootless_podman:bookworm-2024-09-21 podman info\nhost:\n\u2026<\/code><\/pre>\n\n\n\n<p>Dies ist schon deutlich besser, da dem Container hiermit deutlich weniger Privilegien einger\u00e4umt werden m\u00fcssen. Das Thema Container-Privilegien und <code>capabilities(7)<\/code> werde ich noch genauer untersuchen. Eventuell folgt dazu dann auch ein weiterer Artikel. F\u00fcr den Moment ist das Ergebnis gut genug.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quellen und weiterf\u00fchrende Links<\/h2>\n\n\n\n<ol id=\"eins\" class=\"wp-block-list\">\n<li><a href=\"https:\/\/buildah.io\/\">Buildah.io<\/a><\/li>\n\n\n\n<li>Ansible Collection <a href=\"https:\/\/codeberg.org\/tronde\/nextcloud\">tronde.nextcloud<\/a><\/li>\n\n\n\n<li>Mein Wochenendprojekt <a href=\"https:\/\/www.my-it-brain.de\/wordpress\/nextcloud-im-container-teil-1-der-plan\/\">Nextcloud im Container<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/catalog.redhat.com\/search?q=podman&amp;searchType=containers&amp;product_listings_names=Red%20Hat%20Universal%20Base%20Image%208|Red%20Hat%20Universal%20Base%20Image%209&amp;p=1\">Podman Images im Red Hat Catalog<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/podman.io\/blogs\/2018\/10\/31\/podman-buildah-relationship.html\">Buildah and Podman Relationship<\/a> von Tom Sweeney<\/li>\n\n\n\n<li><a href=\"https:\/\/ansible.readthedocs.io\/projects\/molecule\/\">About Ansible Molecule<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.redhat.com\/sysadmin\/podman-inside-container\">How to use Podman inside of a container<\/a> by Dan Walsh<\/li>\n\n\n\n<li><a href=\"https:\/\/ansible.readthedocs.io\/projects\/molecule\/examples\/podman\/\">Using podman containers<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/containers\/podman\/discussions\/23838\">Having trouble getting started with rootless podman running rootless podman inside a Debian 12 image #23838<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/container-registry\/container-registry-image-tag-version\">Recommendations for tagging and versioning container images<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Dieser Artikel gibt meine Motivation f\u00fcr den Bau von Container-Images und die Vorgehensweise wieder und zeigt, wie ich mit Buildah meine OCI-kompatiblen Container-Images erstelle. Es handelt sich dabei mehr um einen Erfahrungsbericht als ein Tutorial und ich erhebe keinen Anspruch auf Vollst\u00e4ndigkeit. Das behandelte Beispiel ist jedoch zum Einstieg und zur Nachahmung f\u00fcr all jene<span class=\"continue-reading\"> <a href=\"https:\/\/www.my-it-brain.de\/wordpress\/buildah-baut-meine-container-images\/\">[Weiterlesen&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_metis_text_type":"","_metis_text_length":0,"_post_count":0,"footnotes":""},"categories":[51],"tags":[850,517,849,430,305],"class_list":["post-3947","post","type-post","status-publish","format-standard","hentry","category-linux","tag-buildah","tag-container","tag-oci","tag-osbn","tag-planet"],"_links":{"self":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts\/3947","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/comments?post=3947"}],"version-history":[{"count":8,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts\/3947\/revisions"}],"predecessor-version":[{"id":3958,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/posts\/3947\/revisions\/3958"}],"wp:attachment":[{"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/media?parent=3947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/categories?post=3947"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.my-it-brain.de\/wordpress\/wp-json\/wp\/v2\/tags?post=3947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}