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