Day 41: Exercise: build a "container" from unshare, chroot, cgroups
Proving "containers are just processes" to yourself
The best way to internalize everything from this phase is to build a crude 'container' using nothing but the raw Linux primitives Docker is built on: unshare (create namespaces), chroot (change the apparent filesystem root), and cgroups (limit resources).
sudo unshare --pid --mount --uts --fork --mount-proc bash
# inside this shell:
hostname mycontainer
ps aux # you should see almost nothing — this is a new, empty PID namespace# (in a separate terminal, prepare a minimal rootfs first, e.g. by extracting an Alpine tarball to ./myrootfs)
sudo chroot ./myrootfs /bin/sh
# now / is myrootfs — you cannot see the real host filesystem above itsudo mkdir /sys/fs/cgroup/mycontainer
echo 100000000 | sudo tee /sys/fs/cgroup/mycontainer/memory.max # ~100MB
echo $$ | sudo tee /sys/fs/cgroup/mycontainer/cgroup.procs # add current shell to the cgroupWhat you just built
Combine all three and you have, crudely: a process that can't see other processes (PID namespace), can't see the real filesystem (chroot), and is capped on memory (cgroup). This is genuinely the same foundation runc builds on — just without OCI's polish, image layer management, or networking setup.
Exercise: put it together
Combine unshare, chroot, and a cgroup memory limit into a single script that launches a shell inside your crude 'container', and confirm: (1) ps aux inside it shows almost no processes, (2) ls / shows only your minimal rootfs, (3) allocating more memory than your cgroup limit gets the process killed (OOM).
Key terms
- unshare
- A Linux command that creates new namespaces for the process it launches.
- chroot
- Changes a process's apparent filesystem root, hiding everything above it.
Phase 6 complete — you should now be able to
When a process inside your cgroup-limited shell allocates more memory than the cgroup allows, what happens?