I am trying to experiment with mount namespace in Ubuntu. So far I can create an empty mount namespace using the following:

# mkdir test
# unshare --mount
# mount none test -t tmpfs
# cd test
# pivot_root . . 
# cd / <--- test becomes /

When I check a LXC Ubuntu container, the mount command displays the following:

enter image description here

Since the mount namespace initially gets a copy of the mount points, I presume the /dev/sda1 inside the container is the global /dev/sda1 (because there is no /dev/sda1 inside the container once it is started), and yet the contents of the / inside the container correspond to its rootfs. Can someone familiar with LXC please explain what mount operations does LXC do before it does its pivot_root inside the container ?

  • Please do not include screenshots of the text into the question. They are not-copyable, so no one can easily quote parts of your question. Instead, copy the text and place it into the code block. – Danila Kiver Apr 19 at 15:40

To see what LXC actually does, let's create a new container and trace its startup process via strace(1):

[root@localhost /]# lxc-create -n testcontainer -t debian
[root@localhost /]# strace -e trace=clone,chdir,mount,pivot_root,execve \
                           -f -o lxclog \
                           lxc-start -n testcontainer

The resulting trace is written to lxclog file, and here are the most relevant parts of it (ellipses are added by me where some non-significant calls are omitted):

14671 clone(child_stack=0x7fff9379eb80, flags=CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWNET|SIGCHLD) = 14677
14677 mount("/var/lib/lxc/testcontainer/rootfs", "/usr/lib64/lxc/rootfs", 0x7fe4c2d10eac, MS_BIND|MS_REC, NULL) = 0
14677 chdir("/usr/lib64/lxc/rootfs")    = 0
14677 pivot_root(".", "/usr/lib64/lxc/rootfs/lxc_putold") = 0
14677 chdir("/")
14677 execve("/sbin/init", ["/sbin/init"], [/* 1 var */]) = 0

First, a new process (PID 14677) is spawned by lxc-start (PID 14671) using clone(2) and is placed in new mount namespace (CLONE_NEWNS flag). Then inside this new mount namespace the root file system of the container (/var/lib/lxc/testcontainer/rootfs) is bind-mounted (MS_BIND flag) to /usr/lib64/lxc/rootfs, which then becomes the new root. Finally, when the container initialization is finished, the process 14677 becomes the container's init.

The important thing here is that the root directory of container's mount namespace is the bind mount of the directory belonging to the host's root FS. This is why the root mount of the container still has /dev/sda1 as a source in the mount(8) output. However, there also is a difference which is not shown by mount(8) - to see it, try findmnt(8) inside the container:

root@testcontainer:~# findmnt
TARGET                                SOURCE                     FSTYPE    OPTIONS
/                                     /dev/sda1[/var/lib/lxc/testcontainer/rootfs]

Compare this to the output of findmnt(8) from the host system:

[root@localhost /]# findmnt
TARGET                                SOURCE                    FSTYPE     OPTIONS
/                                     /dev/sda1

Note that the source is the same, but inside the container you also see the source directory of the bind mount.

  • One question regarding bind mount used in LXC: Any changes made inside the container to its root directory will be reflected in the rootfs folder in the global /var/lib/lxc/testcontainer/ directory. Is this done for reasons for saving the state of the container, so if it is shutdown and restarted, it can resume from the last change to its root directory ? Thanks. – Jake Apr 21 at 21:28
  • I asked a follow up: unix.stackexchange.com/q/513734/63934 – Jake Apr 22 at 0:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.