Aller au contenu

Build d'images multi-plateformes avec Buildkit

logo docker

Voici une feature de Docker builtx des plus sympathiques. On peut en une seule commande lancer la construction d’images Docker à destination de plusieurs architectures comme les raspberry p1 et même les instances AWS A1.

Pour rappel buildkit est le nouveau moteur de construction d’images intégrant pas mal de nouvelles fonctionnalités.

Et celle-ci vaut le détour. Surtout si vous utilisez plusieurs architectures, linux/amd64, linux/arm64, ou darwin/amd64, … au sein de votre infrastructure.

Mise en place du builder

Le générateur est en fait une image, utilisant les fonctionnalités QEMU, qui tourne au sein d’une instance Docker. Une feature que l’on retrouve sur la version de Docker Desktop d’ailleurs mais caché.

Faisons le test, bien sûr dans une machine vagrant. En premier, il faut lancer une image permettant d’installer l’outillage nécessaire sur la machine hôte.

Terminal window
docker run --privileged --rm tonistiigi/binfmt --install all
Unable to find image 'tonistiigi/binfmt:latest' locally
latest: Pulling from tonistiigi/binfmt
981e1f26977e: Pull complete
6269e8ad230e: Pull complete
Digest: sha256:11128304bc582dc7dbaa35947ff3e52e2610d23cecb410ddfa381a6ce74fa763
Status: Downloaded newer image for tonistiigi/binfmt:latest
installing: arm64 OK
installing: mips64 OK
installing: mips64le OK
installing: arm OK
installing: s390x OK
installing: ppc64le OK
installing: riscv64 OK
{
"supported": [
"linux/amd64",
"linux/arm64",
"linux/riscv64",
"linux/ppc64le",
"linux/s390x",
"linux/386",
"linux/arm/v7",
"linux/arm/v6"
],
"emulators": [
"qemu-aarch64",
"qemu-arm",
"qemu-mips64",
"qemu-mips64el",
"qemu-ppc64le",
"qemu-riscv64",
"qemu-s390x"
]
}

La messe est dite en sortie, on pourra avoir des builds à destination de toutes ces archis.

Ensuite vient la création du builder. Pour utiliser une registry perso, il faudra utiliser un fichier de config. Plus d’infos ici Pour appeler ce fichier, il faudra ajouter --config=/path/to/config.toml

Impact : Je n’utiliserai donc pas l’option --push car pour le moment je n’ai pas réussi à le faire tourner en local. L’option --load n’accepte pas pour le moment de lancer plusieurs builds en //.

Terminal window
docker buildx create --driver-opt network=host --driver docker-container --name mybuilder --use
mybuilder
docker buildx inspect --bootstrap
[+] Building 78.2s (1/1) FINISHED
=> [internal] booting buildkit 78.2s
=> => pulling image moby/buildkit:buildx-stable-1 77.6s
=> => creating container buildx_buildkit_mybuilder0 0.6s
Name: mybuilder
Driver: docker-container
Nodes:
Name: mybuilder0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6

Nous allons utiliser une image plutôt simple :

1.3
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "Je cours sur $BUILDPLATFORM, je construis pour $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

Allez, on lance le build (ca va prendre un peu de temps la première fois):

Terminal window
docker buildx build --platform linux/arm64 -t localhost:5000/test-arm64 --load .
[+] Building 0.6s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 315B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 0.3s
=> [internal] load metadata for docker.io/library/golang:alpine 0.3s
=> [build 1/2] FROM docker.io/library/golang:alpine@sha256:5519c8752f6b53fc8818dc46e9fda628c99c4e8fd2d2f1df71e1f184e71f47dc 0.0s
=> => resolve docker.io/library/golang:alpine@sha256:5519c8752f6b53fc8818dc46e9fda628c99c4e8fd2d2f1df71e1f184e71f47dc 0.0s
=> [stage-1 1/2] FROM docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a 0.1s
=> => resolve docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a 0.0s
=> CACHED [build 2/2] RUN echo "Je cours sur linux/amd64, je construis pour linux/arm64" > /log 0.0s
=> CACHED [stage-1 2/2] COPY --from=build /log /log 0.0s
=> exporting to oci image format 0.2s
=> => exporting layers 0.0s
=> => exporting manifest sha256:17e7bcf7fcb6895660bb46c5c6a5b520eadec6406c07b4b2bf310942d6485e0d 0.0s
=> => exporting config sha256:0a8db5b1ffe94f49fe3be55c3d12b68c96f3fd0deda6bedfa034ecc4d7763ba4 0.0s
=> => sending tarball 0.1s
=> importing to docker 0.0s

On lance une deuxième plateforme :

Terminal window
docker buildx build --platform linux/amd64 -t localhost:5000/test-amd64 --load .
=> => exporting config sha256:8216a415ef7aa768684334e16ef91fda415f56d15cfd0277306d31b8d4e71d4a 0.0s
=> => sending tarball 0.2s
=> importing to docker

Allez, on vérifie :

Terminal window
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost:5000/test-amd64 latest 8216a415ef7a 34 seconds ago 5.59MB
localhost:5000/test-arm64 latest 0a8db5b1ffe9 7 minutes ago 5.33MB

Si vous avez une registry, vous pouvez utiliser l’option --push avec plusieurs plateformes dans la même ligne de commande. Pour les autres un simple script fera l’affaire.

Pour améliorer les perfs on peut ajouter plusieurs nœuds à votre builder avec la fonction --append.

Franchement ça déchire.

Sources :