How Docker and Kubernetes fit together… for now.
Tags: Containers, Kubernetes, Cloud Computing, Docker, Container Orchestration

This blog post contains my own opinions, and don’t reflect those of my employer.

TL;DR

The decision of the Kubernetes community to deprecate Docker as a container runtime isn’t conclusive proof that 2020 has been one of the worst years since records began. This is the knock on effect of the deprecation of a bridging component for Docker to interact with Kubernetes’ Container Runtime Interface, without which Docker runtime will not be supported. This will represent differing degrees of work for teams, as it will require uptake of other runtimes such as cri-o or containerd. Images built by Docker will still be able to be pulled and run by other runtimes.

The dreaded change log v1.20

As far as inflammatory change log messages in Kubernetes go, “Docker support is now deprecated” is right up there with the best. The change log for v1.20 revealed that in future releases the Kubelet would no longer support Docker as a container runtime. For a brief period, you would have been forgiven for thinking the sky is falling — until Sys Admins and DevOps engineers globally took a breath. Whilst this will break clusters, the ramifications of the announcement were not as horrifying as first thought. This announcement is as good an opportunity as any to explain what actually makes up Docker, how it interacts with Kubernetes; and what the implications of this announcement are.

Popping the hood of Docker

The Docker Engine is built up of 3 main building blocks: server or Docker daemon, REST API and Docker CLI. The Docker CLI presents a command line interface for users to send commands via the REST API to the underlying Docker Daemon. The Docker daemon is a long running service that interfaces with different Docker facets like storage, networking, containers and images. Users of Docker can choose to use the CLI or directly interact with the REST API to pass instructions on what to do with the Docker objects; but all roads lead back to the daemon.

Basic Docker anatomy

When the Docker daemon goes to action commands like run a container or pull an image, it farms them out down the chain to two layered runtimes. In Docker, the higher level of these two utilities is a ‘Docker wrapped’ version of containerd. Containerd is responsible for image pulling and management. When it comes time to run a container, containerd will pass the image (as an unpacked bundle) to runc, operating as the lower level runtime. Runc will implement some of the operating system low level features, such as configuring the namespaces and the actual running of the container within this segregated context.

The Kubernetes Worker node

The Kubelet is an agent that sits on each worker node, and ensures that containers are running within the pods. In 2016, the Kubernetes community implemented a component called the Container Runtime Interface (CRI). The idea of the interface was to make Kubernetes accessible to as many runtimes as possible. The CRI created a layer of abstraction from the Kubelet, based upon which runtimes could integrate in a standardised way. Runtimes such as containerd (a CRI plugin is baked in) and cri-o are CRI compatible, but critically the Docker wrapped containerd runtime is not.

Kubernetes worker node flows

As Docker runtime didn’t natively integrate with the CRI, a component known as Dockershim was developed to act as a bridge between the two entities. Docker is fundamentally different to containerd and cri-o, in that its service offering extends vastly beyond just a container runtime. Dockershim enables Kubernetes to bypass the less important parts of Docker, and access the runtime components (containerd and runc) that it needs in order to manage workloads.

What does it all mean?

So, to dissect this announcement. The Kubernetes maintenance community has decided to deprecate Dockershim, and by association, Docker runtime. The Docker runtime is not compliant with CRI, and maintenance of Dockershim has created an on-going burden for the community. The continued support of Dockershim also goes against the key benefit of implementing CRI in the first place, namely as a common interface.

Kubernetes isn’t throwing the baby out with the bathwater; images built in Docker will still be able to be pulled and run in Kubernetes clusters. The way images are created by Docker isn’t proprietary or unique to Docker; they conform to the Open Container Initiative (OCI). Because of this, different runtimes will have no problems pulling and running images that have been built by a normal Docker process. There is still a place for Docker, as high level runtimes like vanilla containerd do not have the functionality by which to build docker images.

For teams utilising Docker runtimes within their Kubernetes clusters, in the short term there will be a deprecation message to click through; because people always read those, right? By late 2021, a transition to a CRI compliant runtime will be required. Given the potential blast radius of this change, it has been slated to be rolled out late 2021 in v1.23; affording teams plenty of time to prepare for it’s arrival. As Docker uses containerd under the hood, it would seem to be the path of least resistance. That said, minor differences between the two options present the opportunity for teams to investigate whether there are other runtimes out in the wild that may better service their needs.

In Closing

Whilst this change will break clusters in 2021, the heart stopping impact of the initial announcement for most of Twitter has somewhat lessened as people looked into the details. The removal of the Dockershim will hopefully free up the limited time of the Kubernetes maintenance community, and give them more of an opportunity to introduce new features that the broader ecosystem is asking for.

I hope that there were some pieces of information that people can take away from this post, and as always, feedback is welcomed.

~ Ben