Build container images using buildah

buildah is a command line tool that facilitates building OCI compliant images.

The Buildah package provides a command line tool that can be used to

  • create a working container, either from scratch or using an image as a starting point
  • create an image, either from a working container or via the instructions in a Dockerfile
  • images can be built in either the OCI image format or the traditional upstream docker image format
  • mount a working container’s root filesystem for manipulation
  • unmount a working container’s root filesystem
  • use the updated contents of a container’s root filesystem as a filesystem layer to create a new image
  • delete a working container or an image
  • rename a local container

Installing buildah

buildah is available in the CentOS repo. All we need to do is to run the following to install it:

[podman@localhost ]# yum install -y buildah

After installation is done, we can check the version of the installed package

[podman@localhost base]# buildah --version
buildah version 1.7.1 (image-spec 1.0.0, runtime-spec 1.0.0)

I am going to use a previous Dockerfile that I have to build a test container image. The application is just a simple flask test application.

[podman@localhost base]# ls
Dockerfile hello.py requirements.txt run.py

[podman@localhost base]# cat Dockerfile
from python:2.7.10

COPY requirements.txt /app/requirements.txt
COPY hello.py /app/hello.py

RUN pip install -r /app/requirements.txt

CMD ["python","/app/hello.py"]

And running build-using-dockerfile,  you’ll find a pretty similar output when using docker build command

[podman@localhost base]# buildah build-using-dockerfile -t base_bud .
STEP 1: FROM python:2.7.10
Getting image source signatures
Copying blob d4bce7fd68df [======================================] 1.5GiB / 1.5GiB
Copying blob a3ed95caeb02 [======================================] 1.5GiB / 1.5GiB
Copying blob 816152842605 [======================================] 1.5GiB / 1.5GiB
Copying blob 5dcab2c7e430 [======================================] 1.5GiB / 1.5GiB
Copying blob dc54ada22a60 [======================================] 1.5GiB / 1.5GiB
Copying blob b7b0de78f891 [======================================] 1.5GiB / 1.5GiB
Copying blob a3ed95caeb02 [======================================] 1.5GiB / 1.5GiB
Copying blob 88363ed594cb [======================================] 1.5GiB / 1.5GiB
Copying blob a3ed95caeb02 [======================================] 1.5GiB / 1.5GiB
Copying blob a3ed95caeb02 [======================================] 1.5GiB / 1.5GiB
Copying blob f8c4a940a0da [======================================] 1.5GiB / 1.5GiB
Copying blob dd19554ab82c [======================================] 1.5GiB / 1.5GiB
Copying blob a3ed95caeb02 [======================================] 1.5GiB / 1.5GiB
Writing manifest to image destination
Storing signatures
STEP 2: COPY requirements.txt /app/requirements.txt
STEP 3: COPY hello.py /app/hello.py
STEP 4: RUN pip install -r /app/requirements.txt
Collecting flask (from -r /app/requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/9a/74/670ae9737d14114753b8c8fdf2e8bd212a05d3b361ab15b44937dfd40985/Flask-1.0.3-py2.py3-none-any.whl (92kB)
 100% |████████████████████████████████| 94kB 905kB/s
Collecting itsdangerous>=0.24 (from flask->-r /app/requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting Werkzeug>=0.14 (from flask->-r /app/requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/9f/57/92a497e38161ce40606c27a86759c6b92dd34fcdb33f64171ec559257c02/Werkzeug-0.15.4-py2.py3-none-any.whl (327kB)
 100% |████████████████████████████████| 327kB 394kB/s
Collecting Jinja2>=2.10 (from flask->-r /app/requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl (124kB)
 100% |████████████████████████████████| 126kB 796kB/s
Collecting click>=5.1 (from flask->-r /app/requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
 100% |████████████████████████████████| 81kB 847kB/s
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask->-r /app/requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz
Building wheels for collected packages: MarkupSafe
 Running setup.py bdist_wheel for MarkupSafe
 Stored in directory: /root/.cache/pip/wheels/f2/aa/04/0edf07a1b8a5f5f1aed7580fffb69ce8972edc16a505916a77
Successfully built MarkupSafe
Installing collected packages: itsdangerous, Werkzeug, MarkupSafe, Jinja2, click, flask
Successfully installed Jinja2-2.10.1 MarkupSafe-1.1.1 Werkzeug-0.15.4 click-7.0 flask-1.0.3 itsdangerous-1.1.0
You are using pip version 7.1.2, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
STEP 5: CMD ["python","/app/hello.py"]
STEP 6: COMMIT containers-storage:[overlay@/var/lib/containers/storage+/var/run/containers/storage]localhost/base_bud:latest
Getting image source signatures
Copying blob 12e469267d21 [======================================] 124.9MiB / 124.9MiB
Copying blob 5f70bf18a086 [======================================] 1.0KiB / 1.0KiB
Copying blob 0d78baeff42f [======================================] 43.1MiB / 43.1MiB
Copying blob c32291971339 [======================================] 120.4MiB / 120.4MiB
Copying blob d6d84a7ea9f1 [======================================] 307.6MiB / 307.6MiB
Copying blob a21d673437af [======================================] 1.4MiB / 1.4MiB
Copying blob 5f70bf18a086 [======================================] 1.0KiB / 1.0KiB
Copying blob 3c62eeb65f64 [======================================] 33.0KiB / 33.0KiB
Copying blob 5f70bf18a086 [======================================] 1.0KiB / 1.0KiB
Copying blob 5f70bf18a086 [======================================] 1.0KiB / 1.0KiB
Copying blob 641946be2935 [======================================] 60.9MiB / 60.9MiB
Copying blob 7ce007c412b5 [======================================] 5.8MiB / 5.8MiB
Copying blob 5f70bf18a086 [======================================] 1.0KiB / 1.0KiB
Copying blob ff620944702d [======================================] 5.9MiB / 5.9MiB
Copying config e3b4c77357 [======================================] 4.6KiB / 4.6KiB
Writing manifest to image destination
Storing signatures
--> e3b4c773576230182c498a12b5a3fd8611da958ba5b273947eedc46b50ff6617

Listing our images using buildah

[podman@localhost base]# buildah images
IMAGE NAME IMAGE TAG IMAGE ID CREATED AT SIZE
docker.io/library/python 2.7.10 4442f7b981c4 Dec 6, 2015 05:49 696 MB
localhost/base_bud latest e3b4c7735762 May 27, 2019 22:47 703 MB

Using podman (podman is a tool for managing pods, containers, and container images.), let’s try the newly created image

[podman@localhost base]# podman run base_bud
 * Serving Flask app "hello" (lazy loading)
 * Environment: production
 WARNING: This is a development server. Do not use it in a production deployment.
 Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 437-588-275

And listing our running container

[podman@localhost base]# podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
32597e3dd8c5 localhost/base_bud:latest python /app/hello... 2 minutes ago Up About a minute ago upbeat_mcnulty

Checking our app by going inside the container

[podman@localhost base]# podman exec -it 32597e3dd8c5 /bin/sh
# curl localhost:8080
127.0.0.1 - - [27/May/2019 14:56:46] "GET / HTTP/1.1" 200 -
Hello World
#

In this post we saw how simple (and identical to docker) it is to use buildah to create an OCI compliant container image.