Containers from Scratch — What namespaces and cgroups Actually Are
A container is not a lightweight virtual machine — it is an ordinary process with its view restricted. We take it apart into namespaces (what you can see), cgroups (how much you may use), and images (stacked read-only layers), from the first analogy through overlayfs, cpu.max, and how to read exit code 137.
The tool that killed "works on my machine"
A new teammate needs half a day before the code runs. A test that passed locally fails in production. You dig in and find a different language version, an older C library, one missing environment variable. Software never runs on code alone. It runs on code plus a large pile of assumptions around it.
A container is a way to ship that pile along with the code. "The same image starts the same way on a laptop, in CI, and in production" is the promise that modern server operations are built on.
And here is where most people trip. A container is not a lightweight virtual machine. The thing that behaves like one is, underneath, an ordinary process running on your machine. This article takes that process apart into three pieces — namespaces, cgroups, and images — and builds it back up from nothing.
Analogy: building a second house vs. putting up a wall
A virtual machine is like building another house on a rented plot. It goes up from the foundation, so it gets its own plumbing, its own gas line, its own front door. In software terms, the hypervisor fakes a CPU and a disk, and a full guest OS kernel boots on top of that fake hardware. That is why a VM can run Linux, or Windows, or anything else. You get strong isolation, and you pay for a whole house worth of materials.
A container is like putting up a partition inside a building that already exists. Foundation and plumbing are shared; all you install is a wall and a lock. The shared part is the kernel — the core of the OS that talks to hardware directly (the job described in Processes and Memory from Scratch). A container never boots a second kernel. It asks the host's kernel for everything, the same way any other program does.
Every property of containers follows from that. No kernel boot means starting one is about as involved as starting a process. One kernel for everyone means many more of them fit on the same machine. And because the kernel is shared, a Linux container fundamentally runs on Linux, and the isolation is not as strong as a VM's. You trade one wall's worth of thickness for the lightness.
Neither one is the better tool. If you are running code handed to you by strangers, you want the thick wall. If you want dozens of copies of your own application running in exactly the same shape, partitioning a room is faster and cheaper. Which is why real clouds are two storeys tall: your containers run inside a VM that the provider booted for you. The question is never "VM or container" — it is how many boundaries you want stacked, and where.
Intuition: Linux has no feature called "container"
This surprises people: there is no create_container() system call in the Linux kernel. What exists is a way to stack three things onto an ordinary process.
- Namespaces — change what the process can see. Other processes, the network, the file tree get cut off from the outside world
- cgroups (control groups) — cap how much it may use: CPU, memory, I/O, number of processes
- Images — swap out the files it sees, assembled from a stack of read-only layers
"Starting a container" is just shorthand for configuring those three and then running a program. Run ps on the host and the processes inside your containers show up in the list like anything else — the opposite of a VM, whose insides are opaque from outside.
Laid out in order, here is what docker run actually does. The runtime first makes sure the image's layers are present on disk and assembles them into a single file tree. Then it creates a fresh set of namespaces, replacing the world the new process is about to see. Then it creates a cgroup directory, writes the limits into it, and registers the process there. Finally it switches the root to that assembled file tree and executes the start command recorded in the image. That is the whole of "starting"; from then on, the thing competes for CPU on exactly the same terms as a program you launched from your own shell.
Which also means that "the container crashed" usually amounts to the program inside exiting. There is no OS shutdown sequence in between, and the namespaces are torn down along with the process. The reason stopping is fast and the reason a crash often leaves you nothing but one last log line are the same reason.
Let's preview what the third one buys you. An image is made of read-only layers, and an identical layer only has to exist once on a host, no matter how many containers use it. Start a hundred containers from the same image and you do not get a hundred copies of the image. What grows is only what each one has written. Compared with cloning a VM disk image per instance, the difference here is measured in orders of magnitude.
Comments
Sign in to comment