Thursday, December 26, 2024
Google search engine
HomeGuest BlogsHow To Build OCI & Docker Container Images With Buildah

How To Build OCI & Docker Container Images With Buildah

Before we get into the business of building images with Buildah, I believe a little clearing of the bush is worthwhile because the field of containerization can become a maze of jargon within the first opening paragraphs in most articles. First let us get to understand what OCI, Buildah, and Docker are all about.

Open Container Initiative (OCI)

Well, referenced from the OCI Offical Site, the Open Container Initiative (OCI) launched on June 22nd 2015 by Docker, CoreOS and other partners is a lightweight project, for the express purpose of creating open industry standards around container formats and runtime.

The OCI currently contains two specifications: the Runtime Specification (runtime-spec) and the Image Specification (image-spec). The Runtime Specification outlines how to run a “filesystem bundle” that is unpacked on disk. A “filesystem bundle” is a set of files organized in a certain way, and containing all the necessary data and metadata for any compliant runtime (eg Docker and CRI-O) to perform all standard operations against it.

At a high-level an OCI implementation would download an OCI Image then unpack that image into an OCI Runtime filesystem bundle. At this point the OCI Runtime Bundle would be run by an OCI Runtime (eg Docker and CRI-O). Now moving to Buildah.

Buildah

Buildah is a command line tool that can be used to build images compliant with Open Container Initiative (OCI). It can be used with Docker, Podman, Kubernetes or any of your favourite container tool.

Buildah’s commands are similar to all of the commands that you can find in a Dockerfile. Buildah’s goal is also to provide a lower level coreutils interface to build container images, allowing people to build containers without requiring a Dockerfile. The most powerful way to use Buildah is to write Bash scripts for creating your images—in a similar way that you would write a Dockerfile without requiring a daemon.

You can achieve the following and much more with the buildah command

  • Build a container image from a Dockerfile (using buildah bud).
  • Build a container from another base image (buildah from ) or from complete scratch (using buildah from scratch)
  • Inspecting a container or image (using buildah inspect)
  • Mount a container: Mount a container’s root filesystem to add or change the content (buildah mount).
  • Create a new container layer (using buildah commit).
  • Unmount a container: Unmount a mounted container (buildah umount).
  • Delete a container (using buildah rm) or an image (using buildah rmi)

In plain English, OCI sets out the rules for building images and how runtimes should consume it whild Buildah takes those rules set out and used to Build container images. Now what is Docker all about?

Docker

It was hard to keep it brief but reading through will definitely help

Before version 1.11 of Docker, the implementation was a monolithic daemon. The monolith did everything as one package such as downloading container images, launching container processes, exposing a remote API, and acting as a log collection daemon, all in a centralized process running as root (Source coreos). Such a centralized architecture has some benefits when it comes to deployment but unearths other fundamental problems. An example is that it does not follow best practices for Unix process and privilege separation. Moreover, the monolithic implementation makes Docker difficult to properly integrate with Linux init systems such as upstart and systemd https://coreos.com/rkt/docs/latest/rkt-vs-other-projects.html#rkt-vs-docker. This led to the splitting of Docker into different parts as can be shown on the below opening paragraph after Docker 1.11 was launched.

We are excited to introduce Docker Engine 1.11, our first release built on runC ™ and containerd ™. With this release, Docker is the first to ship a runtime based on OCI technology, demonstrating the progress the team has made since donating our industry-standard container format and runtime under the Linux Foundation in June of 2015. Source Docker”.

According to them (Docker), splitting Docker up into focused independent tools mean more focused maintainers and ultimately better quality software.

Since then containerd now handles the execution of containers which was previously done by docker daemon itself. This is the exact flow, the user runs commands from docker-cli. Docker-cli talks to the Docker daemon. The Docker daemon(dockerd) listens for requests and manages the lifecycle of the container via containerd which it contacts. containerd takes the request and starts a container through runC and does all the container life-cylces within the host. runc in brief is a CLI tool for spawning and running containers according to the OCI specification.

How To Build OCI & Docker Container Images With Buildah

Run the command below on your Linux box to install Buildah. We shall install Podman alongside it so that we can use it to run our image from scratch.

### Ubuntu / Debian ###
sudo dnf update
sudo dnf -y install buildah podman

### RHEL / CentOS / Rocky / AlmaLinux ###
sudo apt update
sudo apt install podman buildah

Check if it was installed successfully

### RHEL / CentOS / Rocky / AlmaLinux ###
$ rpm -q buildah
buildah-1.23.1-2.module+el8.5.0+735+2f243138.x86_64

### Ubuntu / Debian ###
$ apt policy buildah
buildah:
  Installed: 1.23.1+ds1-2
  Candidate: 1.23.1+ds1-2
  Version table:
 *** 1.23.1+ds1-2 500
        500 http://ke.archive.ubuntu.com/ubuntu jammy/universe amd64 Packages
        100 /var/lib/dpkg/status

Creating an OCI image from scratch with Buildah

Here, we are going to build an image without anything but a small amount of container metadata and then add everything it needs to run a simple Apache webserver. Doing this allows you to build your container image block by block.

As it had been explained earlier, we use buildah scratch command to achieve this. In order to avoid problems, we shall proceed from here using “root” user account.

brandnewcontainer=$(buildah from scratch)
$ buildah containers

CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME                       CONTAINER NAME   
e6c2f7b0b567     *                  scratch                          working-container

### You can also use Podman ###
$ podman images

The above command creates an empty container (no image). Mount the empty container as follows:

$ scratchmnt=$(buildah mount $brandnewcontainer)
$ echo $scratchmnt
/var/lib/containers/storage/overlay/40e7215211b47e8de47991d0dc7be07e0b1b4f48eda25ebaf6ff8ff46c466be5/merged

Buildah mount command allows you to mount the container’s root filesystem, which gives you access to it from the host.

Next, install packages into the scratch image.

sudo yum -y group install "Minimal Install" --releasever=8 --installroot=$scratchmnt

After the packages have been installed. We can now unmount the image and run it as an independent container becuse it has everything it needs to be independent.

$ buildah umount $brandnewcontainer
e6c2f7b0b5679133ad6e0ad6bd74164dac7f357f0076cf6cc819f9ed664236d5

To run the image as a continer, supply the command below and you should be able to land right into the arms of your new container that you have created from scratch. You can install other applications inside there.

$ buildah run $brandnewcontainer bash
[root@e6c2f7b0b567 /]# ls 
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

As you can see from above, all directories are available as you would expect in a standard container. We can install Apache to test if everything is working fine.

[root@e6c2f7b0b567 /]# yum install httpd -y

Add a simple html page in the default Apache document root and exit from the container

[root@e6c2f7b0b567 /]# echo "Testing Apache." > /var/www/html/index.html
[root@e6c2f7b0b567 /]# exit

Back to our host system, instead of running httpd as an init service, set a few buildah config options to run the httpd daemon directly from the container:

$ buildah config --cmd "/usr/sbin/httpd -DFOREGROUND" working-container
$ buildah config --port 80/tcp working-container
$ buildah commit working-container localhost/firstapache:latest
Getting image source signatures
Copying blob b34ab2705c68 done
Copying config a0c546bc39 done
Writing manifest to image destination
Storing signatures
a0c546bc39271565946d11a843979e017aae73e2b792cc5d9ca589661f427543

After buildah is done committing the image, let us now brandish Podman to run it as follows.

First, check all the images available in the system and to get the ID of the ones we are interested in.

$ podman images
REPOSITORY              TAG      IMAGE ID       CREATED         SIZE   
localhost/firstapache   latest   a0c546bc3927   8 minutes ago   1.68 GB

Now run the image

$ podman run -p 8080:80 -d --name apache-server a0c546bc3927
b1d29603542c8f91a289127cee4fa9157962f8fcdda9baa82b335e08a94c0992

To make sure that our Apache webserver is serving the page we added earlier, we must do something. You guessed it, we can either curl or even use our browser to see the results. Remember that port 80 in the container has been bonded to port 8080 on the system. We can test as shown below using curl.

$ curl -ik "localhost:8080"
HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 22:21:54 GMT
Server: Apache/2.4.37 (centos)
Last-Modified: Thu, 23 Apr 2020 22:02:43 GMT
ETag: "f-5a3fc6872e6c0"
Accept-Ranges: bytes
Content-Length: 15
Content-Type: text/html; charset=UTF-8      

Testing Apache.

As you can lucidly witness, the webserver is listening and has returned a response we were expecting. It is working.

Buildah comes with a lot more tools at your disposal. You can delete images and containers, inspect images and containers among others. To inspect as an example, list all images to get their IDs using “buildah images” command, then snatch the ID of the one you would wish to inspect then run:

$ buildah inspect a0c546bc3927
{
    "Type": "buildah 0.0.1",
    "FromImage": "localhost/firstapache:latest",
    "FromImageID": "a0c546bc39271565946d11a843979e017aae73e2b792cc5d9ca589661f427543",
    "FromImageDigest": "sha256:2060eb441d905934f0aa1749b0d9ed065fd464b30483a963e5719b27836c844d",
    "Config": "{\"created\":\"2020-04-23T22:09:50.515019063Z\",\"architecture\":\"amd64\",\"os\":\"linux\",\"config\":{\"ExposedPorts\":{\"80/tcp\":{}},\"Cmd\":[\"/usr/sbin/httpd\",\"-DFOREGROUND\"],\"Labels\":{\"io.buildah.version\":\"1.11.6\"}},\"rootfs\":{\"type\":\"layers\",\"diff_ids\":[\"sha256:b34ab2705c68989b396dfc33dd5174f34b14890242aae93397d628787d2a6a94\"]},\"history\":[{\"created\":\"2020-04-23T22:09:50.515019063Z\",\"created_by\":\"/bin/sh\"}]}",
    "Manifest": "{\"schemaVersion\":2,\"config\":{\"mediaType\":\"application/vnd.oci.image.config.v1+json\",\"digest\":\"sha256:a0c546bc39271565946d11a843979e017aae73e2b792cc5d9ca589661f427543\",\"size\":396},\"layers\":[{\"mediaType\":\"application/vnd.oci.image.layer.v1.tar\",\"digest\":\"sha256:b34ab2705c68989b396dfc33dd5174f34b14890242aae93397d628787d2a6a94\",\"size\":1684295168}]}",  
    "Container": "",
    "ContainerID": "",
    "MountPoint": "",
    "ProcessLabel": "",
    "MountLabel": "",
    "ImageAnnotations": null,
    "ImageCreatedBy": "",
    "OCIv1": {
        "created": "2020-04-23T22:09:50.515019063Z",
        "architecture": "amd64",
        "os": "linux",
        "config": {
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Cmd": [
                "/usr/sbin/httpd",
                "-DFOREGROUND"
            ],
            "Labels": {
                "io.buildah.version": "1.11.6"
            }
        },
        "rootfs": {
            "type": "layers",
            "diff_ids": [
                "sha256:b34ab2705c68989b396dfc33dd5174f34b14890242aae93397d628787d2a6a94"
            ]
        },
        "history": [
            {
                "created": "2020-04-23T22:09:50.515019063Z",
                "created_by": "/bin/sh"
            }
        ]
    },
    "Docker": {
        "created": "2020-04-23T22:09:50.515019063Z",
        "container_config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "80/tcp": {}
            },

It should produce copious amount of information for you to digest.

Conclusion

Buildah is a cool tool that can build images from scratch or using another image as its base. You can as well modify another image as you wish which makes it quite versatile and flexible. Thus far, we have covered building from scratch and we appreciate you staying till the end.

You may also be excited by:

RELATED ARTICLES

Most Popular

Recent Comments