Build container images using Podman and Buildah

This is the second in a series of blog posts about building container images. The series starts with “How will we build container images in the future?” “start. This article explores how building images has changed since Docker was first released and how to overcome the many limitations of using Dockerfiles. This article focuses on Podman[1] and Buildah[2]. In future articles, we will explore other new methods in this field.

Podman and Buildah are two recently emerged tools designed to help build container images. They are complementary tools, both part of the Open Repository for Container Tools, and emerged from Red Hat’s mission to remove the Docker Daemon from container workflows. Why these two tools, and what experience will each bring to container image building? Let’s start with Podman.

Podman

Podman’s function goes beyond building container images, and it is often discussed together with Buildah. We mention it here because of its contribution to container image building.

Daemonless Builds

Podman attempts to recreate the full functionality of the familiar Docker CLI by being able to handle and respond to API requests without running a daemon. Different from the client/server mode, Podman adopts the local fork/exec mode, which in the eyes of Red Hat greatly simplifies the control and security of the container life cycle.

Podman simulates various client commands provided by Docker, and some fans even encourage new users to use Podman as an alias for Docker commands to facilitate future transitions. In addition to providing the Docker command suite, Podman also provides Podman commands. It is used to build OCI (Open Container Initiative) [3] compliant container images, using Dockerfile as the source of its various build steps. In this sense, it is effectively equivalent to the docker build command, but without the overhead of the Docker daemon.

As you would expect, Podman build is compatible with all docker build parameters (some occasionally used parameters have not been included, such as –cache-from), and Podman also includes some additional parameters for implementations previously guarded by Docker. Features that can only be provided by processes (for example, registry communication). Therefore, transitioning from Docker builds to Podman builds is a seamless experience, unless you have quirks such as looking for images that are not named according to the naming convention.

Rootless Builds

In addition to enabling daemon-less builds, Podman also offers another popular feature – root-less builds. In the past, due to the use of the Docker daemon, building container images using docker build required root privileges, which was often considered too open-ended by security-conscious organizations. Podman solves this serious problem when providing the ability to perform Rootless builds, but that doesn’t mean there are no limitations.

The process of building images from Dockerfiles involves temporarily creating containers for running commands in order to install packages, retrieve remote content, build artifacts, etc. Creating and running containers usually requires root privileges. So, how does Podman solve this problem? To avoid building as root, Podman utilizes User namespaces. Namespaces provide an isolation mechanism for Linux processes and are a major component of the container abstraction. If the set of namespaces used to create a container includes a user namespace, the agent calling the container can be an unprivileged user – in other words, using the user namespace, Podman can use the container to build images without root permissions.

User namespaces provide a way to map a set of unprivileged user and group IDs (UIDs/GIDs) in the host’s default user namespace to a different set of UIDs/GIDs in a new user namespace associated with a container. GID. This way, an unprivileged UID/GID on the host can be securely mapped to the root user inside the container (UID/GID=0), thus providing the container’s process with privileges that may be needed during the image build process (e.g., installing OS packages ). However, depending on the mapping, the container only has the same file access permissions on the host as the unprivileged user who issued the podman build command. This means that the host’s file system is protected from accidental or malicious damage.

Disadvantages of the current build without root permissions

There are problems here too. Images are usually built based on a base image (FROM instruction in Dockerfile), and usually the user with UID/GID = 0 is the owner of its content. When an unprivileged user starts a container build in a container, the container’s files are owned by UID/GID=0 on the host, and the container’s processes will only have file access permissions associated with that unprivileged user. This may mean that the container’s process cannot write to its file system, which will seriously hinder the building of the container image. In order for the files in the image to have correct ownership within the container, the UID/GID set needs to be “moved in” to an inline location of the user namespace map. Currently, there is no optimal means of achieving this goal.

When a rootless build is called and the container requests an ownership “transfer”, the filesystem contents are copied and the ownership is chowned to reflect the mapping. This is obviously inefficient in terms of space utilization and takes time, which can seriously impact the time it takes to build a container. One of the important ideas behind containers is that container images can be shared by multiple containers without copying them. Ideally, when the container’s file system is made up of its constituent layers, this transfer should be done as part of the installation operation without having to be repeated.

Most container runtimes use overlayfs to form the container’s file system, which does not support shifting UID/GID on its mounts, but recently Ubuntu became the first Linux distribution with kernel support for (shiftfs) overlays. This version is already used in the Linux Containers LXD project.

A temporary remedy for Podman is the introduction of the mount option for overlayfs in Linux kernel version 4.19. This option only copies the metadata of files and directories to the read/write layer, not the content itself. Ultimately, in order to achieve this goal, the community still has to wait for the mainstream Linux kernel to support UID/GID transfer.

Let’s move on to Buildah and explain how it is related to and how it differs from Podman builds.

Buildah

So far, we haven’t mentioned how podman build uses Buildah in the background to perform the build of the container image. This means daemon-less and root-less building are also features of Buildah. Unlike Podman, Buildah has proprietary features for building container images, and also has many other features that are not limited to building images based on Dockerfiles.

Most container images are built using Dockerfiles. We have already discussed how podman build uses a Dockerfile to build an image, and Buildah can also use the buildah bud command to build an image from a Dockerfile. However, Buildah’s innovation came from exploring alternatives to Dockerfiles. The rationale for using an alternative approach is that all that is required is a “bundle” of “OCI-compatible” images, achieving the goal of ultimately not requiring a Dockerfile. Buildah’s maintainer insists that the Dockerfile is a roadblock.

How Buildah works

Although the intention of taking a different approach from the Dockerfile is clear, Buildah uses a very similar process to build container images. Docker build launches a new container to process each Dockerfile directive, resulting in new/changed content or image metadata needing to be created before committing that container as a new image. The next instruction creates the container using the previously created image and submits it as a new image, and so on. Buildah does the same thing, but instead of using Dockerfile instructions, it executes Buildah subcommands and does not require a “submit” after each subcommand is executed.

The building process starts with the buildah from command, and the result will be a running container generated based on the image with the given parameters, which is very similar to the FORM instruction of the Dockerfile. As part of building an image, to execute instructions in the container (for example, create a new user, or create an artifact from source), the image author can use buildah run, which can interact when needed. In addition to running commands that create content for container images, Buildah also provides a way to define image metadata using buildah config. This allows you to specify things like public ports, default users, container entries, etc. The buildah copy and buildah add commands are directly similar to the COPY and ADD Dockerfile instructions and are used to get external content into the image. Using buildah mount, it is even possible to mount the container’s root filesystem in an appropriate location on the host for later manipulation using the host’s own tools.

Once the image author is confident that their image is complete, the buildah commit command will create the container into a new image.

Workflow

There are some obvious similarities between Dockerfile and docker build and Buildah. Dockerfile enforces sequential execution of related instructions, so how does Buildah provide similar ordering and repeatability in container builds? It is recommended to use the daemon to programmatically define container builds using Buildah, rather than using the daemon’s build engine to force this docker build command.

#!/bin/bash

id=$(buildah from --pull node:10)

buildah run $idmkdir -p /usr/src/app

buildah config --workingdir /usr/src/app $id

buildah copy $id$PWD.

buildah run --net host $idnpm install

buildah config --port 1337 --entrypoint '["npm", "start"]'$id

buildah commit $idexample-app

The simple example above shows how to use Buildah in a Bash script to achieve repeatable builds.

By doing image building in this way, Buidah removes the dependence on the daemon process and further frees the image builder from the constraints of Dockerfile syntax. Images built by Buildah can be uploaded to the image warehouse, then pulled by the Podman or Docker daemon, and finally run smoothly on a container runtime that supports the OCI specification.

Build caching and parallel execution

If the image is built using a Dockerfile and buildah bud, the image layers will be cached and reused in future builds. This is expected for those transitioning from a Docker environment to Buildah and can significantly increase build execution speed. However, if you expect that the image built using the Buildah command in the script will use the cache, then you will be surprised. Caching is not available. This means that the entire set of build steps needs to be executed on each new build iteration, regardless of any changes in content or commands.

Additionally, Buildah executes its build steps sequentially even if one is completely independent of another. While the ability to implement parallel build steps has been considered, Buildah does not currently offer this functionality, which would further extend the time required to perform complex container image builds.

Conclusion

Although there are clear differences between Podman and Buildah, it’s confusing to have two ways to achieve the same goal. When in doubt, you should use Podman to build when using a Dockerfile to create images, and if you feel that the Dockerfile syntax is too strict, or if a script-like approach is used to achieve repeatability, you should use Buildah. It’s worth mentioning that container images and Dockerfiles are almost synonymous, so it remains to be seen whether Buildah will gain enough traction outside of the Red Hat community to eventually replace Dockerfiles.

Podman and Buildah provide two of the most popular features for building container images; daemonless and rootless builds. However, these tools compete in an increasingly crowded space, and while still in their infancy, they do lack some of the functionality that similar tools currently offer.

Related links:

https://podman.io/ https://buildah.io/
https://github.com/opencontainers/image-spec/blob/master/spec.md#open-container-initiative
Original link: https://www.giantswarm.io/blog/building-container-images-with-podman-and-buildah