1. Overview
A kernel module is a piece of code that we can load on demand. The main purpose of a kernel module is to extend the functionality of the Linux kernel. On the other hand, Docker containers are processes that use the host computer’s kernel. Another key point is that the Docker engine uses the namespaces feature to execute a container in an isolated environment.
In this tutorial, we’ll examine the relationship between kernel modules and Docker containers.
2. Namespaces and Kernel Modules
As we already mentioned, containers are processes that run in isolation thanks to the namespaces feature of the Linux kernel. There are eight namespace types:
- Cgroup
- IPC
- Network
- Mount
- PID
- Time
- User
- Hostname and domain name (UTS)
As we can see, none of the above types deal with kernel modules. As a result, containers aren’t isolated with regard to kernel modules. Moreover, containers use the kernel modules of the host machine that they are running on.
Having said that, we can’t have two containers with different kernel modules running in the same host.
3. Loading Kernel Modules Through the Host
One way to load a kernel module in a Docker container is to load it in its host.
3.1. Starting the Container
Firstly, let’s start a new CentOS Docker container:
$ sudo docker run -d --name my-centos centos:latest sleep 1000
0a3eae9f2b8d3183145e379607ef1c7b1b15d16bb58c2087b05308942cf450cc
The name of the container is my-centos. Here, we run it in detached mode, with the -d option. Furthermore, to keep the container alive, we run a sleep command within it. Thus, we’ll have plenty of time to do our test.
Notably, we picked the CentOS distribution for the container because the CentOS official image, version 8, includes tools that handle kernel modules. This isn’t true for other distributions. In those cases, we can overcome the problem by installing them through a Dockerfile.
3.2. Loading the Module in the Host
The kernel module we’ll load is cordic, a math library. To find out if the module is loaded in the host, we can use the lsmod command:
$ sudo lsmod | grep cordic
$
As we can see, the lsmod command didn’t return cordic. Therefore, the module isn’t loaded in the host. Now, let’s run the same command inside the container:
$ sudo docker container exec my-centos lsmod | grep cordic
$
As we expected, cordic isn’t loaded in the container either.
Now, let’s load the kernel module in the host first. We can do this with the modprobe command:
$ sudo modprobe cordic
By default, the modprobe command doesn’t print anything to the standard output. So, let’s verify that the module is loaded:
$ sudo lsmod | grep cordic
cordic 16384 0
Indeed, we’ve successfully loaded the cordic module in the host. The output of the lsmod command contains three fields:
- name of the module
- size in bytes
- number of other modules that depend on it
At this point, we can check what has happened in the container:
$ sudo docker container exec my-centos lsmod | grep cordic
cordic 16384 0
As we can see, the cordic module is also automatically loaded in the container. This happened because, as we mentioned previously, the container uses the kernel modules of the host.
4. Loading Kernel Modules in the Docker Container
In this section, we’ll do the opposite of the last. We’ll load the kernel module in the container, and then check what happens in the host.
If the module is loaded after following the steps from the previous section, we should unload it before we proceed.
4.1. Create Container in Privileged Mode
By default, the Docker engine doesn’t permit loading a kernel module in a container. Nevertheless, we can permit it by running the container in privileged mode. Privileged mode allows the container to perform actions as if it were the host. To do this, we use the –privileged option.
Despite the above, even in privileged mode, the Docker container won’t be able to run the modprobe command. This is so because the /lib/modules directory isn’t available to the container by default. Still, we can overcome this problem by mounting the directory on the container with the -v option:
$ sudo docker run -d --privileged -v /lib/modules:/lib/modules --name my-centos centos:latest sleep 1000
a247c19b63df28860f29a8f6f013ed6cd56fa146fc644ca72f0affbe7948a358
As we can see, we again created the container my-centos. However, we also used the –privileged option this time. Furthermore, we mounted the/lib/modules directory of the host to the /lib/modules path of the container**.
4.2. Load Module in the Container
Now, let’s run the modprobe command to load the cordic module on the container:
$ sudo docker container exec my-centos modprobe cordic
As before, the modprobe command didn’t produce any output. Yet, we can check if the cordic module is loaded in the container via the lsmod command:
$ sudo docker container exec my-centos lsmod | grep cordic
cordic 16384 0
In this case, the lsmod command returned the cordic module. Let’s check if this module is now also available in the host:
$ sudo lsmod | grep cordic
cordic 16384 0
Indeed, we can see that the cordic module was loaded in the host once the container loaded it.
5. Conclusion
In this article, we’ve examined two methods to load kernel modules in Docker containers. The first was to load a kernel module in the host. The second was to load it in the container. Both methods result in the same outcome – the module is available in both the host and the container.