Buildah: Granularity & Control
The goal of this lab is to introduce you to Buildah and the flexibility it provides when you need to build container images your way. There are a lot of different use cases that just "feel natural" when building container images, but you often, you can't quite wire together and elegant solutions with the client server model of existing container engines. In comes Buildah. To get started, lets introduce some basic decisions you need to think through when building a new container image.
- Image vs. Scratch: Do you want to start with an existing container image as the source for your new container image, or would you prefer to build completely from scratch? Source images are the most common route, but it can be nice to build from scratch if you have small, statically linked binaries.
- Inside vs. Outside: Do you want to execute the commands to build the next container image layer inside the container, or would you prefer to use the tools on the host to build the image? This is completely new concept with Buildah, but with existing container engines, you always build from within the container. Building outside the container image can be useful when you want to build a smaller container image, or an image that will always be ran read only, and never built upon. Things like Java would normally be built in the container because they typically need a JVM running, but installing RPMs might happen from outside because you don't want the RPM database in the container.
- External vs. Internal Data: Do you have everything you need to build the image from within the image? Or, do you need to access cached data outside of the build process? For example, It might be convenient to mount a large cached RPM cache inside the container during build, but you would never want to carry that around in the production image. The use cases for build time mounts range from SSH keys to Java build artifacts.
Alright, let's walk through some common scenarios with Buildah.
Just like Podman, Buildah can execute in rootless mode, but since you have tools on the container host interacting files in the container image, you need to make Buildah think it's running as root. Buildah comes with a sub-command called unshare which does just this. It puts our shell into a user namespace just like when you have a root shell in a container. The difference is, this shell has access to tools installed on the container host, instead of in the container image. Before we complete the rest of this lab, execute the "buildah unshare" command. Think of this as making yourself root, without actually making yourself root:
Now, look at who your shell thinks you are:
root, but you really aren't, but let's prove it:
First declare what image you want to start with as a source. In this case, we will start with Red Hat Universal Base Image:
buildah from ubi8
buildah mount ubi8-working-container
echo "hello world" > $(buildah mount ubi8-working-container)/etc/hello.conf
ls -alh $(buildah mount ubi8-working-container)/etc/
cat $(buildah mount ubi8-working-container)/etc/hello.conf
buildah commit ubi8-working-container ubi8-hello
Now, we can see the new image layer in our local cache. We can view it with Buildah:
Or Podman, since they both use the same image store:
When we are done, we can clean up our environment quite nicely. The following command will delete references to "working containers" and completely remove their mounts:
buildah delete -a
You can verify that you've successfully exited the namespace by running
whoami, which should no longer display
Now, you have a pretty good understanding of the cases where Buildah really shines. You can start from scratch, or use an existing image, use tools installed on the container host (not in the container image), and move data around as needed. This is a very flexible tool that should fit quite nicely in your tool belt. Buildah lets you script builds with any language you want, and build tiny images with only the bare minimum of utilities needed inside the image.