Linux Namespaces

Linux namespaces comprise some of the fundamental technologies behind most modern-day container implementations. At a high level, they allow for isolation of global system resources between independent processes. For example, the PID namespace isolates the process ID number space. This means that two processes running on the same host can have the same PID!

This level of isolation is clearly useful in the world of containers. Without namespaces, a process running in container A could, for example, umount an important filesystem in container B, or change the hostname of container C, or remove a network interface from container D. By namespacing these resources, the process in container A isn’t even aware that the processes in containers B, C and D exist.

It follows that you can’t interfere with something if it’s not visible to you. And that’s really what namespaces provide - a way to limit what a process can see, to make it appear as though it’s the only process running on a host.

Note that namespaces do not restrict access to physical resources such as CPU, memory and disk. That access is metered and restricted by a kernel feature called ‘cgroups’.

👟 Kicking the tyres

The following has been tested on an Ubuntu 16.04 Xenial machine

Let’s jump straight in with a practical example of namespaces in action.

$ unshare -h

Usage:
unshare [options] <program> [<argument>...]

Run a program with some namespaces unshared from the parent.

Options:
-m, --mount[=<file>] unshare mounts namespace
-u, --uts[=<file>] unshare UTS namespace (hostname etc)
...

The unshare command allows you to run a program with some namespaces ‘unshared’ from its parent. Essentially what this means is that unshare will run whatever program you pass it in a new set of namespaces.

Let’s run through an example using the UTS namespace. The UTS namespace provides isolation of the hostname and domainname system identifiers. This isolation can be tested by running hostname my-new-hostname inside a UTS namespaced /bin/sh process, and confirming that the hostname change is not reflected outside that process.

$ sudo su                   # become root user
$ hostname # check current hostname
dev-ubuntu
$ unshare -u /bin/sh # create a shell in new UTS namespace
$ hostname my-new-hostname # set hostname
$ hostname # confirm new hostname
my-new-hostname
$ exit # exit new UTS namespace
$ hostname # confirm original hostname unchanged
dev-ubuntu

Breaking this down, we start by running sudo su to become the root user. Root privileges are required to create most namespaces (the exception being the user namespace - more on that in a later article). Then we run hostname to confirm our current hostname ('dev-ubuntu' in my case).

Now for the exciting part! The unshare -u /bin/sh command drops us into a shell that's running in a new, separate UTS namespace. We then run hostname my-new-hostname to set the hostname inside the new UTS namespace only. The change can be confirmed by running hostname again.

Lastly we exit the namespaced shell and run hostname one last time. We can see that the value for the hostname matches the original value, despite having run hostname my-new-hostname in between. This is because that change only took effect inside the new UTS namespace.

👑 7 namespaces to rule them all

The above example demonstrates the UTS namespace, but the fun doesn’t end there. At the time of writing there are 7 namespaces available:

  1. Mount - isolate filesystem mount points
  2. UTS - isolate hostname and domainname
  3. IPC - isolate interprocess communication (IPC) resources
  4. PID - isolate the PID number space
  5. Network - isolate network interfaces
  6. User - isolate UID/GID number spaces
  7. Cgroup - isolate cgroup root directory

Most container implementations make use of the above namespaces in order to provide the highest level of isolation between separate container processes. Although note that the cgroup namespace is slightly more recent than the others and isn’t as widely used.

📺 On the next …

The unshare command is great, but what happens when we want more fine-grained control over the namespaces in our programs? The answer to this and plenty more coming up, stay tuned…

Update: Part 2, “Namespaces in Go - Basics” has been published and is available here.