Photo by Diego Fernandez on Unsplash

Linux Namespaces (part 5/5)

Docker & Lxc

Lukasz
5 min readSep 26, 2020

--

In the previous four parts I have described (more or less detailed) Linux Namespaces — now let’s practice more with docker and Lxc! If you have any docker container started in your system — check its PID:

$ docker inspect CONTAINER | grep -i pid

e.g.:

$ docker inspect ba26bbd2de76 | grep -i pid 
"Pid": 4542,
"PidMode": "",
"PidsLimit": 0,

Then we can check the NS identifiers used by the main process working in a container:

$ lsns -p PID

It’s worth to mention that in systems different than GNU/Linux docker will work in a virtual machine, so the process indicated by the command docker inspect will not be visible in your local system (host system) — because it is running in that virtual machine (hyperkit, hyperv or VirtualBox). If by chance there is a process with a given PID (in your system) — that’s for sure not the one we are looking for :). In MacOS, if you use hyperkit (used by default by Docker Desktop), we must connect to the console of this hypervisor in order to continue our example. We can do this e.g. by screen command:

$ screen ~/Library/Containers/com.docker.docker/Data/ com.docker.driver.amd64-linux/tty

(the path to the tty file/symlink can be different depending on the version of the Docker Desktop app, so if you can’t find it in the indicated location, please check its subdirectories).

In case of VirtualBox I think that it is much easier and everyone will deal with it :)

When we finally work in the console of the system in which the process of our container has been run, we should be able to find it on the processes list:

$ ps ax | grep 4542 
4542 root 0:00 python2

Then the command lsns -p PID should give us the result similar to the one below:

$ lsns -p 4542 
NS TYPE NPROCS PID USER COMMAND
4026531835 cgroup 219 1 root /sbin/init text
4026531837 user 220 1 root /sbin/init text
4026533249 mnt 2 4542 root python2
4026533250 uts 2 4542 root python2
4026533251 ipc 2 4542 root python2
4026533252 pid 2 4542 root python2
4026533254 net 2 4542 root python2

The interesting thing is that Docker does not use “cgroup” and “user Id” NS — in the list above we can see that these NS are inherited after the process init/systemd.

Now we can do the trickconnect to the console of this container thanks to nsenter command:

$ nsenter -m -u -p -n -i -t 4542 bash

Where 4542 is obviously an appropriate PID indicated by the docker inspect command! Of course, the bash shell must be available in our container. The result generally should be identical to the one received after executing:

$ docker exec CONTAINER bash

:)

In the case of Lxc the information on a given container can be acquired by the command:

$ sudo lxc-info -n CONTAINER

After checking the NS (lsns -p PID) it will turn out that Lxc uses cgroups!

$ sudo lxc-info -n deb01 | grep -i pid 
PID: 9867
...
$ sudo lsns -p 9867
NS TYPE NPROCS PID USER COMMAND
4026531837 user 79 1 root /sbin/init
4026532169 mnt 9 9867 root /sbin/init
4026532170 uts 9 9867 root /sbin/init
4026532171 ipc 9 9867 root /sbin/init
4026532172 pid 9 9867 root /sbin/init
4026532174 net 9 9867 root /sbin/init
4026532230 cgroup 9 9867 root /sbin/init

An interesting fact is also that process 9867 (so the main process of the Lxc container) is the init process. It results from the fact that Lxc delivers us the so-called system containers (which contains all the processes regularly started in real systems — that’s why Lxc is much more similar to the virtualization). Docker shares the so-called application containers (where there is no init process and the application itself is started with the PID 1).

Connecting to the Lxc container looks identical like in the Docker container case (notice the additional parameter “-C” meaning cgroups NS):

$ sudo nsenter -m -u -C -i -p -n -t 9867

It works and I encourage you to carry out your own experiments!

Summary

I wrote in one of the previous parts that the tools like docker or Lxc can be considered as the high-level tools used for managing Linux Namespaces.The command nsenter lets us e.g. connect to any container in the way we can call low-level one. Of course, it is not the point, to encourage anyone to use nsenter in everyday work – it is not the case! It is only to give us more details about what happens under the hood :).

Another thing is that as in probably any case of reaching the lower levels in the technological stack also here we gain new possibilities! Using docker or Lxc we work only on defined NS groups (that we call containers) — for tools like nsenter/unshare the idea of container is unknown – here we work exclusively with namespaces. What can it mean? Let's imagine two containers:

$ docker inspect ba26bbd2de76 | grep -i pid 
"Pid": 1111,

$ docker inspect 425d479f0666| grep -i pid
"Pid": 2222,

And now let’s imagine (or test in practice) the effect of the following commands:

$ sudo nsenter -n -i -t 1111 
(nsenter)$ nsenter -m -u -t 2222
(nsenter)$ echo $$
3333

Where the process 3333 is running? In a new container which is using net and ipc NS from the container 1111 and mnt and utc NS from container 2222 (and the default pid NS)? Have we created a new container by mixing two others? An interesting result, isn’t it? :D

I wonder whether that idea of containers (in the way they are implemented by Lxc or docker) will last longer. Maybe soon we will stop using the abstract structures — containers — and start working directly with namespaces? It will surely demand breaking with the similarities to virtualization, but do we really need such connections? Probably the most important will be the users’ awareness — whether they will be interested in using Linux Namespaces directly ? It doesn’t look so (for the time being) and containers seem to be rather a long term solution, but — will see! :)

<< Linux Namespaces (part 4/5)

--

--