Blog Zenika

#CodeTheWorld

DevOps

Découverte de Buildkit

En novembre dernier, le projet moby, qui regroupe les projets open-source de la société Docker, a publié buildkit. La création de ce projet fait suite à une proposition de réécriture du système de construction d’image du Docker engine.
Dans cet article, je vous propose de revenir sur les raisons de ce choix et de découvrir l’approche adoptée par le projet.

Présentation du projet

Le projet buildkit est hébergé sur Github. Avec une seule release en preview, environ 45 contributeurs et 450 stars, il s’agit d’un projet encore jeune. Il s’inscrit néanmoins dans la mouvance qui vise à découper les fonctionnalités de docker en autant de composants spécialisés, comme c’est le cas pour l’orchestrateur swarmkit, le runtime de conteneur containerd qui gère le cycle de vie des conteneurs ou encore l’outil runc qui permet de les créer. Comme la plupart des projets issus de Docker, il est écrit dans le langage Go.
Parmi ses fonctionnalités, on retrouve la possibilité d’effectuer des builds (même multi-stage) simultanés et distribués tout en conservant une gestion de cache efficace grâce à un mécanisme de résolution de dépendances des instructions et l’import/export des caches intermédiaires.
Une autre fonctionnalité intéressante est la possibilité de supporter différents frontend, c’est à dire le descripteur qui va servir à construire notre image, et également différents backends, c’est-à-dire le format dans lequel notre image sera produite.
Ces fonctionnalités peuvent être implémentées grâce au découpage en composants des différentes étapes nécessaires à la réalisation de la construction de l’image.

Architecture du projet

Au coeur du projet on retrouve le principe de LLB (Low Level Builder), il s’agit d’un format binaire interne représentant les instructions nécessaires à la réalisation du build.
En utilisant cette représentation intermédiaire, il est possible de supporter plusieurs descripteurs ou mécanismes de définition de l’image en entrée et plusieurs formats de production pour l’image en sortie, comme le font les compilateurs modernes comme LLVM.

Frontends: Pour le moment, seuls les Dockerfile sont supportés, mais par la suite n’importe quel descripteur d’étapes de construction d’une image pourrait être utilisé.
Solver: En utilisant le graphe d’instructions envoyé par le frontend, le solveur va chercher à optimiser l’exécution des différentes instructions afin de construire un cache qui sera réutilisable pour les prochains builds.
Sources: L’objectif de ce composant est de faire en sorte que les données soient accessibles au worker afin qu’il s’exécute correctement. Ces données peuvent être : une image docker, un repo git, une archive accessible en http ou un répertoire local.
Worker: Le rôle du worker est d’exécuter l’instruction de build en ayant accès aux données et points de montage nécessaires.
Exporter: L’exporter s’exécute après la dernière étape du build afin de finaliser l’export de l’image au format choisi. Il peut s’agir d’une image docker, un répertoire ou un fichier.
Snapshots: Ce composant a pour rôle de gérer le stockage et l’indexation des systèmes de fichiers intermédiaires produits lors de la construction. Par défaut, il s’appuie sur le système de snapshots de containerd, mais supporte également d’autres mécanismes.
Cache manager: Il a pour rôle de gérer le cycle de vie des caches et de les supprimer lorsqu’ils sont obsolètes. Il a également pour objectif de permettre l’import et l’export de caches afin de pouvoir distribuer la construction sur plusieurs machines en profitant tout de même du cache.

Prise en main

Après ce tour d’horizon des différents composants, voyons comment utiliser buildkit.
Pour commencer, le plus simple est de faire tourner le démon buildkitd dans un conteneur :

docker run -d --privileged -p 1234:1234 tonistiigi/buildkit --addr tcp://0.0.0.0:1234

Il faut ensuite positionner la variable d’environnement BUILDKIT_HOST qui permet au client de savoir où contacter le démon:

export BUILDKIT_HOST=tcp://0.0.0.0:1234

Vous pouvez alors lancer un build, l’exemple suivant, vous permet de construire une image comme vous le feriez avec docker build:

docker build . -t myimage:latest
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=.
--exporter=docker --exporter-opt name=myimage | docker load


Et constatez que le build s’est bien passé avec la commande:

docker image ls

Vous pouvez également tester les autres exporters, l’exemple suivant construit une image et en exporte le contenu de le répertoire indiqué:

buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=.
--exporter=local --exporter-opt output=./mon-repertoire

Pour aller plus loin :

Container Builder Interface for Kubernetes – https://github.com/containerbuilding/cbi
Building Docker images without Docker –  https://kccnceu18.sched.com/event/Dqu1/building-docker-images-without-docker-matt-rickard-google-intermediate-skill-level-slides-attached
Solver Design – https://github.com/moby/buildkit/blob/master/docs/solver.md
Des builds sans être root – https://github.com/moby/buildkit/blob/master/docs/rootless.md
 
 

Auteur/Autrice

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.