Building Docker Images with Buildah
Building Docker images in a CI environment can be challenging. There are ways to run ‘Docker in Docker’ (DinD), to enable you to do this, but it looks hacky and it doesn’t feel right. Fortunately, we now have tools that can build containers without having to handle the mess involved with running DinD. Buildah is one of these tools, specifically designed to build all kinds of OCI container images. Additionally, it does not need a (docker) daemon to build image, so it’s an ideal candidate for building images in CI/CD situations.
Install
To get started with buildah, it’s convenient to have it on your local workstation, but you can also directly start with the buildah docker-image.
Local
For most Linux distributions, installation is quite easy. You can find the instructions here. To verify that everything was installed correctly:
ruben@am8.nl:~ $ buildah --version
buildah version 1.13.2 (image-spec 1.0.1-dev, runtime-spec 1.0.1-dev)
Dockerized
To enter the Buildah docker container, you can use this:
docker run --rm -ti --privileged -v $(pwd):/mnt -w /mnt buildah/buildah:latest sh
Basic Usage
If we want to use buildah as a drop-in replacement for ‘docker build’, we need to provide some additional flags.
Buildah has a couple of different ways in which you can define and create your container image. To use a Dockerfile for this, we use the command ‘buildah build-using-dockerfile’ or ‘buildah bud’. By default, it creates the image in an generic format without metadata that Docker expects, so Docker won’t be able to run it.
To make sure the image is built with everything we expect, we use the '–format docker' argument. The rest is the same as ‘docker build’, so we provide the image name with '-t image:latest' and the build context last, the current directory in this case.
The end result looks something like this:
export USER=you
export PASSWORD=yourpassword
export IMAGE_NAME=${USER}/api:latest
buildah build-using-dockerfile --format docker -t ${IMAGE_NAME} .
buildah push --creds="${USER}:${PASSWORD}" ${IMAGE_NAME} docker://${IMAGE_NAME}
Make sure that you created the repository on Docker Hub, or else the pushing will not work.
Usage in GitLab CI
Example - Single Image
The same commands can easily be used in GitLab CI, using the official buildah container. The following example makes use of the default CI variables of GitLab CI to know the location and credentials with which are to be used to push the image.
---
stages:
- build
build:
image: buildah/buildah:latest
stage: build
variables:
IMAGE_NAME: ${CI_REGISTRY_IMAGE}:latest
script:
- buildah build-using-dockerfile --format docker -t ${IMAGE_NAME} .
- buildah push --creds="${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}"
${IMAGE_NAME} docker://${IMAGE_NAME}
Example - Multiple Images
Let’s say you have two images to build: an api, and a webserver. If they have their own directories, with their own Dockerfiles, you can easily enhance the CI file to handle that for you.
---
stages:
- build
.build:
image: buildah/buildah:latest
stage: build
variables:
IMAGE_NAME: $CI_REGISTRY_IMAGE/$NAME:$VERSION
script:
- buildah build-using-dockerfile --format docker -t ${IMAGE_NAME} ./${NAME}
- buildah push --creds="${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}"
${IMAGE_NAME} docker://${IMAGE_NAME}
build-api:
extends: .build
variables:
NAME: api
VERSION: 0.0.1
build-webserver:
extends: .build
variables:
NAME: webserver
VERSION: 0.0.1