Containers are an easy and effective way of deploying different applications, commonly used with web applications. They are modular environments that allow us to run resource-isolated processes. It is a type of virtualization. Containers are easy to move, copy and backup, making it a flexible and resource-friendly solution for application deployment.
This post will cover the fundamentals of containerization. If you are not familiar with virtualization, I advise you to read the two posts about it before continuing reading about containerization. Understanding virtualization will make it easier for you to understand containerization and the examples used in this post.
You can find the posts about virtualization from the links below:
What is a container
Containerization is a modern type of virtualization, it is a set of configuration files which can be deployed anywhere to reproduce a copy of the original environment. The underlying operating system’s kernel is used to power the applications running in the containers, meaning that the containers do not run any operating systems. They are just containers for different processes, running different applications.
Containers vs. Virtualization
Virtualization is true isolation between the operating systems. All virtual machines have their own resources and operating systems, they are using the hardware which is normalized by the hypervisor.
Containers are simply processes that run in a resource-constrained environment. They share the host machines OS kernel, the underlying hardware resources, and the host machines binaries. Containers do have their own filesystems.
Containers provide portability and reproducibility since all environments are defined by an environmental configuration file. A container first deployed locally or on a private cloud, will be exactly the same when deployed on the public cloud. Containers make it very simple and fast to develop and deploy applications.
Containers are grouped together strategically because all of them are just application spaces. This allows multiple, related applications to run together on a single host while still being resources-constrained. Containers use resources more efficiently than virtual machines, as it is not true isolation.
Increased developer productivity
As the container environments are consistent, the applications need for cross-service dependencies is removed. Applications can be sliced into individual components where each process runs in its own container environment. For example, a web application that needs NGINX and MySQL could run these processes in different containers.
All application containers are defined by files, which makes controlling the versions of the entire environment quick and easy. Developers can easily use, for example, GIT, when working with containers.
Container use cases
Containers can and are used in multiple ways, here is a list of example use cases for containers:
- Distributed applications
- Batch processing
- Continuous integration
Types of containers
The application containerization technology can be used in different engines that are made by different organizations or companies. In this post we will focus only on Docker and LXC/LXD, just keep in mind that there are other possibilities, such as:
- Google Kubernetes
- Amazon AWS
- Cloud Foundry
Introduction to Docker
Docker is the most popular engine for containerization. It offers container-based “virtualization” that makes it quick for developers to develop, deploy and run their applications.
The containers are isolated and the may or may not have resource restrictions. The containers share the host’s kernel and binaries but have their own filesystems. This is done using namespaces and cgroups. Docker containers run as an isolated process.
Docker is simply a piece of open-source software running on a server. It also provides a remote API for the Docker client software.
Docker client software
Docker client software is either the Docker application running on the actual server or a Windows/OSX application that connects to a remote Docker container.
Docker Hub is an internet SaaS (Software-as-a-Service) platform for sharing and managing Docker containers.
Docker vs. Virtual Machines
Virtual machines require on top of the host operating system a hypervisor software, an operating system, the necessary binaries and libraries and then the actual application we want to run. One operating system can be around 10 GB in size.
Docker only requires the Docker engine, which then shares the host operating system’s kernel with other containers. It has the resource isolation and allocation benefits of a virtual machine but is way more efficient and portable.
Introduction to LXC/LXD
LXC means simply Linux Containers and is the core container service. LXD is an open source container management extension for the Linux Containers. LXD improves already existing LXC features and provides new features and functionalities. They are both open source and is a default on modern Ubuntu server and desktop systems.
LXC/LXD constrains resources using cgroups, namespaces, AppArmor profiles, and SECCOMP. As containers share the host kernel, you can run almost any Linux versions as guest operating systems on LXD.
LXD takes the speed and latency of containers and gives you full system functionality, not just a single process. You can run Docker and LXC inside of LXD to mix container types. LXD is fast and lets you operate the same way you already are, but with container speeds.
LXC/LXD offers many benefits, including:
- Bare-metal performance
- Precise quality-of-service and quotas
- Snapshots and live migration
- Support for ARM, POWER and X86
- Rapid provisioning and instant guest boots
- Remote image services
- Extensible storage and networking
Docker vs. LXC/LXD
Now that we know the basics of Docker and LXD, it is time to compare them to see the differences between these container types.
Docker runs single processes while LXD provides a whole guest OS. The whole OS includes supportive services like SSH and logging, whereas Docker is just the process.
Docker supports DevOps (method for developing, consists of development, quality assurance and operations) and PaaS (Platform-as-a-Service). LXD focuses on IaaS (Infrastructure-as-a-Service).
Docker can be run inside LXD as it is just an application wrapper, this has almost zero performance impact.
Security of containers
As we have covered two different container engines that work in different ways, we also have to cover the security aspects of these two engines separately.
LXC/LXD relies on the cgroups and namespaces for isolation. Cgroups typically provide resource quotas, but often cannot enforce them adequately. This means that if a single container is using all the inotify handles in the kernel to track filesystem changes, the system on the host will fail.
Kernel modules loaded on the host pass through to all containers, exploiting them all. Also, exploiting the kernel of the container can potentially exploit the host’s kernel as well.
Public LXD images could come pre-loaded with malware and some resources cannot be constrained, like disk IO.
Resource quotas are enforced by the Docker service using namespaces and cgroups. Memory IO and process forks are hard to constrain. Running out of UIDs will also affect other containers.
If the Docker container faces a kernel panic, it will cause host kernel panics. Kernel modules loaded to on the post pass through to all containers. Also, exploiting the kernel of a container can potentially exploit the host’s kernel as well.
Public Docker images could come pre-loaded with malware.