logo
cd ..

how cgroups and namespaces become a container (our beloved docker for example)

Comments
3 min

how cgroups and namespaces become a container (our beloved docker for example)

how cgroups and namespaces become a container (our beloved docker for example)

containers get sold as some kind of magic lightweight VM. they're not. a container is a normal linux process that's been lied to about the world, and the lying is done by two kernel features that existed long before docker: namespaces and cgroups. once you see the two pieces, "container" stops being magic and becomes an implementation detail.

namespaces: the lying part

a namespace makes a process believe it's alone. the kernel gives it a private version of some part of the system:

  • PID: inside the namespace, your process is PID 1. it can't see any other process on the machine
  • NET: its own network stack. own interfaces, own routing table, own iptables. this is where container IPs come from
  • MNT: its own filesystem mounts. this is why the container "has" its own filesystem
  • UTS: its own hostname
  • IPC: its own shared memory and semaphores
  • USER: inside here, you can be root (UID 0) while actually being some unprivileged user outside. this is how containers "run as root" without root-ing your machine

you don't need docker to see this. try:

sudo unshare --pid --fork --mount-proc bash
# now run:
ps

you're in a shell that thinks it's a fresh linux machine. ps shows only your bash as PID 1. no docker involved, no image pulled, nothing. that's the core illusion, straight from the kernel.

cgroups: the rules part

namespaces hide the world, but the process still competes with everything else on the machine for CPU and RAM. cgroups (control groups) fix that: they put processes in buckets and enforce limits per bucket.

# limit a process to 50% of one cpu core and 100MB of ram:
sudo systemd-run --scope -p CPUQuota=50% -p MemoryMax=100M stress

when a container "has 2 CPUs and 512MB of memory", that's not a VM with allocated hardware. that's a cgroup with two numbers in it, enforced by the kernel scheduler and the OOM killer. no virtualization anywhere.

so what is docker then?

docker (and podman, and containerd, and lxc) are just polish on top of these two features:

  1. create namespaces (isolate)
  2. put the process in a cgroup (limit)
  3. set up mounts with a filesystem image (the "image" part, which is also just mounts, layered)
  4. run one process

that last point is the mind-bender if you're coming from VMs: a container is not a thing that runs. it's one process, with extra steps. your nginx container is literally one nginx process that got namespaces and cgroups applied at fork time. it sits on the same process list as everything else:

ps aux | grep nginx
# the container's nginx. regular PID, regular process, lying to itself

this is also why containers start in milliseconds. there's no boot, because there's nothing to boot. it's a fork with paperwork.

why this matters

  • "it works in the container": understanding namespaces means you can debug why. usually it's a different NET namespace or different mounts
  • security: namespaces limit what the process can see, cgroups limit what it can take. misconfigured ones (running with --privileged, or with the docker socket mounted) break the illusion from the outside
  • the escape stories: every "container escape" CVE is really "found a way around the namespaces". you'll read those headlines differently now

resume:

  • containers = namespaces (isolate what the process sees) + cgroups (limit what it can use)
  • sudo unshare --pid --fork --mount-proc bash is a container with no docker, in one command
  • a container isn't a thing running. it's one regular process with paperwork
  • docker's real contribution is making the paperwork easy, not inventing the concept