TL;DR: as of Kubernetes 1.20, support of the Docker container engine is deprecated, but users will still be able to use Docker container images and registries, as well as create containers that look identical at runtime.
When Red Hat launched OpenShift 4.X and RHEL 8.X around two years ago, we started down this exact same journey. We moved from Docker which needed extra code to CRI-O which interfaces natively with the Kubelet in OCP 4 and moved from Docker to Podman in RHEL 8. At the time, we published a blog called Why Red Hat is investing in CRI-O and Podman. This was only a small hiccup for most customers because the same images, registries, and running containers were still supported. That still might not make perfect sense, so let’s explain it further in the context of five different areas of Docker support.
#1 The Container Image Format
Stack ranked, the container image is probably the most genius piece of Docker. It was elegant, simple and leverages the Tape Archive (TAR) file format. Basically, the container image made it simple to glue a bunch of TAR files together in layers, push them to a file server and share them with the world. This was world altering because it allowed us to collaborate in such an elegant and simple way.
The format of the way these images were glued together was called the Docker image format. This Docker image format evolved into an open standard and was later donated to the Open Containers Initiative (OCI). This new format is called the OCI image specification and is controlled by a neutral, openly governed standards body which has participation from many different vendors.
While both of these formats still exist today and are widely used, they are 99.9% identical. Moreover almost every major container engine (Docker, Podman, CRI-O, containerd, etc) and registry on the planet supports both formats. In fact, the format is being adopted to deliver software (Flatpaks, OS images for Fedora/RHEL CoreOS, etc) and even virtual machines (KubeVirt). OCI images have pretty much become the de facto way to deliver software in a DevOps or cloud native world. The innovation in the container image format continues to develop, but the OCI and Docker format remain tightly coupled, never straying too far from each other.
Even though Kubernetes is moving away from Docker, it will always support the OCI and Docker image formats. Kubernetes doesn’t pull and run images itself, instead the Kubelet relies on container engines like CRI-O and containerd to pull and run the images. These are the two main container engines used with CRI-O and they both support the Docker and OCI image formats, so no worries on this one.
#2 The Registry Server
The registry server is essentially a specialized file server based on HTTPD, instead of NFS or WebDav. The registry server was invented side by side with image format to all users to push and pull container images. This is governed by the OCI distribution specification, and again every major registry server and container engine supports this same format.
Since the use cases for container images is expanding to things like virtual machines, Helm Charts, Flatpaks, and even container images which deliver source code, it only makes sense that container registries are becoming a critical piece of infrastructure which are leveraged in production for not only Kubernetes but tons of other use cases and workloads.
Again, as with the container image format, Kubernetes doesn’t directly communicate with the registry server, it relies on CRI compliant container engines like CRI-O and Containerd, so the registry server format is still supported. No problems here.
#3 The Runtime Format
As a Kubernetes user, you may be asking yourself, “if Kubernetes removes Docker support and I have to move to CRI-O or Containerd, how will I know that my containers run the same way and don’t break my workloads?” This is a reasonable question to ask, but let’s explain how it’s not even a tiny bit of a worry because every CRI compliant container engine runs containers that look identical in Linux (or Windows).
The OCI runtime specification governs how containers are expressed at runtime, and almost every container engine on the planet uses runc which is not only the reference implementation, but also the gold standard for communicating with the Linux kernel to start containers.
The following is a simple architectural diagram of Kubernetes using Docker:
Now, take a look at an architectural drawing with CRI-O supporting CRI-O natively without the Dockershim:
Notice how much looks the same? In fact, it’s simplified because there’s less moving parts. No REST API translation and no Dockershim. Better, not worse, but the containers on Linux are identical.
#4 Command Line Interface (CLI)
This is the interface to Docker that introduced so many people to brillina, simple containers. This includes things like “docker run” or “docker images” which most people should be familiar with. It’s not widely understood, but this Docker CLI is not governed by any open standards. That said, the Docker CLI is fairly stable and many people use it to troubleshoot what’s going on in a Kubernetes cluster.
This is where things do change a bit with the removal of the Dockershim and therefore the Docker Engine. In a Kubernetes cluster which uses a CRI compliant container engine like CRI-O or containerd, the Docker command is replaced with the the cri-ctl command. If you’e never heard of this command, check out this blog: What Is CRICTL And Why Should You Care? This little command looks a lot like Docker and gives an administrator a fairly familiar interface into what’s going on in CRI-O or Containerd. An admin can run something like “cri-ctl images” or “cri-ctrl ps” - check out the man page. One thing you’ll notice is that there’s no “cri-ctl run” like Docker. That’s because Kubernetes creates and starts containers in a slightly different way, but if you think about it, you should be relying on the Kubelet to start containers. An admin should only ever need to poke around if something goes wrong.
So, all in all, things do change with the CLI, but it’s manageable.
Application Programming Interface (API):
This is where the biggest change is between using Docker or using CRI-O or Containerd. The API absolutely changes. It goes from a REST to gRPC and as was hinted at, the function calls are similar but not identical. I often refer to the CLI as the interface for humans, and the API as the interface for robots like the Kublet.
Typically, end users won’t notice a change to the API because it’s buried in the bowels of the Kubernetes worker node, unless…
- They have operational scripts that interact directly with the Docker API to gather metrics/data or kickstart things when they aren’t working, etc.
- Third party vendor tooling like security tooling which needs to gather low level data about the running containers
- Upstream tooling which gives extra control or access to running containers. Docker has a huge ecosystem, and often administrators will augment their capabilities by talking directly to the Docker API with these tools
Since the Dockershim code being removed, this will create a gap with tooling talking directly to the Docker API. The Dockershim translates CRI requests to Docker REST based requests, allowing the Kubelet to communicate with Docker. Without that shim, no Docker support.
Luckily, since OpenShift moved to CRI-O as the default about a year and a half ago at the time of this writing, many third party vendors and projects have already begun to look at, or move to interfacing directly with the CRI interface instead of with the Docker REST API. The OpenShift move started these conversation, and I think the Kubernetes deprecation will cause even more software to integrate with the CRI interface.
If you are really in a bind and have tooling that you absolutely cannot live with, it sounds like you may be able to run Dockershim as a standalone daemon. Long story short, when it comes to direct API integration, mileage may vary. This is the biggest risk for Kubernetes users with complex integrations.
What Does This All Mean?
Breaking down support into these five key areas gives us a framework for talking about this change in the context of the Dockershim deprecation announcement with Kubernetes 1.20.
Essentially, nothing changes with container images, registry servers, and runtimes. Container engines which are CRI compliant, like containerd and CRI-O, adhere to these standards. They even run containers using runc which means the running containers look identical between Docker, containerd, CRI-O and even Podman. So, from a basic functionality perspective, nothing changes.
For more information, check out the Dockershim FAQ on the Kubernetes site.