Skip to content

Virtualization and container technologies

The progress from mainframes to personal computers meant moving computational power to the end user, but applications were also becoming more complex and resource-hungry.

Applications were moved to servers in datacenters for the increased power and ease of management. One server was dedicated to one application, but this meant that a lot of compute capacity was underutilized.

Popular OSes at the time failed to provide:

  • full isolation of configuration/shared components;
  • temporal isolation for performance predictability: a process using a lot of resources shouldn’t affect others;
  • spatial isolation: if A crashes it shouldn’t compromise B;

The fundamental idea of virtualization is to abstract the hardware of a single computer into several different execution environments.

  • A virtual machine is the software emulation of a physical machine.
  • The host OS is the OS running on the bare-metal server.
  • The guest OS is the OS running inside the virtual machine. It shouldn’t be aware that it is running inside a VM.
  • The hypervisor is the program that virtualizes hardware.

The hypervisor has to provide virtual hardware to the guest VM, with characteristics specified by the user. Sometimes real hardware may have little to do with what the VM sees.

Emulation takes virtualization a step further, allowing the guest OS to run software compiled for another CPU architecture by performing runtime translation to native CPU instructions.

Full virtualization uses ring de-privileging to run all guest software at privilege levels greater than 0.

This allows the guest OS to run unmodified, but it may not be able to fully protect itself due to the lack of privileged ring levels.

The guest OS is executed in an unprivileged domain. When it executes a privileged instruction, the processor launches a trap that is intercepted by the hypervisor. The hypervisor emulates the effect of the privileged instructions for the guest OS.

This works for some systems, but, for example, in x86_64 some sensitive instructions do not trap.

Possible solutions are:

  • interpretation (emulating each instruction): can slow the guest by an order of magnitude.
  • binary translation: the guest OS is not modified, but there is some performance overhead.
  • paravirtualization: the hypervisor applies changes to the guest to obtain near-native performance.
  • hardware support: Intel VT-x and AMD SVM reduce the performance penalty of CPU virtualization.

Depending on where they run, hypervisors are subdivided into three categories:

  • Type 1 hypervisors run directly on bare metal. They are essentially operating systems stripped down to only the essential components, and they usually provide the best performance.
  • Type 2 hypervisors run on a host OS as a normal application. Installation and creation of VMs are simpler tasks.
  • Hybrid hypervisors have a kernel module that handles performance-sensitive parts, but the rest of the system is like a normal OS, simplifying management tasks.

Lightweight virtualization is appropriate when:

  • there is no need for a classical VM and the overhead is not acceptable;
  • the environment must be quick to deploy, migrate and dispose;
  • there’s a need to scale both vertically (thousands of VMs on the same machine) and horizontally (VMs on many different machines).

The Linux kernel provides isolation and resource-management mechanisms that allow implementation of LXCs (Linux containers) or OCI containers (docker, podman).

  • Cgroups: a kernel feature to limit, account for, isolate, or deny resource usage for processes or groups of processes.
  • Namespaces: a kernel feature that isolates system resources, making groups of processes believe they have their own independent operating environment (virtualized views of PIDs, networks, mount points, etc.).

Containers are a technology that simplifies application portability across distributions of operating systems.

A container image packages a minimal version of the directory layout with only the dependencies needed to run the app. The kernel is shared with the host and access to resources is regulated through cgroups and namespaces.