Kubernetes has announced in its latest Changelog that it will not use Docker as a container runtime since Kubernetes 1.20. So what’s going on here? How will developers and businesses be affected?
In recent years, Kubernetes has become a widely used container choreography solution in its own computer room and cloud. The most widely used way is Kubernetes+Docker. From the perspective of DevOps personnel, kubctl command and K8S API are used to operate the cluster on one side, and Docker command is used to manage and run the mirror on the other side.
Docker alone is also used in some company scenarios. One scenario is “separate only”, where one machine uses Docker for resource isolation but does not need to “orchestrate” multiple containers. Using Kubernetes alone, the lower layer is not Docker, not a lot.
To put it simply, the relationship between Kubernetes and Docker is complementary as well as competitive. In general cognition, Kubernetes and Docker are complementary:
-
Dockers belong to the lower level — container engines;
-
Kubernetes belongs to the upper layer, the scheduling layer.
Docker originates from Linux Container, which can divide the resources of a machine into N containers to achieve resource isolation and define the runnable program as the standard Docker image. Kubernetes can arrange and schedule each container of different machines to form a distributed system.
Kubernetes and Docker are not completely “distinct” complementary relations. There are overlaps between them, which can also be said to be competition, mainly in the following points:
-
The three major migration resources of the system are computing, storage and network. From the perspective of Kubernetes, Docker belongs to “Runtime”, that is, computing resources. But Docker technology system itself also includes storage layer, network layer. The overlap of responsibilities between the upper and lower levels can also be seen as competition.
-
Docker originally had a native scheduling engine — Swarm. A few years ago, Kubernetes, Mesos and Swarm coexisted in the scheduling field. Kubernetes won out, but Docker still has the “will to continue to make progress”.
Kubernetes is controversial and uncertain about how to use Docker. Kubernetes 1.20 ChangeLog to scrap the so-called Docker rumors, there is no smoke without fire. In other words, even if Kubernetes uses Docker all the time, it doesn’t use all of Docker, it’s more or less different.
In addition, the word “discard Docker” itself has multiple meanings, Docker is not a single-layer software, Kubernetes 1.20 enabled Dockershim does not mean the abandonment of all Docker, there is still containerd can connect to Docker.
Kubernetes has CRI and OCI container standards
At present, the Container Runtime Interface (CRI) and Oracle Call Interface (OCI) are the “nesting dolls”. Kubernetes’ kubelet calls CRI, and the implementer of OCI then calls OCI.
The following figure also illustrates the relationship between CRI and OCI:
From Kubernetes’ perspective, CRI is an interface at the same level as CNI (network) and CSI (storage).
OCI is a bottom-up standard that abstracts interfaces from implementations and is led by the Linux Foundation. Docker implementation of the core RunC, that is, the typical implementation of OCI, standard implementation.
CRI is a top-down standard derived from Kubernetes’ migration layer (runtime) requirements.
The container engine layer defines OCI from the bottom up and the container choreographer layer defines CRI from the top down, which gives them a nesting situation.
Among the three implementations of Kubernetes dockershim, Cri-Containerd, and Cri-O, The cri-O favored by RedHat has been relatively mainstream. Although it is still a “doll”, it has been relatively simplified.
The following is a panoramic view of the location of cri-O from the Kubernetes cluster operation:
Docker originated from Linux Container
As a Container engine, Docker’s implementation is based on Linux Container — the kernel-to-user-space mechanism.
Linux Container can be divided into two parts: the cGroup in the kernel and the LXC in user space. When Docker was first implemented, it was also completely based on Linux Container, based on LXC to do the higher level things.
This graph, which many consider “factually incorrect,” actually represents the past:
In the development process of Docker, LXC language was finally enabled, and libcontainer written in GO language was replaced.
The diagram below is not new either, but it is a better representation of the typical Docker architecture that follows, without LXC.
However, the root of the Docker implementation is Linux Container. Even without LXC, the kernel cgroup is still used, and the pattern is similar.
How does Kubernetes end up bridging containers
From a purely technical point of view, it is better to discuss the relationship between Kubernetes and the final container implementation layer than to discuss the relationship between Kubernetes and Docker. Because the noun Docker has different connotations and denotation at different times.
Here’s a schematic of Docker:
From the perspective of software modules, docker Engine, Cri-Containd, Containd-Shim and runC in the figure all belong to the software of docker system.
The purple, orange, and red colors in the figure below represent the typical approach of The Dockershim, Cri-Containerd, and Cri-O CRIS. The process is gradually shortening, which is an evolution of CRI implementation.
If it is Kubelet’s Dockershim mode (purple), the flow looks like this:
-
Kubelet calls Dockershim from CRI’s gRPC, both in the same process
-
Dockershim calls the Docker daemon
-
The docker daemon calls containerd; Containerd calls containerd-shim (sometimes called the Docker-containerd-shim daemon) to create containers
-
Containerd-shim access OCI implementation runC (command line executable)
For Kubelet’s Cri-Containerd mode (orange), the process looks like this:
-
Kubelet calls cri-containerd from the CRI gRPC;
-
Cli-containerd calls containerd; Containerd calls containerd-shim.
-
Containerd-shim calls RucnC (same as above);
In many people’s mind, if you don’t use the Docker daemon, you are “abandoning Docker”, which is basically the change from Dockershim to Containerd. Containerd, on the other hand, is a daemon that the Docker organization does.
If it is Kubelet’s Cri-O mode (red), it is more concise:
-
Kubelet calls CRI-O from CRI’s gRPC;
-
Cri-o calls the implementation runC of OCI
If kubelet calls CRI as the starting point and OCI calls runC as the ending point, the three modes experience the following executables:
-
Dockershim: dockershim(*)->dockd->containerd->containerd-shim
-
Cli-containerd mode: Cli-containerd (*)-> Containerd ->containerd-shim
-
Cli-o mode: Cli-O
Dockershim mode has three executable programs, Dockershim and Kubelet generally the same process; The Cli-Containerd mode has two or three executable programs. The Cli-containerd can run the same process as containerd. Cli-o mode has only one executable program.
Red Hat’s favored Cli-O mode eliminates the need for Containerd, leaving only the runC command-line application. RunC is also written in go and contains calls to libcontainer.
When Docker shrinks to this point, in fact, there are only cgroup and namespace functions in the Linux kernel.
To sum up, due to the inertia of the implementation of the old technology, the classic Kubernetes+ Docker scheme widely used in the production environment is still running, and the operation and maintenance are mature, and will not be upgraded soon. For developers and enterprises, the use frequency and variable of K8S API are far greater than that of Docker API. As for the bridge between Kubernetes and Docker, there is no need to care. Therefore, even if “ditching Docker completely”, the impact on developers and enterprises will be very limited.