Creating a viewport into another container
I have had it a couple of times already that I am running a container and quickly want to see the contents of some of the files that are in the container (e.g. verify some settings file or quickly view/edit the source of the scripts in the container). Just like in the above image, I only want to cut a hole or quickly open a window into the container contents :)

The first thing that comes to mind is to use a command like

docker exec -it <CONTAINER_NAME> /bin/bash

to get a bash shell in the running container and use this bash shell to move around in the container.

Simple access/viewing works this way, but when I need to go through some of the source files, my editor of choice is, of course, the best editor there is: ViM (Don't worry, the rest of this post will also hold if you have another favorite :) ).

Normally we should NOT be able to use ViM in the container because it is good practice to keep your container images to the minimum needed. Typically, additional editors like ViM do not need to be part of the container image, meaning that they will not be available in the running containers.

Of course, the action might be anything, instead of just using an editor, you might want to run any other program on the contents of the running container.

One way you can solve this is by using the shell in the running container to install the additionally required applications within the running container. However, if you have to do this more often, this approach becomes quite cumbersome.

PID Namespace

Fortunately, there is another way to solve this issue and that is using the functionality of Docker to share the PID namespace between containers. In the case of our goal to see the contents of a container RunningContainer, you can achieve this by starting a new container ContainerB in the PID namespace of the original container RunningContainer. This is done by using the --pid argument for the docker executable as follows:

docker run -it --rm --name ContainerB --pid container:RunningContainer alpine /bin/ash

By adding the additional argument, the processes are shared between the two containers. You can easily check this by running the ps command in the shell of the new container. This in itself is already very useful, for example for debugging purposes. By giving ContainerB access to the processes of RunningContainer, you can effectively use a debugger on the process(es) running in RunningContainer.

One very cool side-effect of this process sharing that is extremely helpful for the goal I want to achieve is the fact that the process(es) that are running in RunningContainer also have a directory entry in the /proc directory in the container ContainerB because they share the PID namespace. Within the subdirectory for each process under /proc, there is a symlink called root that is pointing to the root of the filesystem for that running process. By following this symlink for any of the processes running in the container RunningContainer, we end up in the root of the filesystem of the original container!


You can easily see this cool feature in action by starting the ash shell in a new container based on the alpine image as follows:

docker run -it --rm --name RunningContainer alpine /bin/ash

After the container is started, you can check that the /tmp directory is empty by using the command ls /tmp.

In a second terminal, you can now start another container that is sharing the namespace of the container we just created:

docker run -it --rm --pid container:RunningContainer --name ContainerB alpine /bin/ash

The ash process in RunningContainer will have pid 1 (it was started before all other processes). Using this information, we can now use the shell in ContainerB to create a new file in the /tmp/ directory corresponding to the tmp directory of the process with pid 1 with the following command:

touch /proc/1/root/tmp/hello_RunningContainer.txt

If you now go back to the terminal of the original RunningContainer container, you can verify that this new file hello_RunningContainer.txt is now available under /tmp by running the command ls /tmp!