Aller au contenu

Accélère tes pipelines Docker avec Buildx et BuildKit

Introduction

Quand on veut construire des images Docker dans un environnement Kubernetes, plusieurs solutions s'offrent à nous. Mais toutes ne se valent pas. Ce cours va te guider à travers trois grandes étapes :

D’abord, on va comparer les outils les plus courants pour construire des images : Docker-in-Docker, Kaniko, et Buildx/BuildKit.

Ensuite, tu verras comment déployer une infrastructure BuildKit sur Kubernetes, capable de monter en charge.

Et enfin, on terminera par une mise en pratique avec une pipeline GitLab qui utilise ce cluster de build pour générer une image.

Les outils de build : quelles différences, quels choix ?

Quand tu construis une image Docker dans un cluster, tu ne peux pas juste lancer un docker build comme en local. Il faut un environnement d’exécution qui supporte ça. Voici les trois grandes approches.

Docker-in-Docker : la méthode "à l'ancienne"

Docker-in-Docker, ou DinD pour les intimes, consiste à démarrer un conteneur… dans lequel tourne le démon Docker lui-même. C’est comme si tu miniaturisais ton environnement Docker classique à l’intérieur d’un pod Kubernetes.

C’est simple à mettre en place, et ça fonctionne bien avec les outils Docker traditionnels. Mais ce n’est pas sans contrepartie. Pour fonctionner correctement, DinD a souvent besoin du mode --privileged, ce qui pose de gros problèmes de sécurité. Et dans un cluster, ce genre de contournement peut rapidement devenir instable. Bref, ça dépanne, mais on évite en production.

Kaniko : la solution cloud-native

Kaniko a été créé par Google pour contourner les limites de DinD. L’idée est élégante : on construit une image Docker sans démon Docker, en lisant et en interprétant le Dockerfile directement.

C’est très séduisant, surtout dans un monde Kubernetes, car c’est sécurisé, simple à déployer et conçu pour fonctionner dans des environnements sans privilèges. Mais Kaniko a aussi ses limites : les performances ne sont pas folles, et le support de certaines fonctionnalités avancées de Dockerfile est incomplet.

Projet archivé

Google a archivé le dépôt GoogleContainerTools/kaniko le 3 juin 2025 : plus de correctifs ni de patchs de sécurité. Si tu en dépends encore, Chainguard maintient un fork, mais pour un nouveau projet, pars plutôt sur BuildKit.

BuildKit + Buildx : la version moderne et puissante

Et puis, il y a BuildKit. C’est l’outil de build Docker nouvelle génération, développé par les créateurs de Docker. Il est pensé pour la performance, le parallélisme, et la construction distribuée. Buildx, c’est l’outil en ligne de commande qui permet de piloter BuildKit comme un chef.

Avec BuildKit, tu peux compiler pour plusieurs architectures, bénéficier du cache entre les builds, et connecter des workers sur différents nœuds. Bref, c’est rapide, moderne, et ultra flexible.

Alors pourquoi ne l’utilise-t-on pas partout ? Tout simplement parce qu’il demande un peu plus d’infrastructure. Il faut installer et configurer un pool de builders dans ton cluster.

Déployer un cluster BuildKit sur Kubernetes (avec scaling)

Bon, maintenant qu’on a choisi notre arme (spoiler : c’est BuildKit), voyons comment mettre tout ça en place dans un cluster Kubernetes.

Une infrastructure à base de pods buildkitd Le cœur de l’architecture, c’est un déploiement de pods qui exécutent le démon buildkitd. Chaque pod écoute sur un port gRPC et peut accepter des requêtes de build. Tu peux les exposer via un service Kubernetes ClusterIP, accessible uniquement depuis le cluster. N'expose jamais buildkitd hors du cluster (LoadBalancer) : quiconque atteint ce port peut lancer des builds avec tes ressources et tes secrets de build.

Tu peux démarrer avec 2 ou 3 pods, puis monter progressivement si besoin. Et si tu veux que le cluster adapte automatiquement sa capacité à la charge, tu peux ajouter un autoscaler (HPA ou même KEDA pour de l’event-driven).

On commence par apply un deployment :

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: buildkitd
  name: buildkitd
  namespace: gitlab
spec:
  replicas: 1
  selector:
    matchLabels:
      app: buildkitd
  template:
    metadata:
      labels:
        app: buildkitd
    # see buildkit/docs/rootless.md for caveats of rootless mode
    spec:
      containers:
        - name: buildkitd
          image: moby/buildkit:v0.34.0-rootless # épingle une version, jamais master
          args:
            - --addr
            - unix:///run/user/1000/buildkit/buildkitd.sock
            - --addr
            - tcp://0.0.0.0:1234
            - --oci-worker-no-process-sandbox
          readinessProbe:
            exec:
              command:
                - buildctl
                - debug
                - workers
            initialDelaySeconds: 5
            periodSeconds: 30
          livenessProbe:
            exec:
              command:
                - buildctl
                - debug
                - workers
            initialDelaySeconds: 5
            periodSeconds: 30
          securityContext:
            # Needs Kubernetes >= 1.19
            seccompProfile:
              type: Unconfined
            # Needs Kubernetes >= 1.30
            appArmorProfile:
              type: Unconfined
            # To change UID/GID, you need to rebuild the image
            runAsUser: 1000
            runAsGroup: 1000
          ports:
            - containerPort: 1234
          volumeMounts:
            # Dockerfile has `VOLUME /home/user/.local/share/buildkit` by default too,
            # but the default VOLUME does not work with rootless on Google's Container-Optimized OS
            # as it is mounted with `nosuid,nodev`.
            # https://github.com/moby/buildkit/issues/879#issuecomment-1240347038
            - mountPath: /home/user/.local/share/buildkit
              name: buildkitd
      volumes:
        - name: buildkitd
          emptyDir: {}

Puis on apply son service associé pour être accesible :

apiVersion: v1
kind: Service
metadata:
  labels:
    app: buildkitd
  name: buildkitd
  namespace: gitlab
spec:
  ports:
    - port: 1234
      protocol: TCP
      targetPort: 1234
  selector:
    app: buildkitd

Note

Tu as des versions pour le lancer sur un simple pod, sous forme de job, en StatefulSet... Tous les exemples sont disponibles ici

Active le mTLS

Tel quel, buildkitd écoute en TCP sans authentification : n'importe quel pod du cluster peut s'en servir. En dehors d'un lab, génère des certificats (le dossier d'exemples ci-dessus fournit un script) et lance buildkitd avec --tlscacert, --tlscert et --tlskey, montés depuis un Secret.

Intégrer BuildKit à GitLab CI : une pipeline simple et efficace

Maintenant, place à la pratique. Imaginons que tu veux builder une image Docker simple depuis un job GitLab, mais au lieu d’utiliser DinD ou Kaniko, tu veux utiliser ton pool BuildKit sur Kubernetes.

On commence par se créer un Dockerfile des plus minimal :

FROM alpine:3.20
RUN echo "Hello from BuildKit!" > /hello.txt
CMD ["cat", "/hello.txt"]

Je te met ensuite la configuration Gitlab qui contacte notre pod de BuildKit, via la cli buildx, pour un simple docker build.

stages:
  - build

variables:
  BUILDKIT_HOST: tcp://buildkitd.gitlab.svc.cluster.local:1234

build_image:
  stage: build
  before_script:
    - apk add --no-cache docker-cli docker-cli-buildx
  script:
    # le driver "remote" pointe directement sur buildkitd, sans démon Docker
    # avec mTLS : ajouter --driver-opt cacert=ca.pem,cert=cert.pem,key=key.pem
    - docker buildx create --name remote-builder --driver remote --platform=linux/arm64 --use --bootstrap "$BUILDKIT_HOST"
    - docker buildx build -o type=docker,dest=- . > mon_image.tar

J'utilise ici une version rootless bien évidement, mais sans certificat, pour un gain de temps pour mon installation en local. Le driver remote de buildx se connecte directement au démon buildkitd : pas besoin de docker context ni de démon Docker dans le job.

Tip

La cli Buildx contient un max d'argument super intéressant, je met vraiment ici un exemple de build le plus minimaliste possible.

Docker BuildKit Buildx

Conclusion

BuildKit, c’est vraiment la solution la plus moderne et scalable pour construire des images Docker dans un environnement Kubernetes. Couplé à GitLab CI par exemple, il te permet de sortir des vieilles pratiques risquées (comme DinD), tout en profitant de performances et de flexibilité inégalées, de build en multi-stage, etc.

Et en prime, tu peux scaler ton infra BuildKit automatiquement via HPA, pour que ton cluster soit prêt à répondre aux pics de builds... sans jamais trop consommer en temps normal.


Déployer tes runners sur Kubernetes

Pour orchestrer et exécuter tes pipelines de build directement sur ton cluster, découvre le guide pas à pas :
👉 Déployer des GitLab CI Runners sous Kubernetes