Docker was born in 2013 and popularized the concept of containers to the point that most people still equate the concept of containers with “Docker containers.”
As a first mover, Docker sets the standards that new entrants must follow. Docker, for example, has a large library of system images. All alternatives must use the same mirror format while attempting to change one or more parts of the entire stack on which the Docker is based.
During this time, new container standards emerged and the container ecosystem moved in different directions. Now there are many ways to use containers besides Docker.
In this article, we will cover the following:
-
Use Chroot, Cgroups, and namespaces as the technical basis for containers
-
Defines the software stack on which Docker is based
-
Explain the standards Docker and Kubernetes need to adhere to and comply with
-
Introduces alternative solutions that attempt to replace the original Docker container with components with better and more secure features.
The container’s software stack
Linux features like Chroot calls, Cgroups, and namespaces help the container run in isolation from all other processes, thus ensuring runtime security.
Chroot
All docker-like technologies originate in the root directory of unix-like operating systems (OS). Above the root directory is the root file system and other directories.
This is dangerous in the long run, because any unnecessary deletions in the root directory can affect the entire operating system. This is why there is a system call to chroot(). It creates additional root directories, such as one for running legacy software, another for containing databases, and so on.
For all of these environments, chroot appears to be a true root directory, but in fact it simply adds pathnames to any names that begin with /. The real root directory still exists, and any process can reference any location other than the specified root directory.
Linux cgroups
Control Groups (Cgroups) has been a feature of the Linux kernel since version 2.6.24, 2008. Cgroup limits, isolates, and measures system resource usage (memory, CPU, network, and I/O) of multiple processes simultaneously.
Suppose we want to prevent users from sending a lot of E-mail from the server. We created a Cgroup with a memory limit of 1GB and 50% CPU usage and added the processid for the application to the group. When these limits are reached, the system limits the E-mail delivery process. It may even terminate the process, depending on the hosting policy.
Namespaces
Linux namespaces are another useful abstraction layer. Namespaces allow us to have many process hierarchies, each with its own nested “subtrees.” A namespace can consume a global resource and present it to its members as if it were its own resource.
Specifically, a Linux system starts with a process identifier (PID) of 1, and all other processes are included in its tree. The PID namespace allows us to span a new tree, which has its own PID 1 process. There are now two Pids with a value of 1, each namespace can generate its own namespace, and the same process can have several Pids attached.
A process in a child namespace will not know that the parent process exists, and the parent namespace will have access to the entire child namespace.
There are seven types of namespaces: CGroup, IPC, Network, mount, PID, User, and UTS.
Network Namespace
Some resources are scarce. By convention, some ports have predefined roles that should not be used for any other purpose: port 80 only serves HTTP calls, port 443 only serves HTTPS calls, and so on. In a shared host environment, two or more sites can listen for HTTP requests from port 80. The first site to acquire the port does not allow any other applications to access the data on the port. The first application will be visible on the Internet, while all other applications will be invisible.
The solution is to use network namespaces, through which internal processes see different network interfaces.
In one network namespace, the same port can be open and in another network namespace, it can be closed. To do this, we had to employ additional “virtual” network interfaces that belonged to multiple namespaces. There must also be a router process in the middle that connects requests to the physical device to the appropriate namespace and the processes within it.
Complicated? That’s why Docker and tools like it are so popular. Now let’s take a look at Docker and its alternatives.
Docker: Container for everyone
Before containers ruled the cloud computing world, virtual machines were all the rage. If you have a Windows machine but want to develop mobile apps for iOS, you can either buy a new Mac or install its virtual machine on Windows hardware. Virtual machines can also be clunky, they often gobble up unwanted resources, and they are often slow to start up (up to a minute).
A container is a standard unit of software that has everything you need to run a program: operating system, database, images, ICONS, software libraries, code, and other components you need. The container also runs isolated from all other containers and even from the operating system itself. Containers are lightweight compared to virtual machines, so they can be started quickly and easily replaced.
To run isolation and protection, containers need to be based on Chroot, CGroups, and namespaces.
An image of a container is a template that forms an application on a real machine, and it is possible to create as many containers as possible from a single image. A text file called Dockerfile contains all the information needed to assemble the image.
The real revolution brought by Docker is the creation of Docker image warehouse and the development of Docker engine. These images run in the same way everywhere. As the first widely adopted container image, an unwritten world standard has been formed, and all subsequent entrants must pay attention to it.
CRI and OCI
OCI, which stands for Open Container Initiative, publishes specifications for images and containers. It was launched by Docker in 2015 and has been accepted by Microsoft, Facebook, Intel, VMWare, Oracle, and many other industry giants.
OCI also provides an implementation of the specification, called RUNC, that can directly use containers, create them, run them, and so on.
The Container Runtime Interface (CRI) is a Kubernetes API that defines how Kubernetes interacts with the Container Runtime. It is also standardized, so we can choose which CRI implementation to adopt.
Software stack for containers for CRI and OCI
Linux is the most basic part of the software stack that runs containers:
Note that both Containerd and Cri-O adhere to the CRI and OCI specifications. For Kubernetes, this means it can use Containerd or Cri-O and users won’t notice the difference. It can also use any of the other alternatives we’ll mention now — which is the goal of creating and adopting software standards like OCI and CRI.
Docker software stack
Docker’s software stack includes:
Docker – CLI, docker command line interface for developers
Containerd, originally written by Docker and later launched as a standalone project; It implements the CRI specification
Runc, which implements the OCI specification
Containers (using chroot, cgroups, namespaces, etc.)
Kubernetes’ software stack is almost identical; Kubernetes uses Cri-O instead of Containerd, a CRI implementation created by Red Hat/IBM and others.
containerd
Containerd runs as a daemon on Linux and Windows. It loads the image, executes it as a container, oversees the underlying storage, and is responsible for the runtime and life cycle of the entire container.
Containerd was born in 2014 as part of Docker, became a project of the Cloud Native Computing Foundation (CNCF) in 2017, and graduated in early 2019. If you want to learn more about using Containerd, check out the following article:
Configure the Containerd image repository overview
runc
Runc is the reference implementation of the OCI specification. It creates and runs the container and its processes. It uses lower-level Linux features such as Cgroups and namespaces.
Alternatives to RUNC include KatA-Runtime, GVisor, and Cri-O.
Kata-runtime implements the OCI specification using hardware virtualization as a separate lightweight VM. It runs compatible with OCI, Cri-O, and Containerd, so it works seamlessly with Docker and Kubernetes.
Google’s gVisor creates containers containing its own kernel. It implements OCI through a project called RUNSC, which integrates with Docker and Kubernetes. A container with its own kernel is more secure than one without, but it is not a panacea, and this approach comes at a cost in resource usage.
Cri-o is a container stack designed purely for Kubernetes, the first implementation of the CRI standard. It extracts images from any container image repository and can be used as a lightweight alternative to using Docker.
Today it supports RUNc and Kata Containers as container runtimes, but any other OC-compatible runtimes can also be plugged in (at least in theory).
It is a CNCF incubation program.
Podman
Podman is a Docker replacement without a daemon. Its commands are intended to be as compatible with Docker as possible, so much so that you can create an alias in your CLI interface and start using the word “Docker” instead of “podman.”
Podman aims to replace Docker, so it makes sense to stick with the same command set. Podman tries to fix two problems in Docker.
First, Docker always executes using internal daemons. Daemons are single processes that run in the background. If it fails, the whole system fails.
Second, Docker runs as a background process with root privileges, so when you give a new user access, you’re actually giving access to the entire server.
Podman is a remote Linux client that runs containers directly from the operating system. You can also run them in rootless mode. It downloads images from DockerHub and runs them in exactly the same way as Docker, with exactly the same commands.
Podman runs commands and images as a user other than root, so it is more secure than Docker. On the other hand, there are many tools developed for Docker that are not available on Podman, such as Portainer and Watchtower. Getting rid of Docker means giving up the workflow you established earlier.
Podman’s directory structure is similar to buildah, Skopeo, and Cri-i. Its Pod is also very similar to KubernetesPod.
Linux containers: LXC and LXD
LXC (LinuX Containers) was introduced in 2008 and was the first container with an upstream kernel on LinuX. The first version of Docker used LXC, but LXC was removed in later development because RUNC was already implemented.
The goal of LXC is to run multiple isolated Linux virtual environments on a single control host using a single Linux kernel. To do this, it uses the Cgroups feature without having to start any virtual machines; It also uses namespaces to completely isolate applications from the underlying system.
LXC is designed to create system containers, almost as if you were in a virtual machine — but with minimal hardware overhead because the hardware is virtualized.
LXC does not emulate hardware or software packages and contains only the required applications, so it executes almost bare-metal. Instead, virtual machines contain the entire operating system and then emulate hardware such as hard disks, virtual processors, and network interfaces.
So, THE LXC is small and fast, and the virtual machine is big and slow. Virtual environments, on the other hand, cannot be packaged into off-the-shelf, rapidly deployable machines and are difficult to manage through the GUI administration console. LXC requires a high level of skill from the technician, and the optimized machine may not be compatible with other environments.
LXC VS Docker
LXC is like a supercharged chroot on Linux, producing “small” servers that start faster and require less RAM. However, Docker offers more features:
-
Portable deployment across machines: Objects created with one version of Docker can be transferred and installed on any other Docker-enabled Linux host.
-
Version control: Docker can track versions in a git-like way — you can create new versions of containers, roll them back, and so on.
-
Reuse components: With Docker, you can stack already created packages into new ones. If you want a LAMP environment, you can install its components once and reuse them as pre-made LAMP images.
-
Docker Image Archive: Hundreds of thousands of Docker images can be downloaded from dedicated sites, and new images can be easily uploaded to such an image repository.
LXC is geared toward system administrators, while Docker is more geared toward developers. That’s why Docker is more popular.
LXD
LXD has a privileged daemon that exposes REST apis over local UNIX sockets and over the network (if enabled). You can access it through command-line tools, but it always communicates using REST API calls. It functions the same whether the client is on a local machine or a remote server.
LXD can scale from one local machine to thousands of remote machines. Like Docker, it is mirror-based and is available to all the more popular Linux distributions. Canonical, Ubuntu’s company, is funding the development of LXD, so it will always run on the latest versions of Ubuntu and other linux-like operating systems. LXD integrates seamlessly with OpenNebula and OpenStack standards.
Technically, LXD stands on the shoulders of LXC (both use the same LiBLXC library and Go language to create containers), but LXD aims to improve the user experience.
Will Docker be around forever?
Docker has 11 million developers, 7 million apps and 13 billion image downloads per month. To say that Docker is still the leader would be an understatement. However, as we have seen in this article, there are now many products that can replace one or more parts of the Docker software stack, often without compatibility issues. And compared to the services Docker offers, the other software’s main goal is security.
Original link:
Community.suse.com/posts/beyon…