Deep into Container Runtime
In this tutorial, we are going to discuss deep into container runtime and how it works with Container. In the previous tutorial, we discussed two main components of building Containers i.e., Linux Namespaces and Cgroups.
Container and Container runtime?
Containers were created to help us run a program in an environment completely independent of other programs on the same computer. But we will have some problems later if we only use the Linux namespace and Cgroup to run the container.
The first problem is that to create a container, we need to run a lot of commands, which are the command to create Linux namespace, the command to create Cgroup process, the command to configure limit for Cgroup process, then if we want to delete the container, we must run commands to clear namespace and Cgroup.
And the second problem is when we run dozens of containers with the Linux namespace and Cgroup commands, how do we manage those containers, how do we know what the container is running and which process it is used for?
The third problem is that there are containers that already have what we need and it’s on the Container Registry, how can we download it and run it instead of having to create the container from scratch?
With the above problems, instead of having to run so many commands, why don’t we build a single tool to reduce this work, we just need to run one command to create the container and delete it. And that tool can also help us manage many running containers and we know which container is being used for which process. And we can also use that tool to download containers available on the internet. That’s why the container runtime was born.
To summarize, Container Runtime is a tool that manages all running processes of a container, including creating and deleting containers, packaging, and sharing containers. Container runtime is divided into two types:
- Low-level container runtime: with the main task of creating and deleting containers.
- High-level container runtime: manage the container, download the container image, then extract the container image and pass it to the low-level container runtime so that it creates and runs the container.
Some high-level container runtimes even include the ability to package the container into a Container Image and transfer it to the Container Registry.
Low-level Container Runtime
As I said above, the main task of low-level container runtime is to create and delete the container. What the low-level container runtime will do is:
- Create the cgroup.
- Run CLI in the cgroup.
unsharecommand to create an isolated process.
- Set up a root file system.
- Clean up the cgroup after the command completes.
The low-level container runtime will do a lot more, but the above are the main jobs. For example, simulating the process of container runtime creating the container.
ROOTFS=$(mktemp -d) && UUID=9999
Creating a cgroup.
sudo cgcreate -g cpu,memory:$UUID
Set up a limit memory for this cgroup.
sudo cgset -r memory.limit_in_bytes=100000000 $UUID
Set up a limit CPU for this cgroup.
sudo cgset -r cpu.shares=512 $UUID && sudo cgset -r cpu.cfs_period_us=1000000 $UUID && sudo cgset -r cpu.cfs_quota_us=2000000 $UUID
Creating a container.
sudo cgexec -g cpu,memory:$UUID unshare -uinpUrf --mount-proc sh -c "/bin/hostname $UUID && chroot $ROOTFS /bin/sh"
Deleting this group.
sudo cgdelete -r -g cpu,memory:$UUID
Above is the process of simulating the container runtime to create the container.
runc is a most common low-level container runtime, to create a container with
runc we just need to run a command like the following.
$ runc run runc-container /# echo "Hello from in a container" Hello from in a container
High-level Container Runtime
While the low-level container runtime focuses on creating and deleting containers, the high-level container runtime will focus on managing multiple containers, transporting and managing container images, and loading and unpacking container images to the low-level container runtime.
containerd is a common high-level container runtime, it provides us with the following features:
- Download the container image from the container registry.
- Manage container images.
- Run a container from a container image.
- Managing containers.
For example, we will run the following commands to create a Redis container that has a Redis container image available on the container registry. Download a container image.
sudo ctr images pull docker.io/library/redis:latest
Running a container.
sudo ctr container create docker.io/library/redis:latest redis
And if you want to delete a container, run the following command.
sudo ctr container delete redis
You can list containers and images by the following command.
sudo ctr images list sudo ctr container list
It’s similar when we run the docker command, isn’t it?
Although you can load and run a container from an existing container image,
containerd and many other high-level container runtimes don’t help you build containers, and the high-level container runtime doesn’t focus on UI support for users.
So to make it easier for users to communicate with containers, a new tool called Container Management was born, and Docker is one of them.
Docker was one of the first tools to fully support container interaction. Docker support:
- Image build (Dockerfile/docker build).
- Manage container images (docker images).
- Creating, deleting, and managing containers (docker run, docker rm, docker ps).
- Sharing container image (docker push).
- Provide UI for users to manipulate instead of using CLI.
And Docker will go through the APIs to interact with the underlying container runtime to create and run the container for us. The high-level container runtime that docker uses is
dockerd will provide us with a build image feature,
docker-containerd is similar to
docker-runc is similar to