Avengers of Container World, Episode 2: Buildah and Skopeo Hands-On
In this post, we will explore Buildah and Skopeo, build a Node.js application container using Buildah, and run it using Podman.
Join the DZone community and get the full member experience.Join For Free
In the last episode (Episode 1: Podman Hands on), we got Podman working on CentOS/VirtualBox. We also pulled the tomcat image and got it running. In this episode, we will explore the advantages of Buildah and Skopeo and build a complete custom image with our sample web application.
Docker provided a very sophisticated configuration file-based provisioning with Dockerfile and Docker Compose. It provided a simple YAML-based configuration that the Docker daemon would use to build custom images as well as configure and provision the container. Docker daemon has the functionality to build, pull, push, run, and manage containers.
So why do we need Buildah or Skopeo?
As mentioned in Episode 1, a single point of failure, root access, memory footprint, and performance are some of the shortcomings of the Docker approach. The next generation OCI compliant tools like Podman, Buildah, Skopeo, along with CRI-O, provide a more modular, daemon-less approach, to keep it simple.
Why is daemon-less and rootless a big deal for building images and running containers?
Docker daemon runs with root access. Dockerfile has the configuration of how to build an image, which means, to perform Docker builds, we need root access, which is too permissive in security/compliance sensitive enterprises. Docker build involves pulling images, installing packages, building artifacts, etc., which might require root access.
Podman and Buildah use user namespaces to overcome this root access problem. Here is how it works:
- User Namespaces provides the isolation of Linux processes (refer here for more details about namespaces). User namespaces isolate user IDs, group IDs, root directory, keys, etc. Because of this process isolation, a process can have a full privilege within the namespace but have different privileges outside the namespace.
- What this means is that the container will have complete privileges within the container, but will not have access to the host system, providing complete security.
This is a big deal from a security point of view, and also avoids a lot of vulnerabilities on the host system. The host system is completely protected by the user and group privileges, which does not affect the container user and group privileges, and vice versa, and the container will have access to the host resources, as per the userid/groupid privileges of the user, who performs the build and run.
Another cool feature of Buildah is it completely supports Dockerfile builds –
buildah bud (bud=build using Dockerfile) – and let's you build from scratch, step-by-step, using
buildah from scratch. Also,
buildah from <imagename> helps to build new images from the existing image.
Buildah does not need a container runtime daemon, it uses runx.
Buildah from scratch helps in building the image step-by-step, and test it, before committing and creating the image. This also helps clean up temporary files and create the most optimum image, while Docker internally does this with every command in Dockerfiles. Buildah provides better flexibility here.
Podman, Buildah, and Skopeo use registries to search, pull, and push the images. Typically we will find the registry configurations in
/etc/containers/regitries.conf. Here is a typical snapshot of how it looks.
The red circle highlights the list of all registries to search for, in the order of sequence. It can be a Red Hat registry, quay.io, docker.io, or any other custom registries. We can include custom user registries or enterprise registries in this file.
The user who is trying to pull or push the images from the respective registries should have access to those registries, and typically we use a Podman login or Buildah login before we can access the registries.
Now, let's jump in and create a custom image.
Please refer to the previous article for the steps to install VirtualBox, Vagrant, and CentOS. We are going to use the same environment to setup Buildah and Skopeo. Please note that I am creating this environment on a VM (so that I don't mess up my local macOS Docker environment). If you want to do it directly on your machine, you can ignore the vagrant commands.
Let's build a simple Node.js application running on
node:10 image from
docker.io . We will use Buildah to build the container, test it, and then commit it to create an image out of it. We will then use Podman to run that image and test. Let's get started!
To check if the Buildah is installed, try:
Now that we know that Buildah is installed, let's use the
builah from command to start building our message.
buildah from is exactly like the FROM that we give in Dockerfiles. We have to provide the base image on top of which we want to build our container.
buildah from has various options.
buildah from scratch— As the name suggests, We can build a container from scratch, using this command.
buildah from --pull <image>— This is used to build a container from an existing image. Buildah searches for that image name on the configured repositories, in the order we have specified in the
registries.conffile, as mentioned above. We can also specify the URL of the repository from where we want to pull.
buildah bud— Buildah can also build the container from an existing Dockerfile, using
bud(build with Docker) command.
So let's get started building our image!
Creating a Container With a Node.js Base
Let's build a container with a base node:10 image from docker.io. We will be using the
buildah from image command:
buildah from --pull --name "myNodeJSContainer" node:10
As you can see in the screenshot,
buildah interactively provides the image options we have, from the various repositories we have configured in
Let's select the docker.io standard image. The
--name argument is used to name the container here:
Let's check if the container is created using
Now let's create a directory in the container for deploying our app, and make it the default working directory.
Now let's exit the vagrant shell and create a simple Node.js app on our local machine. Here is how the package.json looks:
And here a sample application (
Now let's copy our sample application files to the Vagrant shell. The easier option to upload a file is to use
vagrant upload index.js:
There is an alternate way to upload using a file using SCP. Before we do that, get out of the Vagrant shell and go back to the host. Then execute
ssh-config. I just thought it will be useful to learn this.
Look for the location where
IndentityFile is stored. Here is what I got when I executed this command:
Now let's copy index.js and package.json to the Vagrant shell using
scp(secure copy). Here are the commands — please note that you have to specify the correct identity file path, based on what you see in ssh-config:
Now, let's go back to the Vagrant shell using
vagrant ssh. You should find the files there in the Vagrant home directory. Let's now upload this to the container.
Let's install and run the app.
npm install will install the Node.js app, by downloading all the dependent files.
Let's configure the entry point for the container:
buildah config --entrypoint '["node", "index.js"]' myNodeJSContainer
Let's test the application. To do so, login to the container shell (please note that we are already in a Vagrant shell, and now we are getting into the container shell. If you don't run Buildah on a Vagrant VM, you don't have worry about the Vagrant commands above).
buildah run - tty myNodeJSContainer /bin/bash
The app can be tested by running:
And using a curl to check if the app is running fine.
Once we are happy that the app is running, we need to commit the container image. So far the image is only being constructed – it will not still show in the local repository. For example if you run
you will not see the new container image that we are building, we only see the node image (which got downloaded as we built our container using
buildah from node). Now let's commit the container as an image.
Build an Image From a Container
buildah commit myNodeJSContainer mynodejsapp
I named my image as
myNodeJSContainer was just a working container name.
As you can see, now the whole image is built and committed to a local repository. Now let's see if we can see the image using
As you can see, now we have our image. We can use now use Podman to push the image to the Docker Hub or any other registries. Let's test the container now using Podman.
Run and Test the Container Using Podman
podman run -dt -p 8080:3000/tcp mynodejsapp
This runs the container and starts the container, with the entry point config that we had created using Buildah. Let's now see if the container is running using
We can see that the container is running and we are able to get the response using curl. Now that we are happy with this, we can use
podman push to push it to the desired registry.
That's it! The best part of using Buildah is that we did not need any deamon or long Dockerfile (if we have it we can always use it with bud and still run it, but as a developer/DevOps engineer, Buildah provides more command level flexibility and we should be able to run this using a shell script. We have much more control over the commands we want to run, rather than sending a batch of commands to a Docker deamon).
Now let's take a quick look at why we need Skopeo.
Skopeo is a powerful tool to inspect, copy, and delete images from repositories. Synchronizing the images between repositories (both internal and external) is one of the most powerful features of skopeo.
Installing Skopeo is straightforward on our Centos (for other OSs, refer to https://github.com/containers/skopeo/blob/master/install.md)
Once installed, we can inspect the image's meta data about that was used to build our container:
skopeo inspect docker://docker.io/library/node:10
You will see a long output with all the information about the image.
Skopeo can be used to copy images between repositories using
skopeo copy. This includes Quay, Docker Hub, OpenShift, GCR, Artifactory, etc. Skopeo also provides a way to synchronize images across two repositories/registries using
skopeo sync. Skopeo can also be used to delete the images using
Skopeo is very powerful for GitOps pipelines where the images need to be managed across the pipeline and for Continuous Delivery, where we might be using different repositories/registries.
That's it for now. You will realize the value of Podman, Buildah and Skopeo more as we get into managing large amount of containers and images across various repositories. These tools provide a very light weight way to manage the images and containers, which can easily be coded into any GitOps pipeline. I have plans to do a blog on Modern GitOps soon.
See you soon!
Published at DZone with permission of A B Vijay Kumar. See the original article here.
Opinions expressed by DZone contributors are their own.