Namespaces: how containers isolate

Linux · Expert · 15 min

Containers are just processes in namespaces; understanding them demystifies Docker and Kubernetes debugging.

A namespace wraps a global resource so that processes inside see their own isolated instance. Linux has eight kinds:

  • PID: its own process numbering; the first process is PID 1 inside,
  • network: own interfaces, IPs, routes, firewall rules and ports,
  • mount: own view of the mount table, so a container sees its own root filesystem,
  • UTS: own hostname; IPC: own System V IPC and POSIX message queues,
  • user: own UID/GID mappings, so a process can be root inside and an unprivileged user outside (the basis of rootless containers),
  • cgroup and time namespaces virtualise the cgroup tree view and some clocks.

Every process belongs to one namespace of each kind; /proc/<PID>/ns/ shows them as links whose numbers identify the namespace, and lsns lists them system-wide.

unshare starts a program in new namespaces (--pid --fork --mount-proc, --net, --user --map-root-user), and nsenter -t PID -n (or -a for all) runs a command inside another process's namespaces. That is extremely useful for containers: you can use the host's tools, such as ss, ip or tcpdump, inside a container's network namespace even if the image has none.

A container is roughly: namespaces for isolation, cgroups for resource limits, a layered root filesystem, capabilities and seccomp filters to restrict privileges. There is no "container" object in the kernel.

Example

ls -l /proc/$$/ns | tail -n +2 | head -n 3   # cgroup, ipc, mnt ... -> 'net:[4026531840]'
sudo unshare --pid --fork --mount-proc ps -o pid,cmd
#   PID CMD
#     1 ps -o pid,cmd       <- new PID namespace: only this process, as PID 1
sudo unshare --net ip -br link              # lo DOWN   (an empty network stack)
unshare --user --map-root-user id -u        # 0  (root inside, no sudo needed)
sudo unshare --uts bash -c 'hostname sandbox; hostname'   # sandbox (host unchanged)
lsns -t net                                 # network namespaces in use
sudo ip netns add blue && sudo ip netns exec blue ip -br link   # lo DOWN
pid=$(docker inspect -f '{{.State.Pid}}' web)
sudo nsenter -t "$pid" -n ss -tlnp          # host's ss inside the container's netns

Remember

  • Namespaces isolate PIDs, network, mounts, hostname, IPC, users, cgroups and time
  • unshare creates namespaces; nsenter joins a process's namespaces
  • A container = namespaces + cgroups + root filesystem + restricted privileges

Watch out for

  • Installing debugging tools inside a running container image instead of entering its namespaces with nsenter.

Check yourself

  1. Which namespace lets an unprivileged user appear as root inside a container?
  2. How can you run the host's `ss` command inside a container's network namespace?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: man7.org/linux/man-pages/man7/namespaces.7.html

Builds on

Unlocks

Nothing builds on this yet.