Creating a Container Image from SCRATCH

Anuj Patel
6 min readFeb 7, 2023

--

Ever wondered how base images of centos or ubuntu are created. This article will go through the basics of container and finally show how to create an image from scratch.

Here I’m using RHEL9.1 for doing all the demos but this procedure will work in any Linux OS. With respect to container management tool, I would be using podman which is now powered with crun as the container runtime.

What is a Container?

By docker,

A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another.

Plainly, a container is a runnable app with all its dependencies and environment. A typical environment is an OS that we all can relate to. Therefore, sometimes people say that a container is a OS that can be launched(started) in a second. However, it is not a proper definition for container. A container is a process, same as the bash or firefox process. The process here, in case of container, is the app. The life of the app is the life of the container.

The container gets their isolation from the host by a concept of linux known as namespaces. There are various kinds of namespaces for example network namespace. This isolates a process and makes it believe that a different network connection has been made for it. With respect to Resource management, Control groups also known as CGroups are used. They limit the container to consume a particular value of the resources (RAM, Storage, CPU core, etc.) available in the host.

Software needed

Only two software are needed:

  • podman
  • crun

or their alternatives. Both of these are included in container-tools. So directly install container-tools.

Container Runtime

It’s not actually podman or docker who runs a container. They are the tools that manages container and helps communicating with the container runtime. The latter is the one who actually starts the container.

A container runtime needs to things to start a container:

  • A specification file
  • Rootfs

A specification file specifies all options of the container. It’s exactly the information that we specify in a ContainerFile or in docker commands. For instance volume mounts.

Rootfs consists of all the required filesystem with the app with its dependencies. It consists of bin that are needed to run the app. for example, there a container image that run a java code or aptly speaking, the app is written in java. So, in order to run the app all its dependencies, especially the JVM must be included in the root filesystem.

The root filesystem and the specification file together are known as a bundle. The container runtime actually needs a bundle to start a container.

Creating and running a bundle with the help of crun:

Let’s create a bundle to experience the same.

To create a bundle, we need two things first the specification and second the rootfs.

To create a specification file, use the below command.

crun spec

This will autogenerate a sample specification file that contains the default settings (specification). The data will be stored in a file named a config.json

Change the configuration setting as per your requirement. Here I will change the first program that is to be executed. The key for the same is args whose value I am setting it as bash.

According to the spec file the root file system is rootfs, so create a folder with same name. It is like the / folder that contains all the other filesystem (or folders).

mkdir rootfs
cd rootfs

Working in rootfs/, create a chroot environment. The chroot in linux is like a jail which is isolated from its parent. It has its own binaries (executable softwares) and their own configurations. A chroot is more likely a container that itself is isolated from the host.

To create a chroot directory, we need the shell and its dependent libraries.

#folder for storing binaries
mkdir bin
#folder to store libraries
mkdir lib64

#now copy the bash shell into the bin of the rootfs/ from /bin/
cp -pv /bin/bash ./bin/
#here option p is for preserving permission and v for verbosity while,
# . represents rootfs

#also add ls command to actually see the rootfilesystem
cp -pv /bin/ls ./bin/

In order to find all the dependencies of a particular command use ldd command of linux.

ldd /bin/bash
#Note: type the full file name as ldd need the full path

#The output shall be:
linux-vdso.so.1 (0x00007ffcddd18000)

libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007f8dc650c000)

libc.so.6 => /lib64/libc.so.6 (0x00007f8dc62fc000)

/lib64/ld-linux-x86-64.so.2 (0x00007f8dc66ac000)

Just copy all the files given under lib64, here /lib64/libtinfo.so.6, /lib64/libc.so.6, and /lib64/ld-linux-x86–64.so.2

cp -pv /lib64/libtinfo.so.6      ./lib64/

cp -pv /lib64/libc.so.6 ./lib64

cp -pv /lib64/ld-linux-x86–64.so.2 ./lib64

Do the same process for the ls command.

Now test the chroot environment that has been created. The output should be as given below.

chroot rootfs

Now let’s run the same env but as a container with the help of crun. To do it, go to the folder where bundle is present. (A bundle is a folder where both rootfs/ and config.json are present). By default crun takes the folder which is nothing but the present working directory.

Creation of a container image:

From Scartch, an image can be created in two ways,

  1. From the root file system(rootfs)
  2. From using a Container file

Since a bundle has already been created, let’s begin. This method will use the import subcommand of the container management tool. The import uses a tar file that consists of the contents of the / drive, that is, bin/, etc/, dev/, and others. The spec file is autogenerated by the tool, which is the default one, i.e., the one generated by crun spec. However, the podman import with its -c option that allows the user to change the container config file.

Let’s being with the first practical

  1. change the directory into the rootfs folder.
  2. create a tar archive of all the files and folder in the rootfs/
  3. import the tarfile and change the default values of the container image, here ENTRYPOINT of the container will be changed.
cd rootfs/    #Path to rootfs
tar -cf ../fs.tar * #all the components in one archive
cd ..
podman import -c 'ENTRYPOINT ["/bin/bash"]' fs.tar my
#here my the image name

The tarfile should be like this.

Now, check the images in the system and my:latest will be found

podman images

Try running the container.

podman run -it my

The container launches successfully.

Using DockerFile (ContainerFile) for the same

  1. Change the directory to rootfs
  2. Open the text editor of copy all the files of rootfs one by one.
  3. Change the ENTRYPOINT
  4. Use podman build -t my:latest -f "file name" .(or path to that file)

The ContainerFile shall be like this,

Now run the image by podman run.

That’s the end. Thank you for reading.

--

--