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.
저자 소개
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!
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
가상화
온프레미스와 클라우드 환경에서 워크로드를 유연하게 운영하기 위한 엔터프라이즈 가상화의 미래