When an application comprises a handful of large components only, it’s completely acceptable to give a dedicated virtual machine (VM) to each component and isolate their environments by giving each their own operating system instance.

But when components start getting smaller and their numbers swell, it’s not prudent to give each their own VM if one doesn’t want to waste money on unnecessary hardware resources.

Isolating environments with containers

A container is a single isolated process running in the host OS, consuming only the resources that the app consumes and without the overhead of any additional processes. The process of containerising requires isolating a process from the rest of the system using Linux namespaces and control groups.

A process running in a container runs inside the host’s operating system, like all other processes (unlike VMs, where processes run in separate operating systems). To the process itself, it looks like it’s the only one running on the machine and in its own operating system.

Compared to VMs, containers are much more lightweight, which allows you to run more applications on the same hardware. This is so mainly because each VM needs to run its own set of operating system and system processes alongside the application’s own process.

Applications running inside a VM perform system calls to the guest OS’s kernel which performs machine instructions on the host’s CPU through the hypervisor.

Containers, on the other hand, share the same kernel. All perform system calls on the exact same kernel running in the host OS. The CPU doesn’t need to do work through a hypervisor or any other intermediary.

Technologies that enable containerisation

Two mechanisms make containers possible:

  • Linux namespaces, and
  • Linux Control Groups (cgroups)

Linux Namespaces make sure each process sees its own personal view of the system, i.e. filesystem, processes, network interfaces, hostname and so on. By default each Linux system initially has one single namespace. All system resources belong to this single namespace. But one can create additional namespaces and organise resources across them. When running a process, you run it inside one of those namespaces. The process will only see resources that are inside the same namespace.

To be completely accurate, multiple kinds of namespaces exist, so a process doesn’t belong to one namespace, but to one namespace of each kind. The following kinds of namespaces exist:

  • Mount (mnt)
  • Process ID (pid)
  • Network (net)
  • Inter-process communication (ipc)
  • UTS
  • User ID (user)

Each namespace kind is used to isolate a certain group of resources. For example, the UTS namespace determines what hostname and domain name the process running inside that namespace sees. By assigning two different UTS namespaces to a pair of processes, it will appear to them as though they are running on two different machines.

Linux Control Groups (cgroups) is a Linux kernel feature that limits the resource usage of a process or a group of processes. A process can’t use more than the configured amount of CPU, memory, network bandwidth and so on. This limits the amount of system resources a container can consume.

Further reading

  1. The case for microservices
  2. Introduction to Docker