Share this:

Source | April 28, 2015 by Leo Reiter

Containers are game changers in cloud computing, despite the large installed base of traditional hypervisor-based infrastructure

What’s hot in today’s cloud computing world? Containers are fast becoming a viable alternative to virtualization for the right use cases. But to understand why containers can be a better option, we need to first understand their origins.

In basic terms, containers are application-centric environments that help isolate and run workloads far more efficiently than the traditional hypervisor technology found in commodity cloud Infrastructure as a Service. Modern operating systems (Linux, Windows, etc.) are made up of two basic parts: kernel space and user space. As its name implies, Kernel space is home to the operating system kernel, or the low level instructions that boot the machine, control hardware, provide subsystems (e.g., networking, storage, etc.), and schedule tasks. Tasks (processes, threads, etc.) run in user space, which is home to applications and services. Different operating systems have different levels of modularity and functional “splits” between kernel space and user space, but most architectures are conceptually the same. While hypervisors run virtual machines that make up both spaces, containers encapsulate just the user space, greatly reducing complexity and redundancy. The immediate benefit is higher performance and less “bloat”, extremely important to the economics of cloud computing. The popularity of containers is a direct result of the realization that hypervisor-based technologies are expensive to host and manage for many types of applications.

The Origins of Containers
Containers are not new. Most of today’s Linux-based examples can trace their lineage directly to what is known as a UNIX “chroot jail” (sometimes pronounced change root jail), developed in the late 1970s and added to BSD (a popular UNIX variant) in 1982. While today’s technology is far more advanced, the underlying concept has not changed. A chroot jail allows you to load just the user space bits of an application and its dependencies, and then lock execution inside that space (hence the term “jail”). The code running in the jail has no visibility to the underlying host operating system it’s running on, except when it interfaces with kernel space (e.g., accesses storage, networking, graphics, etc.). With the Kernel space remaining outside the jail, a host can run any number of jails concurrently. Processes running inside the jail(s) look like normal applications and services to the host kernel. For example, if you decide to run an Apache web server inside a chroot jail, you would only need to create a directory structure that contains the Apache binary code, as well as the libraries it needs to load (such as the C runtime library, etc.). When you run that Apache server, it can only see files living in its jail, allowing for a very secure way to run web services. What’s more, because the container packages both the application and its dependencies, you can ensure that there are no conflicts or version inconsistencies with other components or applications on the system. This avoids “dependency hell” without having to resort to virtual machines.

Why Use Virtual Machines?
While containers are viable, they have limitations. Most significantly the applications they run must be compatible with the underlying “host” operating system kernel. For example, you cannot run a Linux application on top of a Windows host, as you can when using Hyper-V. But on the flipside, containers offer tremendous benefits, such as performance, orchestration velocity, and hardware accessibility. And to further counter the kernel compatibility limitation, containers may indeed host different “flavors” or “personalities” of the same operating system. For example, it is possible to run unmodified Red Hat Linux applications on an Ubuntu kernel, and vice-versa.

Virtual machines used to be more secure than containers, providing higher isolation than traditional chroot jails. But this is no longer universally true, as new facilities in the Linux kernel have pretty much closed this gap.

There are really only two major reasons you should use Virtual Machines over containers:

1)      If you need to run applications not compatible with the host operating system. Since Linux is by far the most popular platform for cloud computing applications, including both computationally intensive (HPC) as well as web services (e.g., LAMPbased), this is becoming a narrow use case.

2)      If you need to be able to transparently migrate workloads between hosts, to rebalance utilization for example. This is interesting to commodity clouds that oversubscribe workloads, but really has no place in a High Performance Computing environment where each node or “host” typically only runs one application at a time.

Performance Benefits of Containers
In comparison, containers run applications faster than virtual machines because they do a lot less work, resulting from far less redundant code. For example, to establish an outbound TCP/IP connection inside a container, you simply call the host kernel (outside the container) to do it and receive a socket that you can use immediately. In a virtual machine, you have to emulate the entire network subsystem, including the driver. This means multiple OSI layers that the host kernel already supports, since the application inside the virtual machine cannot just call the host kernel directly. That same TCP/IP connection inside a virtual machine means calling the virtualized kernel, which in turn hands the request over to its virtualized network subsystem, which in turn runs the virtualized TCP state machine, which in turn injects packets into the virtualized network driver, which then goes through a host bridge, which then goes to the physical network. No matter how fast the virtualized code is, and how much paravirtualization is used, this is still a lot of redundant work. If you are oversubscribing, every single virtual machine running on the host must in turn do the same – so not only is the functionality redundant between the kernel and the virtual machine, it’s redundant across all the virtual machines.

If you are not oversubscribing, which is desirable in HPC, you still pay a “hypervisor tax.” While it’s not competing for resources with other virtual machines, it still wastes time executing a lot of redundant code. While some virtualization vendors put the cost of the “hypervisor tax” in this single virtual machine mode at 5% or less, in practice, degradation can be much higher. Even 5-10% still adds up at scale. If you are an HPC-focused ISV, chances are you’ve painstakingly optimized every bit of code to leave nothing on the table. You’ve labored tirelessly to win that last 1-2% of performance.

Why would you dilute your hard work with the built-in overhead of virtual machines rather than enjoy the bare metal performance of containers? Or, why struggle with complex (and limited) device “pass-through” when you can just address your coprocessors and accelerators directly as they were meant to be used?

Even if the “hypervisor tax” is “affordable” to you, containers really shine at scale. Virtual machines must “cold boot” much like physical machines. Containers just launch applications on systems that are already “warm” and ready to process. If you’ve got a distributed application that requires deployment across hundreds or even thousands of nodes, the extra overhead of “booting” virtual machines adds up quickly, which demands complex orchestration techniques such as “hot seating” environments rather than just scaling up and down on demand. It’s no wonder the state of the art in massively parallel Big Data architectures is starting to favor containerization over traditional virtualization.

Containers are game changers in cloud computing, despite the large installed base of traditional hypervisor-based infrastructure. In a world where economics rule, better performance, less overhead, and faster orchestration lead the charge to adoption of this exciting technology.