Docker adds no new isolation primitive to the Linux kernel: it puts cgroups, namespaces and a union filesystem — all already in the kernel — behind a single command-line interface and a versioned image format. The project was open-sourced by dotCloud in March 2013, under the Apache 2.0 licence, and is written in Go.
Context
The problem Docker addresses is not running processes in isolation, solved for years, but distributing them. Packaging an application with all its dependencies so that it behaves the same way on a developer’s laptop, in staging and in production has remained a manual, fragile job. Virtual machines isolate, but they replicate a whole operating system per instance, and pay for it in startup time and memory.
The technologies Docker rests on are mature. cgroups (control groups) entered the mainline kernel in version 2.6.24 in January 2008; the work had started at Google in 2006 under the name process containers. Namespaces have been in the kernel since around 2002 — the PID namespace arrived in July 2007 — and the idea comes from the pervasive use of namespaces in Bell Labs’ Plan 9. LXC and the libvirt drivers already expose these primitives. What was missing was a chain running from the description of an application to its reproducible execution on any machine.
Architecture
Docker, in the releases of this period, does not talk to the kernel directly: it uses LXC as its default execution driver and delegates to it the creation of namespaces and the configuration of cgroups. Three families of primitive work together.
Namespaces give each container its own view of the system: separate namespaces for PID, network, mounts, IPC and hostname mean a process inside the container sees only its own processes, its own network interface, its own filesystem tree. There is no guest kernel: the container’s processes are ordinary processes on the host, with restricted visibility.
cgroups impose the resource limits — CPU, memory, disk I/O — and account for consumption. They are the same mechanism the kernel uses to manage system-wide resources, applied here per process group.
The union filesystem, AUFS in this period, is the part that sets Docker apart from using LXC directly. An image is a stack of overlaid read-only layers; when a container starts, a thin writable layer with copy-on-write semantics is added on top. Layers common to several images are shared on disk and in the cache, so creating a container does not duplicate the base filesystem. This keeps startup on the order of a second and keeps disk usage contained when many containers derive from the same base image.
Above these primitives sits the format that makes the whole thing distributable. A Dockerfile describes declaratively how to build an image: you start from a base image, and each instruction — install a package, copy a file, set an environment variable — produces a new layer. The resulting image is immutable and identified by a hash. The docker run command pulls it if it is missing locally, derives a container from it and starts it. Images are exchanged through a registry, a store you pull from and push to, much as with a Git repository.
The critical point
The decisive design choice is the separation between the execution plane and the distribution plane. Isolation via namespaces and cgroups was already within reach with LXC, but LXC says nothing about how an image is built, versioned and moved between machines. Docker keeps LXC behind an abstraction and adds what was missing: a layered image format with copy-on-write and a distribution protocol. These two together — immutable image plus transport — turn deployment into a deterministic operation instead of a configuration checklist.
That the execution driver is a replaceable layer is stated from the outset. LXC is today’s default driver, but the interface to the kernel sits behind a driver, and nothing in the image format depends on LXC. The kernel primitives can therefore be reached by other means, without touching images or Dockerfiles.
Implications
For a developer, the immediate effect is that the same image built locally is the one that runs in production, byte for byte. Dependencies are explicit in the Dockerfile and no longer hidden in the state of the machine. The class of failures of the “works on my machine” kind shrinks to a difference of kernel or host configuration, a far narrower surface than before.
There is also a knock-on effect for the open-source ecosystem. Distributing software as an executable image, alongside a distribution package or source to compile, lowers the cost for anyone who wants to try a project: a docker run on a published image takes the place of a page of installation instructions. The flip side is that reproducibility shifts from the build recipe to the binary artefact, and an opaque image is only as auditable as the Dockerfile that produced it.
Limits
At this date Docker is at an early stage and should be treated as such. The project is declared not ready for production, interfaces change from one release to the next, and the only driver is LXC. Sharing the kernel between host and container means isolation is worth whatever namespaces and cgroups guarantee — weaker than the separation of a hypervisor — with an attack surface that takes in the kernel’s entire system-call interface; for untrusted multi-tenant workloads this needs explicit assessment. The dependency on AUFS ties operation to a component that is not in the mainline kernel, with what that entails for portability across distributions. These are constraints to weigh before taking containers outside the development and test environment.
- https://www.docker.io/
- https://github.com/dotcloud/docker
- https://pyvideo.org/pycon-us-2013/the-future-of-linux-containers.html
- https://en.wikipedia.org/wiki/Cgroups
- https://wiki.openvz.org/History_of_containers_in_Linux_kernel
- https://www.noze.it/en/insights/docker-open-source/
Cover image: Solomon Hykes, founder of Docker, speaking while seated in an armchair at a tech conference — photo by Anthony Harvey / Getty Images for TechCrunch, CC BY 2.0 — https://commons.wikimedia.org/wiki/File:518392245AG185_TechCrunch_D_-_Solomon_Hykes.jpg