Search code examples
dockercontainersrunc

libcontainer, runc and nsenter bootstrap


I am fairly new to docker/containers, and am trying to better understand it by looking at the code.

Looking at runC, it looks like it uses libcontainer much like nsinit in the old code base did, and I am trying to use it as a starting point to understand how to use libcontainer and also to dig deeper into how libcontainer works.

One of the things that I find a bit tricky to understand is the bootstrap process and the call into the C code for nsexec.

I do broadly understand that some initialisation needs to be done for namespaces etc before the calling app (runC/libcontainer) can hand over control to the container process, but I cannot seem to find a good step by step explanation of this. Does anyone know of any good docs in this area ?

Am I correct in assuming that as part of this bootstrap process, the C code will call back into (a clone/child of) runC with an "init" cmd line flag ?


Solution

  • Here's an explanation. You can read this document but it's a bit out-of-date to be honest. Effectively this is how it works.

    When you import "github.com/opencontainers/runc/libcontainer/nsenter" in a Go program, we have some magical __attribute__ stuff that tells the Go compiler to make nsexec run before the Go runtime "boots". Effectively this means that every time that you run any runC program, our code (in github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c) runs before the Go runtime starts.

    This code doesn't actually do anything when you're running a normal user process, just calling runC. However, if your process is going to be the container init process (it has the _LIBCONTAINER_INITPIPE environment variable set) then it will read a bunch of configuration information from the file descriptor specified by _LIBCONTAINER_INITPIPE and set up namespaces and other things accordingly. After all of this is set up, the function will return and the Go runtime will boot into the runc init code which then finalises the setup of the container.

    All of this code is fairly similar to how libcontainer worked before the runC split.