Neuigkeiten von trion.
Immer gut informiert.

Docker-Registry auf ARM 64 - Cross-Compiling Docker-Images

Docker

Bisher gibt es die offizielle Docker Registry lediglich als x86 Docker Image. Damit ist ein Betrieb unter ARM oder ARM 64 aktuell nicht möglich. Obwohl Docker Multi-Arch-Support eingeführt hat, also die Möglichkeit, unter demselben Image-Namen verschiedene Architekturen zu bedienen, ist das bei dem hauseigenen Image leider noch nicht umgesetzt worden. In diesem Beitrag geht es nun darum, ein passendes Image durch Cross-Compilation zu erstellen und damit eine Registry bereitzustellen.

Der Betrieb des mit Docker erstellten Image wird unter CRI-O erfolgen, womit die Interoperabilität, dank der Standardisierung von OCI, unter Beweis gestellt wird.

Cross-Build Setup

Um auf Intel- / AMD-64-Architekturen ARM-Images auszuführen, behilft man sich einiger Tricks: Zum einen wird QEMU verwendet, um eine andere Architektur zu simulieren. Damit lässt sich nicht nur ARM emulieren, auch Exoten wie ein s390 IBM-Host sind möglich. Zum anderen wird das im Linux-Kernel vorhandene binfmt Feature genutzt, um sicherzustellen, dass QEMU zur Ausführung von Programmen verwendet wird, wenn die aktuelle Plattform eine andere ist als die Zielplattform des Programms.

Damit lassen sich - ein geeignetes Base-Image vorausgesetzt - auf jeder Plattform Docker-Images für beliebige Zielplattformen bauen.
(Ein paar Voraussetzungen, wie z.B. ein statisch gelinktes QEMU im Image, werden an dieser Stelle nicht weiter betrachtet.)

Aus dem Docker-Repository multiarch können praktischerweise entsprechende Base-Images bezogen werden.

Zusätzlich wird auch ein Image bereitgestellt, mit dem die notwendige Einrichtung des binfmt Supports einfach umzusetzen ist: multiarch/qemu-user-static:register

Konfiguration von QEMU binfmt Regeln
$ docker run --rm --privileged multiarch/qemu-user-static:register --reset
Setting /usr/bin/qemu-alpha-static as binfmt interpreter for alpha
Setting /usr/bin/qemu-arm-static as binfmt interpreter for arm
Setting /usr/bin/qemu-armeb-static as binfmt interpreter for armeb
...

Ist dieser Schritt erledigt, kann testweise ein ARM-64-Image verwendet werden, um darin eine Shell auszuführen.

Ausführung einer Shell im ARM-64-Image unter x86-Architektur
$ docker run -it --rm multiarch/alpine:aarch64-latest-stable /bin/sh
/ \# uname -a
Linux 35f7362a7642 4.15.0-32-generic \#35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 aarch64 Linux
/ \#

Als Nächstes kann das Docker-Image für die Docker-Registry gebaut werden.

Cross-Build Docker-Registry für ARM

Da es bereits einen Pull-Request gibt, um Multi-Arch-Support für das offizielle Registry-Image einzuführen, kann das zugrundeliegende git-Projekt als Basis verwendet werden. Das Projekt wird also lokal geclont und der passende Branch ausgecheckt.

Clone und checkout des Docker-Registry Multiarch-Projekts
$ git clone https://github.com/tianon/docker-registry-image.git
$ cd docker-registry-image/
$ git checkout multiarch

Theoretisch kann nun mittels docker build -t registry . das Image gebaut werden. Doch hier zeigt sich, dass eine Emulation nicht immer perfekt funktioniert: Abgesehen von der niedrigeren Ausführungsgeschwindigkeit von QEMU verhält sich das System auch etwas anders.

Das fällt nun auch auf, während der Go-Compiler die Sourcen der Docker-Registry übersetzt. Bei älteren Go-Versionen wurde das Verhalten toleriert, neuere Go-Versionen brechen jedoch die Compilierung mit einem Fehler ab.
(Siehe Ticket https://github.com/golang/go/issues/24656 )

Zum Glück kann die Docker-Registry auch mit einem älteren Go-Compiler übersetzt werden. Dazu wird die FROM Zeile im Dockerfile der Docker Registry auf eine ältere Version von Alpine konfiguriert, die dann auch ältere Go-Werkzeuge mitbringt:

Verwendung einer älteren Alpine- und Go-Version
FROM multiarch/alpine:aarch64-v3.7

Nun kann das Docker-Image ganz normal gebaut werden.

Docker-Registry-Image bauen
$ docker build -t registry:2 .
...

Anschließend lässt sich das Image durch docker save exportieren und auf ein Zielsystem kopieren. Dort kann das Image mit docker load importiert werden.

Export des Docker-Image und Import auf anderem System
$ docker save registry:2 -o registry.arm64.img
$ scp registry.arm64.img 10.23.15.1:~/
...
$ docker load registry.arm64.img

Verwendung mit CRI-O und runc

Das mit Docker gebaute Image der Docker-Registry soll nun mit CRI-O bzw. runc als Container-Umgebung in einem Kubernetes-Cluster betrieben werden. Anders als Docker kann CRI-O zwar Images pushen und pullen, jedoch nicht importieren oder exportieren. Die Einrichtung von Kubernetes mit CRI-O auf ARM 64 beschreibt der Beitrag Kubernetes mit CRI-O auf Arch Linux ARM.

Dafür gibt es z.B. das Werkzeug skopeo, das mit OCI und Docker-v2-Images arbeiten kann. Damit lässt sich das mit Docker exportierte Image durch das copy Kommando importieren.

Import des ARM Docker Image in Container-Storage Verzeichnis
$ skopeo copy docker-archive:registry.arm64.img \ containers-storage:docker.io/library/registry:2
INFO[0000] [graphdriver] using prior storage driver: overlay
Getting image source signatures
Copying blob sha256:e2f253d59b1422b0bb333773aa217ffdd89e9cfc0214caf8a08e48b27eddc4d9
 10.30 MB / 10.30 MB [======================================================] 1s
Copying blob sha256:c2df8775fe81d7e3acb7a33c0940f5b8334163a21d2000ed43de7605207b45fe
 3.69 MB / 3.69 MB [========================================================] 0s
Copying blob sha256:523414ad9b9696ae9922afa266c35648ff656ab73d9d8599da8a8c0cbd11dd59
 1.52 MB / 1.52 MB [========================================================] 0s
Copying blob sha256:2763f2733114afebfc8ad24677ecf953ec03dde6d233cbe72cbcfd0a702d0e47
 25.46 MB / 25.46 MB [======================================================] 3s
Copying blob sha256:41dfcef05d2375ce2a0a3f1641afd16ec8f941fcc1cb2a9327a4e65dd03cb33d
 2.00 KB / 2.00 KB [========================================================] 0s
Copying config sha256:fd4ea11aea75810d7111538c91b04fe4ff96df7c17cf0c64c2d644cad0459e55
 4.14 KB / 4.14 KB [========================================================] 0s
Writing manifest to image destination
Storing signatures

Anschließend zeigt crictl das importierte Image an, und es steht bereit, um daraus Container-Instanzen zu erzeugen.

Anzeige der Images mit crictl und CRI-O
[root@c2-master0 ~]# crictl images
IMAGE                                TAG     IMAGE ID        SIZE
docker.io/library/registry           2       fd4ea11aea758   43MB

Wie plattformspezifische Images über ein Manifest unter einem gemeinsamen Namen bereitgestellt werden, beschreibt der Beitrag Docker Multi-Arch-Image. Cross-Builds mit QEMU für mehrere Architekturen in einem Schritt zeigt der Beitrag Multiarch Builds mit Docker Buildx.




Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per E-Mail an [email protected] oder über das Kontaktformular. Wir freuen uns auf eine Kontaktaufnahme!

Suche

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.