Exercise 1.8 - Creating Custom Container Images with Buildah

Return to Workshop

Exercise Description

In this exercise, you will build a custom application container utilizing a Fedora Apache container base image, customize that image and test the image functionality.

After completing this section, you will be able to build images from an existing base image using buildah and other host based tools.

Section 1: Overview of buildah and podman

Buildah specializes in building OCI images. Buildah’s commands replicate all of the commands that are found in a Dockerfile. This allows building images with and without Dockerfiles while not requiring any root privileges. The flexibility of building images without Dockerfiles also allows for the integration of other scripting languages into the build process.

Podman specializes in all of the commands and functions that help you to maintain and modify OCI images, such as pulling and tagging. It also allows you to create, run, and maintain those containers created from those images.

These utilities are pre-installed on the workshop systems, but if you needed to install them on your own hosts, you would enter: sudo yum install -y buildah podman

Section 2: Create a container using buildah

In this section, we will use buildah to pull down a Fedora Apache image to use as our base container image.

Step 1: Use buildah to pull the base image

The Fedora Apache base image is a convenient starting point for creating containers.

To build an application container from the base image, we will create a working container with buildah. A working container is a temporary container used as the target for buildah commands.

buildah from registry.fedoraproject.org/f29/httpd

Step 2: Verify the container image pulled correctly

Verify that your pull request for the Fedora container image completed. Using the buildah command will display and allow you to verify what container images your user has access to.

Buildah will append -working-container to the image name used. If that name already exists, a number will also be appended. For this exercise, you should see an image with the container name httpd-working-container.
buildah containers
xxxxxxxxxxx   *        xxxxxxx             registry…/:latest    httpd-working-container

Section 3: Creating an application image from an existing base

Step 1: Install a simple home page

Once the packages are installed in the working container, place a one-line home page we can use to check that our container works properly.

echo 'Welcome to the RHEL8 workshop!' > index.html
buildah copy httpd-working-container index.html /var/www/html/index.html

Step 2: Set httpd to start at launch

To set httpd to start when the container is run, modify the metadata with the buildah config subcommand.

buildah config --entrypoint "/usr/bin/run-httpd" httpd-working-container

The above option to buildah config is equivalent to the CMD directive in an OCIFile.

Step 3: Commit changes to the modified base container using buildah

Once the contents of the working container are complete, and the metadata has been updated, save the working container as the target application image using buildah commit. During the container customization process, you can choose how often you want to save your customizations in order to test each modification that has been completed. In this case we are saving both the installation of apache, a simple home page and the directive to start the httpd service:

buildah commit httpd-working-container el-httpd1
Getting image source signatures
Skipping fetch of repeat blob sha256:24d85c895b6b870f6b84327a5e31aa567a5d30588de0a0bdd9a669ec5012339c
Skipping fetch of repeat blob sha256:c613b100be1645941fded703dd6037e5aba7c9388fd1fcb37c2f9f73bc438126
Skipping fetch of repeat blob sha256:188ab351dfda8afc656a38073df0004cdc5196fd5572960ff5499c17e6442223
Copying blob sha256:8df24355b15ad293a5dd60d0fe2c14dca68b0412b62f9e9c39c15bb8230d1936
26.80 MiB / 26.80 MiB [====================================================] 0s
Copying config sha256:b04fe2c73b034e657da2fee64c340c56086a38265777556fa8a02c5f12896e66
2.42 KiB / 2.42 KiB [======================================================] 0s
Writing manifest to image destination
Storing signatures

In this example, each previous buildah subcommand results in a separate layer, much like building using an OCIFile. Note that we have named our save point at el-httpd1. You can change this to any label that will reflect what changes you have made at that given save point.

Section 4: Using podman to launch and inspect the application container

Step 1: Use podman to inspect available images

In the previous steps we used buildah to pull down a new image and customize that image. The last step of Section 3 had us commit the changes to the container and name it el-httpd1. Using the podman command, we can view what containers are available to start and run.

podman images
REPOSITORY           TAG      IMAGE ID       CREATED          SIZE
localhost/el-httpd1  latest   b04fe2c73b03   24 sec ago       279 MB
regi.../httpd     latest   8c376a94293d   2 weeks ago      231 MB
The name matches what was set using buildah commit.

Step 2: Use podman to start the customized container and bind port 8080

Podman and buildah use the same local image storage locations, which lets us immediately run our new image without specifying the location of the container or system on which the container will run. Note we are using the name el-httpd1 that we created in our previous section. As mentioned previously, you can launch, test, and then stop the container as you make each individual change. This can be used for general application testing or debugging of a change made to the container during customization with buildah.

podman run -d -p 8080:8080 el-httpd1


Now, we can check the status of the application container using podman. Note you can also see the forwarded ports:

podman ps
CONTAINER ID  IMAGE                        COMMAND              CREATED         STATUS            PORTS                   NAMES
f4d9db69e9b5  localhost/el-httpd1:latest   /usr/bin/run-http... 16 seconds ago  Up 16 seconds ago>8080/tcp  amazing_tharp

Further, you can view the container’s processes with the following:

podman top -l
default   1     0      0.000   6m24.454912357s   ?     0s     /usr/sbin/httpd -DFOREGROUND
default   6     1      0.000   6m24.455036731s   ?     0s     /usr/sbin/httpd -DFOREGROUND
default   7     1      0.000   6m24.455132107s   ?     0s     /usr/sbin/httpd -DFOREGROUND
default   9     1      0.000   6m24.455458435s   ?     0s     /usr/sbin/httpd -DFOREGROUND
default   14    1      0.000   6m24.455616596s   ?     0s     /usr/sbin/httpd -DFOREGROUND

Step 3: Test container application and stop container

Now, we can test retrieval of our example home page:

curl -s http://localhost:8080
Welcome to the RHEL8 workshop!
Note the URL specified matches the port mapping specified on the podman run command.

Since your test was successful, you can now stop the container, and continue with additional customization that you would like to try out. Remember to commit your changes as often as you would like, during the customization process, and use names that reflect the customization you have done to ease troubleshooting.

podman stop -a

This will stop all containers that you have running via podman.

You can verify that the container has stopped running by looking at the list of container processes:

podman ps -l

The first line of the output should show a container that was recently stopped, similar to the following:

CONTAINER ID  IMAGE                       COMMAND               CREATED        STATUS                     PORTS                 NAMES
11fcab28fd31  localhost/el-httpd1:latest  /bin/sh -c /usr/s...  4 minutes ago  Exited (0) 10 seconds ago>8080/tcp  amazing_tharp

Notice the STATUS field is now reported as Exited.

Alternatively, if you would prefer to stop only a single container, you can utilize podman ps to identify the Container ID you wish to stop. (If you’ve already performed the stop -a, you can re-start the container with the podman run command shown in Step 2, above.) Then use the following command, with your unique Container ID number, to shutdown a single instance. For example:

podman stop 11fcab28fd31

Step 4: Cool podman tricks

Here are some lesser-known podman features that’re really worth knowing about.

Exporting a container definition for use in OpenShift

If you’ve built and tested a container with podman, and are happy with the results, you can very easily share that container with OpenShift.

podman generate kube $(podman ps -l | awk '$0 ~ /Exited/ {print $1; exit}') > export.yaml

Take a look at the file to see what’s in it. If you were in an OpenShift project, you could then import this file with:

oc create -f export.yaml

This is an example of a single container export, but you can export complete pods as well.

Generating a SystemD service definition

You can also turn a container directly into a system service. The following command will generate a systemd service definition file:

podman generate systemd $(podman ps -l | awk '$0 ~ /Exited/ {print $1; exit}') > my-container.service

Then, copy the service definition into place and activate it:

sudo cp my-container.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now my-container.service

Step 5: Removing a container

If a container will no longer be used, you can remove it from the system using podman rm. In the command below, we use a bit of bash scripting to determine the CONTAINER ID as it is unique to each container image.

podman rm $(podman ps -l | awk '$0 ~ /Exited/ {print $1; exit}')

The output of this removal is the full CONTAINER ID which was removed from the system.

Workshop Details

Domain Red Hat Logo
Student ID

Return to Workshop