Skip to content

Buildx

🎬 Introduction

Buildx est la grande sƓur de la commande build. Elle en reprend les concepts tout en offrant des fonctionnalitĂ©s plus avancĂ©es et puissantes.

Elle est la commande CLI permettant d’utiliser les fonctionnalitĂ©s du nouveau moteur de construction : BuildKit. Les principales innovations proviennent de BuildKit, tandis que Buildx sert Ă  les exposer.

De nombreuses fonctionnalités ont été apportées avec la création de BuildKit et Buildx :

  • amĂ©lioration de la gestion du cache
  • accĂ©lĂ©ration des temps de build
  • meilleure isolation
  • prise en charge des secrets et des connexions SSH
  • capacitĂ© de construire des images multi-architectures.

Profitez de cette partie pour découvrir plusieurs de ces nouveautés et ce que cela peut vous offrir par rapport à la commande build !

Evolution fréquentes

Buildx Ă©volue encore beaucoup ; si la commande vous intĂ©resse, n’hĂ©sitez pas Ă  suivre l’actualitĂ© sur le site de Docker.

🛂 Contrîle

Vérifiez que la commande est disponible sur votre poste :

docker buildx version

Docker buildx est disponible

Sortie attendue
$ github.com/docker/buildx v0.30.1

â„č La version courante est la v0.30.1.

Docker compose n'est pas disponible

Besoin d’installer la commande ? La documentation relative à son installation est disponible ici : Install buildx

🚀 C'est parti

Documentation

N’hĂ©sitez pas Ă  aller faire un tour sur la documentation : Documentation buildx

🐣 Mon premier builder / build

Buildx utilise des builders pour construire des images Docker. Un builder est une instance de BuildKit configurée pour réaliser les builds.

Celui fourni par dĂ©faut par Docker fonctionne pour des builds simples, mais ne prend pas en charge les fonctionnalitĂ©s avancĂ©es de Buildx, comme le multi-architecture, la gestion avancĂ©e du cache, les secrets ou le SBOM. Ce builder est donc davantage adaptĂ© Ă  docker build qu’à docker buildx.

Vous voulez voir ce qui se passe avec le builder par défaut ? Voici un exemple de build avec un Dockerfile que nous utiliserons un peu plus tard et qui contient du multiplateforme.

Tentative de build
    docker buildx build -f Dockerfile.multi.yml --platform linux/amd64,linux/arm64 -t localhost:5000/nginx-test:multi --push .
    [+] Building 0.0s (0/0)                                                                                  docker:default
    ERROR: failed to build: Multi-platform build is not supported for the docker driver.
    Switch to a different driver, or turn on the containerd image store, and try again.
    Learn more at https://docs.docker.com/go/build-multi-platform/

Maintenant, il est temps de crĂ©er notre propre builder. Buildx nĂ©cessite que le builder soit configurĂ© avec des informations prĂ©cises sur l’OS et l’architecture que nous allons utiliser.

Question

Depuis votre terminal, créer votre builder

  • donnez‑lui un nom
  • on souhaite un builder de type docker-container
  • ajouter l'option --driver-opt network=host pour la gestion d'un registry local
  • lancez directement son exĂ©cution

Le build a été réalisé avec succÚs

[+] Building 170.5s (1/1) FINISHED
=> [internal] booting buildkit                                                                                                           170.5s
=> => pulling image moby/buildkit:buildx-stable-1                                                                                        169.2s
=> => creating container buildx_buildkit_mybuilder0                                                                                        1.4s
mybuilder

Vous pouvez vérifier que votre builder s'est bien ajouté à la liste.

docker buildx ls
NAME/NODE        DRIVER/ENDPOINT                   STATUS    BUILDKIT   PLATFORMS
mybuilder        docker-container
\_ mybuilder0    \_ unix:///var/run/docker.sock   running   v0.26.2    linux/amd64 (+3), linux/386
default*         docker
\_ default       \_ default                       running   v0.26.2    linux/amd64 (+3)
La solution est lĂ ... —
Terminal
docker buildx create --name mybuilder --use --driver docker-container --driver-opt network=host --bootstrap

Il est l’heure de construire notre image pour la partie backend de notre application et de tester notre builder. SĂ©lectionner le builder que nous avons créé avec la commande.

docker buildx use mybuilder

Question

Placez‑vous dans backend et rĂ©alisez le build du Dockerfile avec Buildx :

  • construisez l'image, la nommer app-backend et la tagger buildx
  • utilisez le Dockerfile prĂ©sent backend/Dockerfile

Vous pouvez exécuter la commande suivante pour visualiser le moteur utilisé pour la construction :

docker buildx ls

Le build est bien réalisé

[+] Building 1.5s (20/20) FINISHED                                                           docker-container:mybuilder
=> [internal] load build definition from Dockerfile                                                               0.0s
=> => transferring dockerfile: 824B                                                                               0.0s
=> [internal] load metadata for docker.io/library/alpine:3                                                        1.0s
=> [internal] load metadata for docker.io/library/golang:1.25.0-alpine                                            1.1s
=> [auth] library/alpine:pull token for registry-1.docker.io                                                      0.0s
=> [auth] library/golang:pull token for registry-1.docker.io                                                      0.0s
=> [internal] load .dockerignore                                                                                  0.0s
=> => transferring context: 132B                                                                                  0.0s
=> [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfd  0.1s
=> => resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6  0.1s
=> [stage-1 1/6] FROM docker.io/library/alpine:3@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2  0.1s
=> => resolve docker.io/library/alpine:3@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412  0.1s
=> [internal] load build context                                                                                  0.0s
=> => transferring context: 748B                                                                                  0.0s
=> CACHED [stage-1 2/6] WORKDIR /app                                                                              0.0s
=> CACHED [stage-1 3/6] RUN apk --no-cache add ca-certificates                                                    0.0s
=> CACHED [build 2/7] WORKDIR /app                                                                                0.0s
=> CACHED [build 3/7] COPY go.mod go.sum ./                                                                       0.0s
=> CACHED [build 4/7] RUN go mod download                                                                         0.0s
=> CACHED [build 5/7] COPY . .                                                                                    0.0s
=> CACHED [build 6/7] RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server                                0.0s
=> CACHED [build 7/7] RUN mkdir -p /app/uploads                                                                   0.0s
=> CACHED [stage-1 4/6] COPY --from=build /app/server /app/                                                       0.0s
=> CACHED [stage-1 5/6] COPY --from=build /app/uploads /app/uploads                                               0.0s
=> CACHED [stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads                                0.0s
La solution est lĂ ... —
Terminal
docker buildx build -t app-backend:buildx .

On va maintenant regarder les images que nous avons Ă  disposition en local.

$ docker images
REPOSITORY      TAG               IMAGE ID       CREATED       SIZE
moby/buildkit   buildx-stable-1   de10faf919fc   6 weeks ago   333MB

Surprise, il n'y a pas notre image app-backend:buildx !

Par dĂ©faut, Buildx ne crĂ©e pas d’image Docker locale. On peut cependant lui demander d’exporter l’image vers Docker Engine avec l’option --load.

docker buildx build -t app-backend:buildx . --load
docker images
REPOSITORY      TAG               IMAGE ID       CREATED         SIZE
app-backend     buildx            fdd8dd3fe59a   2 minutes ago   49.3MB
moby/buildkit   buildx-stable-1   de10faf919fc   6 weeks ago     333MB

D’accord, mais pourquoi ce changement par rapport à build ?

Buildx et BuildKit portent un intĂ©rĂȘt plus important Ă  l'approche CI/CD et GitOps. L’objectif est de disposer d’une source de vĂ©ritĂ© commune et de limiter les builds locaux et isolĂ©s, pour garantir des builds reproductibles.

Cette option a des limites et n'est pas compatible avec certaines autres fonctionnalités (multi-architecture, builder distant).

Builder et registry local

Pour qu’il soit possible de push sur notre registry local, il est nĂ©cessaire que le builder que l’on utilise le permette. C’est Ă  cela que l’option --driver-opt network=host servait lors de la construction de notre builder.

Pour profiter pleinement de buildx, nous allons créer un registry !

Question

Lancer un conteneur avec un registry en local

  • utiliser l'image registry:2
  • exposer sur le port 5000

Visualiser l'ID du conteneur Registry

    fb73136109d0118f24673df07e4cef16a9b3ecb5db06e546d0fe4c274638439a
La solution est lĂ ... —
Commande pour construire l'image avec SBOM une attestation et un label
docker run -d -p 5000:5000 --name registry-local registry:2

Comme on a fait pour charger l'image en local, on va maintenant utiliser l'autre option pour push l'image dans notre nouvelle registry locale

Pusher l'image dans le registry local

  • tagger l'image avec l'adresse du registry local localhost:5000/app-backend:buildx
  • utiliser l'option --push pour envoyer l'image vers le registry

L'image est bien poussée dans le registry

[...]
=> => pushing layers                                                                                                                           0.2s
=> => pushing manifest for localhost:5000/app-backend:buildx@sha256:aaaaa                                                                                      0.1s
La solution est lĂ ... —
Terminal
docker buildx build -t localhost:5000/app-backend:buildx . --push

đŸ·ïž MĂ©tadonnĂ©es

Avec Buildx, il est possible d’ajouter et de gĂ©nĂ©rer des mĂ©tadonnĂ©es pour les images Docker, comme des fichiers SBOM ou des attestations de provenance. Ces informations permettent de tracer, auditer et sĂ©curiser les builds. Pour les mĂ©tadonnĂ©es, toutes les informations ne sont pas disponibles avec des builds en local (load).

🌍 Provenance

Documentation

N’hĂ©sitez pas Ă  aller faire un tour sur la documentation : Documentation attestation

L'argument --attest type=provenance fait partie des fonctionnalités supplémentaires de Buildx.

Il fournit une attestation de provenance, qui contient le builder utilisé, la source (hash des fichiers copiés), les dépendances du build (Go modules, packages systÚme), etc.

Ces informations sont attachĂ©es au registry, mais Buildx peut aussi les exposer via le cache Docker, sans qu’il y ait une image en local. Par exemple, si vous rĂ©alisez un build sans prĂ©ciser si vous voulez faire un push (distant) ou un load (local).

On peut donc récupérer des infos sans push/pull, mais pas toutes : le cache est exploitable localement pour certaines vérifications.

📁 SBOM

Documentation

N’hĂ©sitez pas Ă  aller faire un tour sur la documentation : Documentation SBOM

L'argument --attest=type=sbom permet, lui, de lister toutes les dépendances et composants utilisés dans l'image. Cela permet un inventaire automatisé des composants logiciels !

Les métadonnées avancées comme le SBOM ou les attestations de provenance sont attachées aux manifests dans le registry et ne sont pas directement lisibles avec les commandes Docker classiques. Pour y accéder, il faut soit générer le SBOM en local via Buildx, soit utiliser un outil comme Cosign pour récupérer et vérifier les attestations stockées dans le registry. Pour simplifier les choses, on va réaliser nos tests en local.

Question

Lancer le build de l'image Docker avec les arguments suivants :

  • utiliser l'argument **--attest type=provenance** en mode min et avec la version v1
  • utiliser le Dockerfile de backend
  • utiliser l'argument --sbom=true

L'image build

    [+] Building 17.3s (25/25) FINISHED                                                                                                                                                                                                                                                                                      docker-container:mybuilder
    => [internal] load build definition from Dockerfile                                                                                                                                                                                                                                                                                           0.0s
    => => transferring dockerfile: 825B                                                                                                                                                                                                                                                                                                           0.0s
    => resolve image config for docker-image://docker.io/docker/buildkit-syft-scanner:stable-1                                                                                                                                                                                                                                                    0.8s
    => [auth] docker/buildkit-syft-scanner:pull token for registry-1.docker.io                                                                                                                                                                                                                                                                    0.0s
    => [internal] load metadata for docker.io/library/alpine:3                                                                                                                                                                                                                                                                                    0.3s
    => [internal] load metadata for docker.io/library/golang:1.25.0-alpine                                                                                                                                                                                                                                                                        0.5s
    => [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                                                                                                                                                                                  0.0s
    => [auth] library/golang:pull token for registry-1.docker.io                                                                                                                                                                                                                                                                                  0.0s
    => [internal] load .dockerignore                                                                                                                                                                                                                                                                                                              0.0s
    => => transferring context: 132B                                                                                                                                                                                                                                                                                                              0.0s
    => docker-image://docker.io/docker/buildkit-syft-scanner:stable-1                                                                                                                                                                                                                                                                             0.2s
    => => resolve docker.io/docker/buildkit-syft-scanner:stable-1                                                                                                                                                                                                                                                                                 0.2s
    => [stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                                                                                                                                                                                                                      0.1s
    => => resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                                                                                                                                                                                                                              0.1s
    => [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440                                                                                                                                                                                                            0.1s
    => => resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440                                                                                                                                                                                                                  0.1s
    => [internal] load build context                                                                                                                                                                                                                                                                                                              0.3s
    => => transferring context: 484.40kB                                                                                                                                                                                                                                                                                                          0.3s
    => CACHED [build 2/7] WORKDIR /app                                                                                                                                                                                                                                                                                                            0.0s
    => CACHED [build 3/7] COPY go.mod go.sum ./                                                                                                                                                                                                                                                                                                   0.0s
    => CACHED [build 4/7] RUN go mod download                                                                                                                                                                                                                                                                                                     0.0s
    => [build 5/7] COPY . .                                                                                                                                                                                                                                                                                                                       1.1s
    => [build 6/7] RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server                                                                                                                                                                                                                                                                  13.5s
    => [build 7/7] RUN mkdir -p /app/uploads                                                                                                                                                                                                                                                                                                      0.2s
    => CACHED [stage-1 2/6] WORKDIR /app                                                                                                                                                                                                                                                                                                          0.0s
    => CACHED [stage-1 3/6] RUN apk --no-cache add ca-certificates                                                                                                                                                                                                                                                                                0.0s
    => CACHED [stage-1 4/6] COPY --from=build /app/server /app/                                                                                                                                                                                                                                                                                   0.0s
    => CACHED [stage-1 5/6] COPY --from=build /app/uploads /app/uploads                                                                                                                                                                                                                                                                           0.0s
    => CACHED [stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads                                                                                                                                                                                                                                                            0.0s
    => CACHED [linux/amd64] generating sbom using docker.io/docker/buildkit-syft-scanner:stable-1                                                                                                                                                                                                                                                 0.0s
    => exporting to client directory                                                                                                                                                                                                                                                                                                              0.6s
    => => copying files 32.87MB   
La solution est lĂ ... —
Terminal
docker buildx build --sbom=true --output type=local,dest=out --attest type=provenance,mode=min,version=v1 -t localhost:5000/app-backend:buildx .

Il est temps de visualiser les métadonnées.

Pour le SBOM, on affiche le contenu du fichier sbom.spdx.json créé localement.

head -30 out/sbom.spdx.json

Exemple de SBOM

    {
    "_type": "https://in-toto.io/Statement/v0.1",  # Type d’attestation in-toto, version 0.1, permet de tracer et de sĂ©curiser la chaĂźne d’approvisionnement logicielle
    "predicateType": "https://spdx.dev/Document",  # Type du document attaché : ici un SBOM au format SPDX
    "subject": [  # Liste des artefacts/fichiers contenus dans l’image
        {
        "name": "app/server",  # Chemin du fichier dans l’image
        "digest": {
            "sha256": "033f5694e78ac79b0521d5c7f385369a3b9b16bf61ec0fed843e4dd46c035839"  # Hash SHA256 du fichier pour vérifier son intégrité
        }
        },
        {
        "name": "bin/busybox", 
        "digest": {
            "sha256": "f3547b3d78d08a028a4833ddb83b77cf012838c078bfd2b76355f53d1d8bba62"
        }
        },
        {
        "name": "etc/alpine-release",
        "digest": {
            "sha256": "6edb469729fc9d6c726e124e76ec6eb816493f0416605ce2c4d13fb38ae7417e" 
        }
        },
        {
        "name": "etc/apk/arch", 
        "digest": {
            "sha256": "aaf631698ae5160ceb04a97681a14887fdcab47cd6e0f163c87485b3b1340b62" 
        }

Pour la partie provenance, on va utiliser le cache de Docker : 

docker buildx imagetools inspect localhost:5000/app-backend:buildx

Exemple de provenance

    Name:      localhost:5000/app-backend:buildx
    MediaType: application/vnd.oci.image.index.v1+json 
    Digest:    sha256:c92fd2ae3abf5130648332498d74daea583e437a7cb7877d282a1ed882157b36 

    Manifests: 
    Name:        localhost:5000/app-backend:buildx@sha256:e752a830f983c70286ff4402329ed69015050ec452522c27e401e9e0019199cf
    MediaType:   application/vnd.oci.image.manifest.v1+json 
    Platform:    linux/amd64  # Plateforme ciblée par ce manifest

    Name:        localhost:5000/app-backend:buildx@sha256:f7ad49ffab5b595e1996b2e1b3e3219ccb8d84fd556380e4bc9bed34c8d4d92f
    MediaType:   application/vnd.oci.image.manifest.v1+json
    Platform:    unknown/unknown
    Annotations:  # Métadonnées attachées au manifest
        vnd.docker.reference.type:   attestation-manifest  # Indique que ce manifest est une attestation (provenance ou SBOM)
        vnd.docker.reference.digest: sha256:e752a830f983c70286ff4402329ed69015050ec452522c27e401e9e0019199cf  # Digest du contenu de l’attestation

En fonction de vos besoins, un usage local ou via un registre offre diffĂ©rentes possibilitĂ©s, avec plus ou moins de simplicitĂ© ou de capacitĂ©s d’analyse.

📐 Multi-architecture

Documentation

N’hĂ©sitez pas Ă  aller faire un tour sur la documentation : Documentation Multi plateforme

Le multi-architecture vise à rendre les images Docker utilisables sur différentes architectures systÚme.

Avec la commande docker build classique, la gestion du multi-architecture Ă©tait complexe. Elle nĂ©cessitait de construire les images sur un environnement correspondant Ă  l’architecture cible. Par exemple, pour produire une image linux/arm64, le build devait ĂȘtre rĂ©alisĂ© sur un environnement linux/arm64. En clair, la construction pour une architecture diffĂ©rente de celle de l’hĂŽte n’était pas supportĂ©e nativement.

Buildx propose plusieurs solutions. Il permet de construire des images pour plusieurs architectures en un seul build.

Auparavant, les images étaient dites single-plateforme. Elles contenaient un seul manifest, qui pointait vers une seule configuration et un seul ensemble de couches.

Buildx propose de changer cette vision. Les images multi-plateforme contiennent un manifest list, qui peut contenir plusieurs manifests, chacun correspondant à une architecture ou un OS différent. Chaque manifest pointe à son tour vers sa propre configuration et son propre ensemble de couches.

Le schéma de la documentation Docker représente trÚs bien ce changement :

Schéma multi architecture

Docker recommande de fournir au minimum des images pour les architectures linux/amd64 et linux/arm64. Lors du pull d’une image, Docker sĂ©lectionne automatiquement la version Ă  tĂ©lĂ©charger en fonction de l’architecture que vous utilisez.

Pour indiquer les différentes architectures que l'on souhaite couvrir, on crée une liste à l'aide de la commande --platform. Vous pouvez aussi utiliser cette option lors d'un pull/run pour forcer la récupération d'une architecture particuliÚre.

Question

Réaliser un pull de l'image nginx avec comme base OS Linux et avec une architecture arm64.

  • Utiliser nginx:latest comme image de base.
  • Suivre la nomenclature /.
  • Utiliser --platform pour sĂ©lectionner l'architecture.
  • VĂ©rifier les informations avec un inspect sur l'image.

Visualiser l'architecture utilisée

[
    {
        "Id": "sha256:de437b5614ad7ef640175c1204414667adecd421752f7ddf3388edd063403a6e",
        "RepoTags": [
            "nginx:latest"
        ],
        "RepoDigests": [
            "nginx@sha256:fb01117203ff38c2f9af91db1a7409459182a37c87cced5cb442d1d8fcc66d19"
        ],
        "Comment": "buildkit.dockerfile.v0",
        "Created": "2025-12-09T22:50:18.400741057Z",
        "Config": {
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NGINX_VERSION=1.29.4",
                "NJS_VERSION=0.9.4",
                "NJS_RELEASE=1~trixie",
                "PKG_RELEASE=1~trixie",
                "DYNPKG_RELEASE=1~trixie"
            ],
            "Entrypoint": [
                "/docker-entrypoint.sh"
            ],
            "Cmd": [
                "nginx",
                "-g",
                "daemon off;"
            ],
            "Labels": {
                "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
            },
            "StopSignal": "SIGQUIT"
        },
        "Architecture": "arm64", #arch
        "Variant": "v8",
        "Os": "linux", #OS
        "Size": 172317468,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/93a3e73e07ef2bf5feaf1d68742b6044186a2d2742ac10a985c5e182ca11ba77/diff:/var/lib/docker/overlay2/118c2cdb5db370acafc09ce720fe218e009f87f1e35e060dc6e772713e25146d/diff:/var/lib/docker/overlay2/4278585c2f5143248eae828e3cd399fb510a937a38bf02894a5f021cc76ee2db/diff:/var/lib/docker/overlay2/bc25746068e65c3d7c1e2646ea0d310e25bf4b40061fb114d3dcceb4c7c4a441/diff:/var/lib/docker/overlay2/f2dd9aaadd731ce991e6eb840982a10d3661a55be858f3410bd26116130464b4/diff:/var/lib/docker/overlay2/a1600e615321bcb963f9d411b4276a11e9e4a9b87d9f25b5bf91100211ef6881/diff",
                "MergedDir": "/var/lib/docker/overlay2/2792481f5a29a0def5f68a0d2c735026637d8e082f0041a1c73044c1d2922a7a/merged",
                "UpperDir": "/var/lib/docker/overlay2/2792481f5a29a0def5f68a0d2c735026637d8e082f0041a1c73044c1d2922a7a/diff",
                "WorkDir": "/var/lib/docker/overlay2/2792481f5a29a0def5f68a0d2c735026637d8e082f0041a1c73044c1d2922a7a/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:742b5304df6eda388fec80a0fc0a7a95d3e920e61c8d3b9dd32efb0fd8f4c3b7",
                "sha256:894e925a9e6174d827393ab132a8a8b71d8f6d2b909bf424e72971a7b274a072",
                "sha256:45c64a060d887b50118bf29249bd31e43d45da5d96fe425439eefa347aee10e8",
                "sha256:b432142ba4b189d00a37c9d2a90f1cb167f30da37d6bf4929442950856050881",
                "sha256:f09e81e666a662c79463ac4821318d4b3f9ba005dd7185047e3976458adb7e01",
                "sha256:440f865dff31a72e6b7f6663f1aff1e32d8767c5912cc7c3851c4304d09ce2cb",
                "sha256:968b8b0456d84792d2e1215a7a91dac84ad8e63375c6075679c64c45d13a0c3b"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]
La solution est lĂ ... —
Terminal
docker pull --platform linux/arm64 nginx
docker image inspect nginx

Il existe trois méthodes pour créer des images disponibles pour différentes plateformes :

  • QEMU : Ă©mule l’architecture cible, quelle que soit l’architecture locale.
  • Plusieurs nƓuds natifs : rĂ©partit le build sur plusieurs machines, chacune exĂ©cutant son architecture native.
  • Compilation croisĂ©e : lorsque le langage et l’outil de build le permettent (comme Go ou Rust), il est possible de compiler des binaires pour une architecture cible depuis une machine source diffĂ©rente.

Pour la suite, je vous propose de nous appuyer sur la cross-compilation, notre application étant développée en Go.

On va débuter avec une image simple pour comprendre certains concepts, puis on passera sur l'image backend de l'application.

Docker met Ă  notre disposition plusieurs variables pour nous aider Ă  rendre nos Dockerfile plus flexibles :

  • TARGETARCH: CPU cible
  • TARGETPLATFORM: OS cible
  • BUILDARCH: CPU d'origine
  • BUILDPLATFORM: OS d'origine

Note

BUILDPLATFORM peut ĂȘtre utilisĂ© sous la forme --platform=$BUILDPLATFORM pour forcer le stage de build Ă  s’exĂ©cuter sur l’architecture native et Ă©viter l’émulation. Chacune de ces variables doit ĂȘtre dĂ©clarĂ©e dans l'image par le biais de la commande ARG. La construction multi-architecture nĂ©cessite d'utiliser un registry pour le push des images.

Question

Dans un nouveau Dockerfile, crĂ©er une image avec deux arguments, TARGETARCH et BUILDPLATFORM, qui vont nous permettre respectivement de connaĂźtre l’architecture CPU utilisĂ©e et la plateforme que l’on cible.

  • utiliser --platform=$BUILDPLATFORM et nginx:alpine comme image de base
  • crĂ©er une commande CMD qui affiche la valeur de BUILDPLATFORM et TARGETARCH
  • construire l'image pour linux/amd64 et linux/arm64
  • push l'image sur le registry local

Image build et push

    [+] Building 1.9s (8/8) FINISHED                                                             docker-container:mybuilder
    => [internal] load build definition from Dockerfile                                                               0.0s
    => => transferring dockerfile: 273B                                                                               0.0s
    => resolve image config for docker-image://docker.io/docker/dockerfile:1                                          0.4s
    => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:b6afd42430b15f2d2a4c5a02b919e98a525b785b1aaff16747  0.0s
    => => resolve docker.io/docker/dockerfile:1@sha256:b6afd42430b15f2d2a4c5a02b919e98a525b785b1aaff16747d2f623364e3  0.0s
    => [linux/amd64 internal] load metadata for docker.io/library/nginx:alpine                                        0.2s
    => [internal] load .dockerignore                                                                                  0.0s
    => => transferring context: 2B                                                                                    0.0s
    => [linux/amd64 1/2] FROM docker.io/library/nginx:alpine@sha256:8491795299c8e739b7fcc6285d531d9812ce2666e07bd3dd  0.0s
    => => resolve docker.io/library/nginx:alpine@sha256:8491795299c8e739b7fcc6285d531d9812ce2666e07bd3dd8db00020ad13  0.0s
    => CACHED [linux/amd64 2/2] RUN echo "Build exécuté sur : linux/amd64"  && echo "Image construite pour : $TARG    0.0s
    => exporting to image                                                                                             0.7s
    => => exporting layers                                                                                            0.0s
    => => exporting manifest sha256:c67bee8b14b3bc2d1d4d56ec6e8af5d2a425f8fd91005b0b1245354b262b6f41                  0.0s
    => => exporting config sha256:33871b51637a2427c58c2f6f9b474e06fb57771321aac4ed4b8b570d22c17964                    0.0s
    => => exporting attestation manifest sha256:933a893740aa1b3098e53336a91d7f25fc62d2a87ca453031510a10ad9f7004c      0.1s
    => => exporting manifest sha256:e05be1717a1e9ce493ef6b33091bb72128a9a134ba320807ba9d526b35a8f7e0                  0.0s
    => => exporting config sha256:9ef21216501d95b291223b11f50ebffa0e666edfb24151cffcf6f09478fa6efb                    0.0s
    => => exporting attestation manifest sha256:fc56a0d2b82cc011d5db3fbfee51c97eef6fcb4a73a3ae0fa946dcef9456e477      0.1s
    => => exporting manifest list sha256:971a10b1fe13e47305ee8825db8bb1212a5e790fc1cd8c3d3bce4a5fbe147b37             0.0s
    => => pushing layers                                                                                              0.2s
    => => pushing manifest for localhost:5000/nginx-test:multi@sha256:971a10b1fe13e47305ee8825db8bb1212a5e790fc1cd8c  0.1s
La solution est lĂ ... —

Dockerfile.multi.yml
    # syntax=docker/dockerfile:1
    # Définit la version de la syntaxe Dockerfile à utiliser.

    FROM --platform=$BUILDPLATFORM nginx:alpine
    ARG BUILDPLATFORM
    ARG TARGETTARGETARCHPLATFORM

    RUN echo "Build exécuté sur : $BUILDPLATFORM" \
    && echo "Image construite pour : $TARGETARCH" \
    && nginx -v
Commande
    docker buildx build -f Dockerfile.multi.yml --platform linux/amd64,linux/arm64 -t localhost:5000/nginx-test:multi --push .

Question

Vérifier que le manifest de l'image dispose bien de: linux/amd64 et linux/arm64.

  • Utiliser la commande imagetools inspect

Visualiser le cache

    Name:      localhost:5000/nginx-test:multi
    MediaType: application/vnd.oci.image.index.v1+json
    Digest:    sha256:2c99524bc93d71f3961175557dab5453583a8316606cc4ddc45300f3c50de2b7

    Manifests:
    Name:        localhost:5000/nginx-test:multi@sha256:c6410e6eb22169edaf67dec59f6af204b97c4d2387e433f55602a3e7493de921
    MediaType:   application/vnd.oci.image.manifest.v1+json
    Platform:    linux/amd64

    Name:        localhost:5000/nginx-test:multi@sha256:a556a2b2a0633fa8d274699c27ba9ee2c0a6a8dec44241ef5b95a3c658966ca0
    MediaType:   application/vnd.oci.image.manifest.v1+json
    Platform:    linux/arm64

    Name:        localhost:5000/nginx-test:multi@sha256:2f206addb988b50e4914e98586a9ac1e30efb6146d139609e8ef655711adc3f7
    MediaType:   application/vnd.oci.image.manifest.v1+json
    Platform:    unknown/unknown
    Annotations:
        vnd.docker.reference.digest: sha256:c6410e6eb22169edaf67dec59f6af204b97c4d2387e433f55602a3e7493de921
        vnd.docker.reference.type:   attestation-manifest

    Name:        localhost:5000/nginx-test:multi@sha256:3ddaa3f904fa19ac4f69d3fc30e0abdde2184ade76675475e71cd20b0734f7b6
    MediaType:   application/vnd.oci.image.manifest.v1+json
    Platform:    unknown/unknown
    Annotations:
        vnd.docker.reference.digest: sha256:a556a2b2a0633fa8d274699c27ba9ee2c0a6a8dec44241ef5b95a3c658966ca0
        vnd.docker.reference.type:   attestation-manifest
La solution est lĂ ... —
Terminal
docker buildx imagetools inspect localhost:5000/nginx-test:multi

Pour la prochaine étape, nous allons reprendre notre image backend et la proposer pour deux architectures différentes : linux/amd64 et linux/arm64.

Question

Dans le fichier Dockerfile existant Ă  la racine du projet backend: * Reprenez --platform=$BUILDPLATFORM * Modifier/Utiliser GOARCH et GOOS.

Build

    [+] Building 28.1s (32/32) FINISHED                                                                                         docker-container:mybuilder
    => [internal] load build definition from Dockerfile                                                                                              0.0s
    => => transferring dockerfile: 923B                                                                                                              0.0s
    => [linux/amd64 internal] load metadata for docker.io/library/golang:1.25.0-alpine                                                               0.9s
    => [linux/amd64 internal] load metadata for docker.io/library/alpine:3                                                                           0.9s
    => [linux/arm64 internal] load metadata for docker.io/library/alpine:3                                                                           1.4s
    => [auth] library/golang:pull token for registry-1.docker.io                                                                                     0.0s
    => [auth] library/alpine:pull token for registry-1.docker.io                                                                                     0.0s
    => [internal] load .dockerignore                                                                                                                 0.0s
    => => transferring context: 132B                                                                                                                 0.0s
    => [internal] load build context                                                                                                                 0.4s
    => => transferring context: 484.40kB                                                                                                             0.4s
    => [linux/amd64 stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62             0.1s
    => => resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                                 0.1s
    => [linux/amd64 build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440   0.1s
    => => resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440                     0.1s
    => [linux/arm64 stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62             1.0s
    => => resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                                 0.1s
    => => sha256:f6b4fb9446345fcad2db26eac181fef6c0a919c8a4fcccd3bea5deb7f6dff67e 4.20MB / 4.20MB                                                    0.6s
    => => extracting sha256:f6b4fb9446345fcad2db26eac181fef6c0a919c8a4fcccd3bea5deb7f6dff67e                                                         0.2s
    => CACHED [linux/amd64 build 2/7] WORKDIR /app                                                                                                   0.0s
    => CACHED [linux/amd64 build 3/7] COPY go.mod go.sum ./                                                                                          0.0s
    => [linux/amd64->arm64 build 4/7] RUN go mod download                                                                                            3.9s
    => [linux/amd64 build 4/7] RUN go mod download                                                                                                   3.8s
    => [linux/arm64 stage-1 2/6] WORKDIR /app                                                                                                        0.1s
    => [linux/arm64 stage-1 3/6] RUN apk --no-cache add ca-certificates                                                                              3.2s
    => [linux/amd64 build 5/7] COPY . .                                                                                                              1.9s
    => [linux/amd64->arm64 build 5/7] COPY . .                                                                                                       1.8s
    => [linux/amd64 build 6/7] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server ./cmd/server                                            16.6s
    => [linux/amd64->arm64 build 6/7] RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o server ./cmd/server                                     16.6s
    => [linux/amd64 build 7/7] RUN mkdir -p /app/uploads                                                                                             0.4s
    => [linux/amd64->arm64 build 7/7] RUN mkdir -p /app/uploads                                                                                      0.4s
    => [linux/arm64 stage-1 4/6] COPY --from=build /app/server /app/                                                                                 0.2s
    => CACHED [linux/amd64 stage-1 2/6] WORKDIR /app                                                                                                 0.0s
    => CACHED [linux/amd64 stage-1 3/6] RUN apk --no-cache add ca-certificates                                                                       0.0s
    => CACHED [linux/amd64 stage-1 4/6] COPY --from=build /app/server /app/                                                                          0.0s
    => CACHED [linux/amd64 stage-1 5/6] COPY --from=build /app/uploads /app/uploads                                                                  0.0s
    => CACHED [linux/amd64 stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads                                                   0.0s
    => [linux/arm64 stage-1 5/6] COPY --from=build /app/uploads /app/uploads                                                                         0.1s
    => [linux/arm64 stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads                                                          0.3s
    => exporting to image                                                                                                                            2.4s
    => => exporting layers                                                                                                                           1.4s
    => => exporting manifest sha256:e752a830f983c70286ff4402329ed69015050ec452522c27e401e9e0019199cf                                                 0.0s
    => => exporting config sha256:613ad69bdbb4448a697fb9ebb359f81dfc6cd478ae6632486c909db2fb15609a                                                   0.0s
    => => exporting attestation manifest sha256:6e8383d142725374f44192f3bf56429a059d7499f476cf0873328b845814d606                                     0.2s
    => => exporting manifest sha256:790af95f1430560517beceb0fd9635e8e356f33f859a8310dd058d72d7ffc2c2                                                 0.1s
    => => exporting config sha256:5b0908ba3e36725e3c92481c1d14fd51c0ed444b31b8a1776f666b2f490cfa87                                                   0.1s
    => => exporting attestation manifest sha256:c9e4d2c0b5213bc4ab47723f34b98e79e3b5d7c07373bba9b6c345dd0f8eabc9                                     0.1s
    => => exporting manifest list sha256:22c701c3f83b0a917ea3827e17efa5de672677183cc736234ad5aa4b34b7f98d                                            0.0s
    => => pushing layers                                                                                                                             0.3s
    => => pushing manifest for localhost:5000/nginx-test:multi@sha256:22c701c3f83b0a917ea3827e17efa5de672677183cc736234ad5aa4b34b7f98d               0.1s
La solution est lĂ ... —

Dockerfile
    # Build stage
    FROM --platform=$BUILDPLATFORM golang:1.25.0-alpine AS build

    WORKDIR /app

    # Arg 
    ARG TARGETOS
    ARG TARGETARCH

    # Copy go mod and sum files
    COPY go.mod go.sum ./

    # Download dependencies
    RUN go mod download

    # Copy the source code
    COPY . .

    # Build the application 
    RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o server ./cmd/server

    # Create uploads directory
    RUN mkdir -p /app/uploads

    # Runtime stage
    FROM alpine:3

    WORKDIR /app

    # Install necessary runtime dependencies
    # hadolint ignore=DL3018
    RUN apk --no-cache add ca-certificates

    # Copy the binary from the build stage
    COPY --from=build /app/server /app/
    COPY --from=build /app/uploads /app/uploads

    # Ensure the uploads directory exists and is writable
    RUN mkdir -p /app/uploads && \
        chmod -R 755 /app/uploads

    # Expose the port
    EXPOSE 8080

    # Run the server
    CMD ["./server"]
Commande
docker buildx build --platform linux/amd64,linux/arm64 -t localhost:5000/app-backend:bluidx --push .

đŸ’œ Cache avec Buildx : un ocĂ©an de possibilitĂ©s

Buildx vient chambouler les possibilitĂ©s offertes par le cache dans Docker. Dans cette partie, dĂ©couvrez comment il a fait Ă©voluer son architecture. C’est orientĂ© vers la CI/CD, avec entre autres, la prise en charge du cache distant.

Rappel sur le fonctionnement du cache avec la commande build

La gestion du cache est rĂ©alisĂ©e par couches et suit la structure du Dockerfile. La premiĂšre couche correspond au FROM, puis le Dockerfile est parcouru commande par commande jusqu’à la fin du fichier.

Chaque instruction (CMD, COPY, RUN, etc) crĂ©e une couche qui est construite indĂ©pendamment et dispose de son propre cache. Lorsqu’une modification est effectuĂ©e, le cache de la couche concernĂ©e doit ĂȘtre recréé, ainsi que celui de toutes les couches qui la suivent.

Avec Buildx, la gestion du cache est modifiĂ©e. Le Dockerfile n’est plus interprĂ©tĂ© de maniĂšre strictement linĂ©aire, mais converti en un graphe d’opĂ©rations. BuildKit permet de parallĂ©liser les exĂ©cutions. Le cache reste sensible Ă  l’ordre des instructions : modifier l’ordre d’une section modifie le graphe et invalide le cache de l’opĂ©ration ainsi que celui des suivantes.

En ce qui concerne la granularitĂ© du cache, elle est beaucoup plus fine qu’avec le build classique. Buildx permet l’utilisation de solutions de cache externes. Dans le cas d’un fichier requirements.txt, apporter une modification (ajout ou suppression de librairies) peut invalider l’étape de construction. Cette dĂ©cision ne dĂ©pend plus uniquement de Docker, mais Ă©galement de pip.

Grñce au cache de pip et aux wheels, l’installation devient plus efficace :

– si une dĂ©pendance existante est modifiĂ©e, pip rĂ©utilise les wheels dĂ©jĂ  construits et reconstruit uniquement les parties concernĂ©es ; – si une dĂ©pendance est ajoutĂ©e, seule son installation est effectuĂ©e, sans refaire l’intĂ©gralitĂ© de l’installation.

Auparavant, Docker travaillait seul et, en cas de modification, ne conservait rien de l’existant. L’intĂ©gration du cache pip + wheels permet donc de gagner du temps et des ressources, tout en conservant les dĂ©pendances dĂ©jĂ  installĂ©es.

De notre cĂŽtĂ©, notre application est en Go, mais on va pouvoir utiliser le mĂȘme concept : jouer avec le cache, rĂ©aliser un premier build, ajouter une librairie dans le fichier go.mod et voir comment se comporte le cache.

Nettoyage

Pour bien voir la gestion du cache, on va nettoyer le cache existant, ce qui nous permet de parler de la commande prune qui permet de faire cela.

docker buildx prune -a

Ici, on nettoie "brutalement" le cache, mais la commande prune permet de faire du nettoyage intelligent. Pour cela, vous pouvez vous référencer à la documentation official de prune

Question

  • rĂ©aliser un premier build de l'image backend et observer les temps des Ă©tapes
  • ajouter github.com/fatih/color v1.16.0 dans le fichier go.mod
  • rebuild l'image et observer le comportement du cache et les temps des Ă©tapes

â„č Utiliser le paramĂštre --progress=plain pour avoir un affichage plus lisible.

Premier build, aprĂšs nettoyage
#0 building with "mybuilder" instance using docker-container driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 909B done
#1 DONE 0.0s

#2 [internal] load metadata for docker.io/library/golang:1.25.0-alpine
#2 DONE 0.9s

#3 [internal] load metadata for docker.io/library/alpine:3
#3 DONE 0.9s

#4 [internal] load .dockerignore
#4 transferring context: 132B done
#4 DONE 0.0s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440 0.0s done
#5 DONE 0.1s

#6 [stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62
#6 resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62 0.0s done
#6 sha256:1074353eec0db2c1d81d5af2671e56e00cf5738486f5762609ea33d606f88612 0B / 3.86MB 0.2s
#6 sha256:1074353eec0db2c1d81d5af2671e56e00cf5738486f5762609ea33d606f88612 3.86MB / 3.86MB 0.3s done
#6 extracting sha256:1074353eec0db2c1d81d5af2671e56e00cf5738486f5762609ea33d606f88612
#6 extracting sha256:1074353eec0db2c1d81d5af2671e56e00cf5738486f5762609ea33d606f88612 0.4s done
#6 DONE 0.8s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 32B / 32B 0.3s done
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 11.53MB / 60.05MB 0.8s
#5 sha256:18414ed0f6669fd1d6e137922f2a57e37aaf0a63ae6968c499fe69b17d148d14 124B / 124B 0.4s done
#5 sha256:ec7d4ca09441bdb9129d5708d2aa8802e233b2d11d1797317158c4095e9df8fc 282.44kB / 282.44kB 0.3s done
#5 sha256:9824c27679d3b27c5e1cb00a73adb6f4f8d556994111c12db3c5d61a0c843df8 3.80MB / 3.80MB 0.4s done
#5 ...

#7 [stage-1 2/6] WORKDIR /app
#7 DONE 0.1s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 17.83MB / 60.05MB 0.9s
#5 extracting sha256:9824c27679d3b27c5e1cb00a73adb6f4f8d556994111c12db3c5d61a0c843df8
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 28.31MB / 60.05MB 1.1s
#5 ...

#8 [internal] load build context
#8 transferring context: 32.91MB 1.1s done
#8 DONE 1.2s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 extracting sha256:9824c27679d3b27c5e1cb00a73adb6f4f8d556994111c12db3c5d61a0c843df8 0.3s done
#5 extracting sha256:ec7d4ca09441bdb9129d5708d2aa8802e233b2d11d1797317158c4095e9df8fc 0.1s done
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 40.89MB / 60.05MB 1.4s
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 49.28MB / 60.05MB 1.5s
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 58.72MB / 60.05MB 1.7s
#5 sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 60.05MB / 60.05MB 1.7s done
#5 extracting sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2
#5 ...

#9 [stage-1 3/6] RUN apk --no-cache add ca-certificates
#9 0.984 (1/1) Installing ca-certificates (20251003-r0)
#9 1.029 Executing busybox-1.37.0-r30.trigger
#9 1.038 Executing ca-certificates-20251003-r0.trigger
#9 1.132 OK: 8473 KiB in 17 packages
#9 DONE 1.2s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 extracting sha256:8286cb4ece30afb97c398c2b5ac1f35e8f502f758d4ea2fc69e179efdf471ea2 5.5s done
#5 DONE 7.3s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 extracting sha256:18414ed0f6669fd1d6e137922f2a57e37aaf0a63ae6968c499fe69b17d148d14 done
#5 extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 done
#5 DONE 7.3s

#10 [build 2/7] WORKDIR /app
#10 DONE 0.4s

#11 [build 3/7] COPY go.mod go.sum ./
#11 DONE 0.0s

#12 [build 4/7] RUN go mod download
#12 DONE 7.3s

#13 [build 5/7] COPY . .
#13 DONE 0.2s

#14 [build 6/7] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server ./cmd/server
#14 DONE 23.7s

#15 [build 7/7] RUN mkdir -p /app/uploads
#15 DONE 0.1s

#16 [stage-1 4/6] COPY --from=build /app/server /app/
#16 DONE 0.1s

#17 [stage-1 5/6] COPY --from=build /app/uploads /app/uploads
#17 DONE 0.0s

#18 [stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads
#18 DONE 0.1s

Second build avec l'ajout de la dépendance go

On voit bien que seules les étapes impactées par le changement du fichier go.mod sont exécutées, les autres sont extraites du cache

#0 building with "mybuilder" instance using docker-container driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 909B done
#1 DONE 0.0s

#2 [internal] load metadata for docker.io/library/alpine:3
#2 ...

#3 [internal] load metadata for docker.io/library/golang:1.25.0-alpine
#3 DONE 0.3s

#2 [internal] load metadata for docker.io/library/alpine:3
#2 DONE 0.3s

#4 [internal] load .dockerignore
#4 transferring context: 132B done
#4 DONE 0.0s

#5 [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440
#5 resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440 0.0s done
#5 DONE 0.0s

#6 [stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62
#6 resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62 0.0s done
#6 DONE 0.0s

#7 [internal] load build context
#7 transferring context: 78.13kB 0.1s done
#7 DONE 0.1s

#8 [build 2/7] WORKDIR /app
#8 CACHED

#9 [build 3/7] COPY go.mod go.sum ./
#9 DONE 0.0s

#10 [build 4/7] RUN go mod download
#10 DONE 7.9s

#11 [build 5/7] COPY . .
#11 DONE 0.3s

#12 [build 6/7] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server ./cmd/server
#12 DONE 23.5s

#13 [build 7/7] RUN mkdir -p /app/uploads
#13 DONE 0.1s

#14 [stage-1 5/6] COPY --from=build /app/uploads /app/uploads
#14 CACHED

#15 [stage-1 2/6] WORKDIR /app
#15 CACHED

#16 [stage-1 3/6] RUN apk --no-cache add ca-certificates
#16 CACHED

#17 [stage-1 4/6] COPY --from=build /app/server /app/
#17 CACHED

#18 [stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads
#18 CACHED
La solution est lĂ ... —

go mod
go 1.25.0

require (
        github.com/gorilla/mux v1.8.1
        github.com/rs/cors v1.11.1
        github.com/swaggo/http-swagger v1.3.4
        github.com/swaggo/swag v1.16.6
        github.com/fatih/color v1.16.0
)
commande
docker buildx build -t localhost:5000/app-backend:buildx --push .

â†Ș Proposition orientĂ© CI/CD, gestion du cache distant

CĂŽtĂ© CI/CD, les runners sont souvent Ă©phĂ©mĂšres et, Ă  chaque pipeline, l’environnement de build repart de zĂ©ro.

Pour compenser cette problématique, on comptait souvent sur des outils externes. Certains registries proposent des options qui permettent de réutiliser des layers déjà existants (Harbor par exemple).

Il est maintenant possible de le faire nativement avec Buildx ! La réutilisation du cache entre pipelines permet de réduire le temps de build et de limiter la charge sur les ressources. Cette fonctionnalité, couplée à la nouvelle construction du cache, permet une réelle maximisation de l'usage du cache.

Buildx permet d’exporter et d’importer le cache vers des backends externes (registry, stockage local...). On va pouvoir poursuivre nos expĂ©rimentations avec notre registry.

Question

Pousser le cache de notre image sur notre registry locale.

  • RĂ©aliser un premier build de l'image backend
  • Ajouter --cache-to Ă  la commande pour donner la cible du cache

Visualiser le cache

    [+] Building 1.6s (22/22) FINISHED                                                                                          docker-container:mybuilder
    => [internal] load build definition from Dockerfile                                                                                              0.0s
    => => transferring dockerfile: 824B                                                                                                              0.0s
    => [internal] load metadata for docker.io/library/alpine:3                                                                                       0.7s
    => [internal] load metadata for docker.io/library/golang:1.25.0-alpine                                                                           0.7s
    => [auth] library/alpine:pull token for registry-1.docker.io                                                                                     0.0s
    => [auth] library/golang:pull token for registry-1.docker.io                                                                                     0.0s
    => [internal] load .dockerignore                                                                                                                 0.0s
    => => transferring context: 132B                                                                                                                 0.0s
    => [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440               0.1s
    => => resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440                     0.1s
    => [internal] load build context                                                                                                                 0.2s
    => => transferring context: 114.06kB                                                                                                             0.1s
    => [stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                         0.1s
    => => resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                                 0.1s
    => CACHED [stage-1 2/6] WORKDIR /app                                                                                                             0.0s
    => CACHED [stage-1 3/6] RUN apk --no-cache add ca-certificates                                                                                   0.0s
    => CACHED [build 2/7] WORKDIR /app                                                                                                               0.0s
    => CACHED [build 3/7] COPY go.mod go.sum ./                                                                                                      0.0s
    => CACHED [build 4/7] RUN go mod download                                                                                                        0.0s
    => CACHED [build 5/7] COPY . .                                                                                                                   0.0s
    => CACHED [build 6/7] RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server                                                               0.0s
    => CACHED [build 7/7] RUN mkdir -p /app/uploads                                                                                                  0.0s
    => CACHED [stage-1 4/6] COPY --from=build /app/server /app/                                                                                      0.0s
    => CACHED [stage-1 5/6] COPY --from=build /app/uploads /app/uploads                                                                              0.0s
    => CACHED [stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads                                                               0.0s
    => exporting to image                                                                                                                            0.3s
    => => exporting layers                                                                                                                           0.0s
    => => exporting manifest sha256:e752a830f983c70286ff4402329ed69015050ec452522c27e401e9e0019199cf                                                 0.0s
    => => exporting config sha256:613ad69bdbb4448a697fb9ebb359f81dfc6cd478ae6632486c909db2fb15609a                                                   0.0s
    => => exporting attestation manifest sha256:e123012b93b0a5e44fa9ed75e410b78b4b4d2cfa02bb81c4101a420469f06076                                     0.1s
    => => exporting manifest list sha256:6aa6ff0937b9f27f95f56f6d5155a36e72431f99f4007badd9a30fc30b4e7454                                            0.0s
    => => pushing layers                                                                                                                             0.1s
    => => pushing manifest for localhost:5000/app-backend:bluidx@sha256:6aa6ff0937b9f27f95f56f6d5155a36e72431f99f4007badd9a30fc30b4e7454             0.0s
    => exporting cache to registry                                                                                                                   0.1s
    => => preparing build cache for export                                                                                                           0.1s
    => => writing layer sha256:1074353eec0db2c1d81d5af2671e56e00cf5738486f5762609ea33d606f88612                                                      0.0s
    => => writing layer sha256:229720617a9e81a0cb80f2fff21b68366fcc9a864a86ec5a7a16521db28e4abf                                                      0.0s
    => => writing layer sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1                                                      0.0s
    => => writing layer sha256:6eb7a6e2535de9885952c82c4559f483aa8b578c65b4a6022181970afbb8cbf2                                                      0.0s
    => => writing layer sha256:b7482504c00153cf21c9b3ab8563cff5b589a4e06eb2ac726dc48e1b478d9323                                                      0.0s
    => => writing layer sha256:ba7a13890897cdddcda58502146b448a8424891a5d43f5dbf2fbf6011949a823                                                      0.0s
    => => writing config sha256:16972018a7f1f30bd95dbd55619fda102d79497d8af043eb21e4b78b8c4caf2c                                                     0.0s
    => => writing cache image manifest sha256:e0da55c5c966e05afd950162a7e7dd4f5e6ba4f8658be43a3cd80c0642980675                                       0.0s
La solution est lĂ ... —
Terminal
docker buildx build --cache-to=type=registry,ref=localhost:5000/app-backend:buildx -t localhost:5000/app-backend:buildx --push .

Si vous souhaitez conserver le cache avec votre image, il est possible d’utiliser un build inline. Cette approche consiste Ă  intĂ©grer les mĂ©tadonnĂ©es du cache directement dans l’image Docker lors du build, ce qui permet de le rĂ©utiliser lors des builds suivants sans dĂ©pendre d’un cache externe. Elle est particuliĂšrement adaptĂ©e aux rebuilds rĂ©guliers.

Question

Pousser le cache de notre image, dans notre image.

  • Ajouter --cache-to=type=inline Ă  la commande
  • Tagger l'image avec inline

Visualiser le cache

    [+] Building 1.7s (22/22) FINISHED                                                                                          docker-container:mybuilder
    => [internal] load build definition from Dockerfile                                                                                              0.0s
    => => transferring dockerfile: 824B                                                                                                              0.0s
    => [internal] load metadata for docker.io/library/alpine:3                                                                                       0.9s
    => [internal] load metadata for docker.io/library/golang:1.25.0-alpine                                                                           0.9s
    => [auth] library/alpine:pull token for registry-1.docker.io                                                                                     0.0s
    => [auth] library/golang:pull token for registry-1.docker.io                                                                                     0.0s
    => [internal] load .dockerignore                                                                                                                 0.0s
    => => transferring context: 132B                                                                                                                 0.0s
    => [build 1/7] FROM docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440               0.1s
    => => resolve docker.io/library/golang:1.25.0-alpine@sha256:f18a072054848d87a8077455f0ac8a25886f2397f88bfdd222d6fafbb5bba440                     0.1s
    => [stage-1 1/6] FROM docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                         0.1s
    => => resolve docker.io/library/alpine:3@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62                                 0.1s
    => [internal] load build context                                                                                                                 0.3s
    => => transferring context: 114.06kB                                                                                                             0.2s
    => CACHED [stage-1 2/6] WORKDIR /app                                                                                                             0.0s
    => CACHED [stage-1 3/6] RUN apk --no-cache add ca-certificates                                                                                   0.0s
    => CACHED [build 2/7] WORKDIR /app                                                                                                               0.0s
    => CACHED [build 3/7] COPY go.mod go.sum ./                                                                                                      0.0s
    => CACHED [build 4/7] RUN go mod download                                                                                                        0.0s
    => CACHED [build 5/7] COPY . .                                                                                                                   0.0s
    => CACHED [build 6/7] RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server                                                               0.0s
    => CACHED [build 7/7] RUN mkdir -p /app/uploads                                                                                                  0.0s
    => CACHED [stage-1 4/6] COPY --from=build /app/server /app/                                                                                      0.0s
    => CACHED [stage-1 5/6] COPY --from=build /app/uploads /app/uploads                                                                              0.0s
    => CACHED [stage-1 6/6] RUN mkdir -p /app/uploads &&     chmod -R 755 /app/uploads                                                               0.0s
    => exporting to docker image format                                                                                                              0.2s
    => => exporting layers                                                                                                                           0.0s
    => => preparing layers for inline cache                                                                                                          0.0s
    => => exporting manifest sha256:e7cb215da9ffbc906326b27ba1c75e47e3fd555ca669572c28d1d5a9e2a5615e                                                 0.0s
    => => exporting config sha256:4ed367a543f77877860c93a81479f0a326f3c2c0c87d58613e664c749df56957                                                   0.0s
    => => sending tarball                                                                                                                            0.1s
    => importing to docker
La solution est lĂ ... —
Terminal
docker buildx build --cache-to=type=inline -t app-backend:inline --load .

❔ Comparaison Build / buildx et quand favoriser build

Buildx apporte de nombreuses nouvelles fonctionnalitĂ©s qui rĂ©duisent le besoin d’outils externes. C’est une Ă©volution majeure de Docker, qui permet de rĂ©pondre Ă  des besoins actuels : utilisation de la CI/CD et orientation vers des pratiques GitOps.

Buildx peut ĂȘtre plus complexe Ă  prendre en main si vous n’ĂȘtes pas encore Ă  l’aise avec les concepts fondamentaux de Docker. Pour des projets simples, sans CI avancĂ©e ni besoin de multi-architecture, la commande docker build reste une solution fiable et suffisante.

DĂšs que l’on vise des builds reproductibles et industrialisĂ©s, Buildx devient incontournable.