À la base de chaque conteneur que nous pouvons créer, se trouve une image.

Cette image, nous la créons depuis un Dockerfile : le fichier qui contient les instructions pour générer l'image. Mais aujourd'hui les outils qui nous permettent de générer des images ont beaucoup évolué et de nouvelles solutions apparaissent également.

C'est le cas de Buildah, projet open-source complémentaire à Podman qui sont tous deux disponibles sur une grande majorité de distribution Linux.

Complémentaires car chacun est spécialisé dans une tâche précise, Podman pour la gestion des containers ( container engine ), alors que Buildah se concentre sur la construction des images.

Buildah c'est quoi ?

Il s'agit donc d'un outil en ligne de commande qui peut-être utilisé pour :

  • create a working container, either from scratch or using an image as a starting point
  • create an image, either from a working container or via the instructions in a Dockerfile
  • images can be built in either the OCI image format or the traditional upstream docker image format
  • mount a working container's root filesystem for manipulation
  • unmount a working container's root filesystem
  • use the updated contents of a container's root filesystem as a filesystem
  • layer to create a new image
  • delete a working container or an image
  • rename a local container

Buildah est donc un outil qui permet de créer des images au format Open Container initiative. Ces images sont bien sûr utilisables avec tous les containers runtimes, qu'ils soient de bas niveau ou non, il est juste nécessaire que celui-ci accepte le format OCI  : Docker, Podman, containerd, runc, etc.

Et donc avec Kubernetes car votre installation utilise généralement comme container runtimes Docker, CRI-O ou bien containerd.

Le projet étant complémentaire à Podman, on retrouve ici une des grandes forces de celui-ci : le daemonless.

Buildah ne nécessite aucun daemon pour construire vos images et en plus, il peut s’exécuter directement en ligne de commande.

Mais avant de voir tout ça, installons Buildah sur notre système !

Installer Buildah

Les packages pour Buildah sont disponibles pour Fedora et CentOS, Ubuntu, Debian et d'autres. Vous pouvez facilement les installer en utilisant le gestionnaire de paquets de votre distribution.

Je ne vais pas montrer l'installation sur tous les environnements possibles mais juste sur Fedora et Ubuntu.

Fedora par exemple :

dnf install buildah -y

Pour Ubuntu et Debian, vous pouvez utiliser le PPA fournit par le projet Atomic :

sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:projectatomic/ppa
sudo apt install buildah

Maintenant que vous avez installé Buildah, nous allons pouvoir construire notre première image.

Une première utilisation

Afin de découvrir Buildah, nous allons créer une image basique avec une application web minimaliste écrite en Go. Il faudra donc :

  • Copier mon code source dans l'image,
  • Compiler le code,
  • Exécuter mon application au démarrage du container ( entrypoint ).

Voici d’abord le code Go que je vais copier dans mon fichier main.go :

package main

import "net/http"

func helloHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("<h1>Hello World !</h1>"))
}

func main() {
    http.HandleFunc("/hello", helloHandler)
    http.ListenAndServe(":8080", nil)
}

Une simple page, affichant "Hello World !".

J'utilise le port 8080 pour mon application afin de pouvoir lancer le container en mode rootless ( il est possible de bind un port < 1024 en restant rootless avec une petite configuration mais ce n'est pas le sujet ici ).

Dans un premier temps, créons un fichier Dockerfile comme j'aurais pu le faire afin de construire mon image à l'aide de la commande docker build :

FROM golang:1.14

WORKDIR /go/src/hello
COPY . .

RUN go install hello

EXPOSE 8080
ENTRYPOINT /go/bin/hello

Mais construisons cette image avec Buildah :

buildah bud -t myapp:latest .
STEP 1: FROM golang:1.14
Getting image source signatures
...
Copying config 5fbd6463d2 done
Writing manifest to image destination
Storing signatures
STEP 2: WORKDIR /go/src/hello
STEP 3: COPY . .
STEP 4: RUN go install hello
STEP 5: EXPOSE 80
STEP 6: ENTRYPOINT /go/bin/hello
STEP 7: COMMIT myapp:latest
Getting image source signatures
...
Copying config 282e7b9ce3 done
Writing manifest to image destination
Storing signatures
--> 282e7b9ce34
282e7b9ce34b53563ffc2861c35a4d497a4bf63e4e5974519b3c58955c1ed438

L'option bud est en réalité une version courte de l'option build-using-dockerfile.

On ne constate quasiment aucun changement par rapport à l'utilisation de docker build. Je peux vérifier la présence de mon image avec la commande :

$ buildah images
REPOSITORY                 TAG      IMAGE ID       CREATED              SIZE
localhost/myapp            latest   8d59a8bf9894   21 seconds ago       838 MB
docker.io/library/golang   1.14     5fbd6463d24b   11 days ago          830 MB

Je vais lancer cette image avec podman :

podman run -d -p 8080:8080 localhost/myapp

Et vérifier son bon fonctionnement :

$ curl http://localhost:8080/hello
<h1>Hello World !</h1>

Mais il est possible de construire cette même image directement en ligne de commande, sans passer par un fichier Dockerfile.

Voyons immédiatement comment réaliser cela !

Buildah command line

Je vais conserver mon application Go. Par contre nous allons cette fois, construire l'image directement en ligne de commande.

Je commence par récupérer l'image golang :

$ buildah from golang:1.14
golang-working-container

En retour de cette commande, j'obtiens le nom d'un container, ici golang-working-container, qui va me servir à construire mon image étape par étape.

Réalisons ensuite les mêmes étapes que dans notre Dockerfile :

$ buildah config --workingdir /go/src/hello golang-working-container
$ buildah copy golang-working-container . .
$ buildah run golang-working-container go install hello
$ buildah config --port 8080 golang-working-container
$ buildah config --entrypoint /go/bin/hello golang-working-container

Il s'agit bien sûr d'une nouvelle commande, alors pas de miracle, il est parfois nécessaire de regarder la documentation pour trouver les bons arguments :

containers/buildah
A tool that facilitates building OCI images. Contribute to containers/buildah development by creating an account on GitHub.

Enfin je vais pouvoir commit mes modifications et donner un nom à mon image :

$ buildah commit golang-working-container buildah-myapp

Je peux vérifier que celle-ci existe bien :

$ buildah images
REPOSITORY                 TAG      IMAGE ID       CREATED          SIZE
localhost/buildah-myapp    latest   5ecabc051771   12 seconds ago   838 MB
localhost/myapp            latest   6c1d9196a77c   12 minutes ago   838 MB
docker.io/library/golang   1.14     5fbd6463d24b   11 days ago      830 MB

Et enfin la lancer avec podman pour vérifier que tout fonctionne correctement :

$ podman run -d -p 8080:8080 localhost/buildah-myapp
$ curl http://localhost:8080/hello
<h1>Hello World !</h1>

Nous avons pu découvrir Buildah en l'installant tout d'abord sur notre distribution puis en l'utilisant une première fois, notamment afin de :

  • Construire une image à partir d'un fichier Dockerfile,
  • Construire la même image en ligne de commande.

Cela nous permet d'avoir une première approche de l'outil et d'en apercevoir ( un peu ) le potentiel d'utilisation.

Pourquoi cette utilisation en ligne de commande est-elle intéressante ? Car elle nous offre - à minima - la possibilité :

  • Vérifier étape par étape la construction d'une image, plus besoin de tout relancer à chaque erreur lors d'une série de commande ou de créer des layers intermédiaires pendant les tests ( qui font grossir la taille de l'image finale ).
  • Vous pouvez utiliser votre langage de script favori pour construire des images ! Cela offre énormément de possibilité.

Il reste bien évidemment d'autres choses à voir dans le cadre d'une utilisation plus poussée. Mais nous pourrons voir ça ensemble au prochain article sur ce sujet !

Et vous? Que pensez-vous de créer vos images directement en ligne de commande ? Cela peut-il servir dans vos pipelines CI/CD ?

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter  ou via les commentaires 👇 ! C'est toujours un plaisir d'avoir des retours ! 😇