by Joseph Anthony C. Hermocilla, Principal Consultant
Using containers, specifically through Docker engine/runtime, has changed the way I work as a developer. It allowed me to develop and test apps with different configurations on a single host system with ease. In this post, I'll write a little about the facilities that can be used to implement containers in a Linux system.
In operating systems, the process abstraction is an essential concept. A process is a 'program in execution'. I can argue that a container is just a 'fat' version of the process abstraction, it is a 'complete system in execution'. By 'complete system' I mean an encapsulation that has operating system components (bins/libs but no kernel), network configuration, root filesystem, support libraries, and applications.
A container is self-contained and isolated from other containers in the same way a process is self-contained and isolated from other processes. A container is a group of processes rooted in a separate process tree and filesystem with certain security configuration and capabilities.
A modern Linux system or distribution has facilities that can be used to build container engine/runtime from scratch: namespaces, cgroups, chroot, and capabilities.
Namespaces have been traditionally used to allow similar names in a system as long as a name belongs to a different namespace. The Linux man page for namespaces states:
"A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes. One use of namespaces is to implement containers."
cgroups allow a collection of processes to be controlled in terms of the resources they use. The Linux man page for cgroups states:
"Control groups, usually referred to as cgroups, are a Linux kernel feature which allow processes to be organized into hierarchical groups whose usage of various types of resources can then be limited and monitored. The kernel's cgroup interface is provided through a pseudo-filesystem called cgroupfs. Grouping is implemented in the core cgroup kernel code, while resource tracking and limits are implemented in a set of per-resource-type subsystems (memory, CPU, and so on)."
The Linux chroot program is used to run a process with a user-defined root directory which is separate from the host. It has been traditionally used to provide a 'jail' for servers exposed on the Internet to minimize security risks. The Linux man page for chroot states:
"run command or interactive shell with special root directory"
Linux capabilities provide a more fine-grained control when checking permissions. The Linux man page for capabilities states:
"For the purpose of performing permission checks, traditional UNIX implementations distinguish two categories of processes: privileged processes (whose effective user ID is 0, referred to as superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged processes bypass all kernel permission checks, while unprivileged processes are subject to full permission checking based on the process's credentials (usually: effective UID, effective GID, and supplementary group list). Starting with kernel 2.2, Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled. Capabilities are a per-thread attribute."
Combining the above Linux facilities enables the creation of a container runtime. Check out the references below for more detailed demonstrations of these facilities in the context of creating containers.
Contact us if you want to know how you can use containers in your organization. We'd be glad to help.
(Photo by frank mckenna on Unsplash)