I don’t know about your family, but my family is crazy about building with LEGOs. Last year for Christmas each of us got at least one LEGO set. The first discussion after the unwrapping is complete regards who gets to build first at the favored kitchen table.
Building container images using Buildah can be like building a LEGO kit, too. Yes, you can build them using a Dockerfile, and that’s pretty straightforward, but that’s like having someone build your LEGO set for you. However, if you’d like to build the container image step-by-step yourself from the command line (or by using a Bash script), you can do that instead. As I’ve fielded a few questions on this topic in the recent past, I thought I’d run through a quick example of how to build a container image based on a Dockerfile using Bash.
Building with a Dockerfile
First, let’s look at the Dockerfile. This example uses the same Dockerfile that’s used to build the quay.io/buildah/upstream:latest image. It’s located here on GitHub. This Dockerfile is used by Quay.io to automatically build a new container image every time something is merged into Buildah’s GitHub repository.
Let’s take a quick peek at the Dockerfile:
# git/Dockerfile
#
# Build a Buildah container image from the latest
# upstream version of Buildah on GitHub.
# https://github.com/containers/buildah
# This image can be used to create a secured container
# that runs safely with privileges within the container.
# The containers created by this image also come with a
# Buildah development environment in /root/buildah.
#
FROM fedora:latest
ENV GOPATH=/root/buildah
# Install the software required to build Buildah.
# Then create a directory and clone from the Buildah
# GitHub repository, make and install Buildah
# to the container.
# Finally remove the buildah directory and a few other packages
# that are needed for building but not running Buildah
RUN dnf -y install --enablerepo=updates-testing \
make \
golang \
bats \
btrfs-progs-devel \
device-mapper-devel \
glib2-devel \
gpgme-devel \
libassuan-devel \
libseccomp-devel \
git \
bzip2 \
go-md2man \
runc \
fuse-overlayfs \
fuse3 \
containers-common; \
mkdir /root/buildah; \
git clone https://github.com/containers/buildah /root/buildah/src/github.com/containers/buildah; \
cd /root/buildah/src/github.com/containers/buildah; \
make;\
make install;\
rm -rf /root/buildah/*; \
dnf -y remove bats git golang go-md2man make; \
dnf clean all;
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
# Set up environment variables to note that this is
# not starting with usernamespace and default to
# isolate the filesystem with chroot.
ENV _BUILDAH_STARTED_IN_USERNS="" BUILDAH_ISOLATION=chroot
This Dockerfile pulls the latest Fedora release, sets up the GOPATH, runs a number of RUN commands to install dependencies and ensure that configuration files are set up properly, and then finishes by setting up an environment variable.
To build this container with a Dockerfile, it’s a pretty simple command:
# buildah bud -t buildahupstream:latest .
and off it will go. But what fun is that?
Building on the command line
Now, let’s try building the container one piece at a time using the Buildah command line.
Building the FROM step equivalents
First, we need to do the FROM step and capture the resulting container name in the ctr variable:
# ctr=$(buildah from fedora)
Getting image source signatures
Copying blob d318c91bf2a8 done
Copying config f0858ad3fe done
Writing manifest to image destination
Storing signatures
Use this command to set the GOPATH:
# buildah config --env GOPATH=/root/buildah $ctr
Building the RUN command equivalents
Now for the RUN commands. Although long, and honestly not something I’d want to type in by hand, this example shows how quickly you can translate any Dockerfile command. It really is simple to translate from that format to Buildah commands with only a little Bash tweaking. They look like:
# buildah run $ctr /bin/sh -c 'dnf -y install --enablerepo=updates-testing \
make \
golang \
bats \
btrfs-progs-devel \
device-mapper-devel \
glib2-devel \
gpgme-devel \
libassuan-devel \
libseccomp-devel \
git \
bzip2 \
go-md2man \
runc \
fuse-overlayfs \
fuse3 \
containers-common; \
mkdir -p /root/buildah; \
git clone https://github.com/containers/buildah /root/buildah/src/github.com/containers/buildah; \
cd /root/buildah/src/github.com/containers/buildah; \
make;\
make install;\
rm -rf /root/buildah/*; \
dnf -y remove bats git golang go-md2man make; \
dnf clean all'
Adding a status check
This next step wasn’t part of the Dockerfile and is not necessary, but it’s useful for checking the status of our container build. Let’s use the buildah mount command and capture the returned directory (which is the container’s root filesystem) in the mnt variable:
# mnt=$(buildah mount $ctr)
# buildah run $ctr -- sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
Sidebar: Let’s make sure that our sed call removed the # from the mount_progam line in the /etc/containers/storage.conf file as we asked. This is where our $mnt variable comes in very handy from our buildah mount command above. We just do a simple grep call to confirm the change:
# grep mount_program $mnt/etc/containers/storage.conf
mount_program = "/usr/bin/fuse-overlayfs"
Back to finishing up the last RUN command needed for building the container image:
# buildah run $ctr /bin/sh -c 'mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock'
Setting our environment variables
Then we set our last environment variables:
# buildah config --env _BUILDAH_STARTED_IN_USERNS="" --env BUILDAH_ISOLATION=chroot $ctr
Saving our image
And finally, we need to save our image. We can do that with a buildah commit command:
# buildah commit $ctr buildahupstream
Creating and running the Bash script
At this point, we’ve created the equivalent container image as if we’d created it using the Dockerfile. Now, running each of these commands by hand is certainly doable, but sometimes it’s nice to use a script. Just grab the above commands and put them into a Bash script. Let’s call it build_buildah_upstream.sh:
#!/usr/bin/env bash
# build_buildah_upstream.sh
#
ctr=$(buildah from fedora)
buildah config --env GOPATH=/root/buildah $ctr
buildah run $ctr /bin/sh -c 'dnf -y install --enablerepo=updates-testing \
make \
golang \
bats \
btrfs-progs-devel \
device-mapper-devel \
glib2-devel \
gpgme-devel \
libassuan-devel \
libseccomp-devel \
git \
bzip2 \
go-md2man \
runc \
fuse-overlayfs \
fuse3 \
containers-common; \
mkdir -p /root/buildah; \
git clone https://github.com/containers/buildah /root/buildah/src/github.com/containers/buildah; \
cd /root/buildah/src/github.com/containers/buildah; \
make; \
make install; \
rm -rf /root/buildah/*; \
dnf -y remove bats git golang go-md2man make; \
dnf clean all'
buildah run $ctr -- sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
buildah run $ctr /bin/sh -c 'mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock'
buildah config --env _BUILDAH_STARTED_IN_USERNS="" --env BUILDAH_ISOLATION=chroot $ctr
buildah commit $ctr buildahupstream
And now after running chmod 755 build_buildah_upstream.sh, we can run the script simply with ./build_buildah_upstream.sh, and our buildahupstream container image will be created for us.
Running the resulting image
No matter how we built our image, we can then run it to test out the upstream version of Buildah using Podman, with commands like:
$ podman run buildahupstream buildah version
$ podman run buildahupstream bash -c "buildah from busybox; buildah images"
Advantages of building your own
So what’s the advantage of all of this work? First, is the ability to check your progress as you go along. As we saw, the buildah mount command can be especially useful here, allowing you to mount the container’s root filesystem, which gives you access to it from the host so you can verify that your bits are in all the right places as you run through each of the commands. Second, even though the examples come from the root account, you could easily run them from a non-root user, too.
One thing to remember is in rootless mode all commands have to be done in the user namespace of the user. You can enter the user namespace using the buildah unshare command. If you don’t do this, the buildah mount, command will fail. After entering the user namespace the user is allowed access to the containers root file system as a non-root user. To execute the script as a non root user, you can execute buildah unshare build_buildah_upstream.sh.
But, you may ask: "If it’s all in a script, what’s the difference between doing this and using a Dockerfile to run a straight buildah bud command?" There are a few advantages that I can see. The first is if you’re building the container image for the first time, you can build each step singularly from the command line. If there’s something wrong with a particular command you’ll find out right away and you don’t have to adjust the Dockerfile and rerun from scratch. You can just adjust the command that failed until you get it right, no restarting the entire container image build process.
When you build using a Dockerfile, it’s a fire and forget type of thing. Once you press the button, the build either completes successfully, or it fails and you have to restart from the beginning (granted, the second time through with a Dockerfile benefits you speed-wise due to cached layers). There’s also no way to get input into the build process if queried.
We recently had someone ask how they could get a response back to a script that was looking for input as they ran their Dockerfile. The simple answer is that you can’t. If you have a script or process that needs a response to a question like, "Is this OK (y|n)?", the Dockerfile just hangs or errors. If you’re running a script to do the build, you have the full power of Bash to create all sorts of scripting, especially its if-then-else syntax. That way, you could use some shell magic to answer the question, or have the script pause and enter it in yourself. Ditto if you're doing the build from the command line, you could answer the question directly.
Wrapping up
There you have it. With Buildah, building container images from the command line or a shell script can easily be done in lieu of a Dockerfile. Doing this allows you to build your container image block by block—much like some of my family’s favorite LEGO kits—with full control of the process. With these techniques, you can gain flexibility for your container development process and create more tools for your development arsenal.
New to containers? Download the Containers Primer and learn the basics of Linux containers.
Sull'autore
Software engineer at Red Hat working on containers focusing on the Buildah and Podman projects. Manages the buildah.io and podman.io websites and can be found on freenode at #buildah and #podman. Joined the Open Source world in 2016 and wished he'd joined the community sooner. Husband and dad of 3 just trying to contain them, but has better luck containing bits and bytes with containers. When it's time to do some serious coding, it's time to crank the Bluegrass in the headphones and get the tea brewing!
Altri risultati simili a questo
Red Hat and Sylva unify the future for telco cloud
Bridging the gap: Secure virtual and container workloads with Red Hat OpenShift and Palo Alto Networks
Can Kubernetes Help People Find Love? | Compiler
Scaling For Complexity With Container Adoption | Code Comments
Ricerca per canale
Automazione
Novità sull'automazione IT di tecnologie, team e ambienti
Intelligenza artificiale
Aggiornamenti sulle piattaforme che consentono alle aziende di eseguire carichi di lavoro IA ovunque
Hybrid cloud open source
Scopri come affrontare il futuro in modo più agile grazie al cloud ibrido
Sicurezza
Le ultime novità sulle nostre soluzioni per ridurre i rischi nelle tecnologie e negli ambienti
Edge computing
Aggiornamenti sulle piattaforme che semplificano l'operatività edge
Infrastruttura
Le ultime novità sulla piattaforma Linux aziendale leader a livello mondiale
Applicazioni
Approfondimenti sulle nostre soluzioni alle sfide applicative più difficili
Virtualizzazione
Il futuro della virtualizzazione negli ambienti aziendali per i carichi di lavoro on premise o nel cloud