Quick Links

Kubernetes v1.24 and later releases ship without Dockershim after its deprecation in December 2020's v1.20 release. Dockershim's no longer available as a built-in container runtime. You need to use a different supported runtime instead, such as containerd, CRI-O, or Docker Engine with the

        cri-dockerd
    

adapter.

In this article, we'll show how to check whether you're affected, then explain how you can migrate to a different runtime. You should take these steps before you upgrade to Kubernetes v1.24 or a later version so your cluster's workloads aren't impacted.

What Was Dockershim?

Dockershim was developed as a necessary component so Kubernetes could support more container runtimes. At the start of the project, Kubernetes only worked with Docker Engine. This restriction was removed by the introduction of the CRI standard. Any CRI-compatible runtime could now be used with Kubernetes, including containerd and CRI-O, an OCI implementation of the standard.

While CRI brought new flexibility to Kubernetes, it presented an issue for existing clusters. Docker lacked support for the CRI standard so Dockershim was built to let the Kubernetes team layer compatibility on top. Dockershim was a direct integration with Docker Engine that was always intended to be a temporary measure.

The container movement is now much more than Docker, as the original Kubernetes push to CRI demonstrates. Docker itself has split into individual components with its runtime extracted as containerd, a graduate of the Cloud Native Computing Foundation (CNCF).

containerd is fully supported by Kubernetes and more suited to standalone use in cloud environments. Kubernetes doesn't require the Docker CLI and its bevy of features to run your Pods; all it needs is the ability to start and run containers at a relatively low level. Dockershim has been removed because it was difficult to maintain. Its use created fragile code that was tightly coupled to Docker Engine's implementation.

Checking Whether You're Using Dockershim

Recently created clusters on modern platforms are highly unlikely to be using Dockershim. This includes clusters managed by popular cloud providers such as Amazon EKS, Azure AKS, Google GKE, and DigitalOcean DOKS.

You're most likely to need to take action if you maintain your own cluster and first set it up several years ago. You can check whether you're using Dockershim as the runtime for any of your Nodes by running this Kubectl command:

$ kubectl get nodes -o wide

NAME STATUS VERSION CONTAINER-RUNTIME

node-1 Ready v1.22.8 docker://19.3.1

node-2 Ready v1.22.8 containerd://1.4.13

In this example, one of the nodes is using containerd and can be left as-is. The other node is configured using Docker and could be affected by the Dockershim removal. You can check by running this command on the Node:

$ tr \\0 ' ' < /proc/"$(pgrep kubelet)"/cmdline | grep "\-\-container\-runtime"

Your Node is using Dockershim to run containers if no output's displayed. If you do get some output, inspect the displayed --container-runtime-endpoint flag value to determine if Dockershim is active. A runtime endpoint of unix:///run/containerd/containerd.sock signals containerd is used, so no migration is necessary.

Changing A Node's Runtime

Nodes that are using Dockershim need to be updated to use a different runtime. First drain the Node's workloads using Kubectl, so your Pods are rescheduled to other Nodes in your cluster. You should cordon the Node too to stop any new Pods being scheduled.

$ kubectl cordon node-1

$ kubectl drain node-1 --ignore-daemonsets

Next run the following commands on the Node itself. Begin by stopping the Docker daemon and the Node's Kubelet worker process:

$ systemctl stop kubelet

$ systemctl disable docker.service --now

Now you can install your new runtime.

Using containerd

containerd is generally the preferred solution for current clusters. You should be able to migrate to containerd if you're not relying on specific features of Docker Engine. If you are, head to the following section and install cri-dockerd instead.

Add Docker's repository to your system if your package lists don't already include it. containerd is distributed in Docker's repository.

$ sudo apt-get update

$ sudo apt-get install ca-certificates curl gnupg lsb-release

$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

$ echo \

"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \

$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install containerd:

$ sudo apt update

$ sudo apt install containerd

Now update the Node's Kubelet configuration file to use the new runtime. Open /var/lib/kubelet/kubeadm-flags.env. Look for or add the --container-runtime and --container-runtime-endpoint flags with the following values:

  • --container-runtime=remote
  • --container-runtime-endpoint=unix:///run/containerd/containerd.sock

Next change the socket annotation saved against the Node object in the Kubernetes control plane:

$ kubectl edit node node-1

In the file that opens, find the kubeadm.alpha.kubernetes.io/cri-socket annotation and change it to unix:///run/containerd/containerd.sock. Save and close the file to update the Node's object.

Now restart Kubelet:

$ systemctl start kubelet

Allow the Node a few moments to start and connect to the Kubernetes control plane. You should be able to repeat the get nodes command and see that containerd is now being used.

$ kubectl get nodes -o wide

NAME STATUS VERSION CONTAINER-RUNTIME

node-1 Ready v1.22.8 containerd://1.4.13

node-2 Ready v1.22.8 containerd://1.4.13

Finally remove the cordon you placed around the Node so it can begin to receive Pods:

$ kubectl uncordon node-1

Using cri-dockerd

cri-dockerd is a runtime jointly developed by Docker and Mirantis. It's effectively a standalone version of Dockershim that's independently maintained. It allows you to keep using familiar functionality without encumbering the Kubernetes project with Dockershim's maintenance requirements.

Make sure you've already got Docker Engine installed. Then install cri-dockerd by downloading the latest binary from GitHub releases:

$ wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.2.0/cri-dockerd-v0.2.0-linux-amd64.tar.gz

$ tar xvf cri-dockerd-v0.2.0-linux-amd64.tar.gz

$ mv cri-dockerd /usr/local/bin/

Next download, install, and enable cri-dockerd's systemd service configurations:

wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.service

wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.socket

sudo mv cri-docker.socket cri-docker.service /etc/systemd/system/

sudo sed -i -e 's,/usr/bin/cri-dockerd,/usr/local/bin/cri-dockerd,' /etc/systemd/system/cri-docker.service

sudo systemctl daemon-reload

sudo systemctl enable cri-docker.service

sudo systemctl enable --now cri-docker.socket

Now you can modify your Node's Kubelet configuration to use cri-dockerd. This is similar to configuring a Node to use containerd.

Open /var/lib/kubelet/kubeadm-flags.env. Look for or add the --container-runtime and --container-runtime-endpoint flags with the following values:

  • --container-runtime=remote
  • --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock

Next change the Node object's socket annotation:

$ kubectl edit node node-1

In the file that opens, find the kubeadm.alpha.kubernetes.io/cri-socket annotation and change it to unix:///var/run/cri-dockerd.sock. Save and close the file to update the Node's object.

Now restart Kubelet:

$ systemctl start kubelet

Wait a few moments and then use Kubectl to check the Node is up. It will still show the Docker runtime but it's now based on the standalone cri-dockerd, instead of the Dockershim that's integrated with Kubernetes.

$ kubectl get nodes -o wide

NAME STATUS VERSION CONTAINER-RUNTIME

node-1 Ready v1.22.8 docker://19.3.1

node-2 Ready v1.22.8 containerd://1.4.13

You can now remove the cordon you placed around the Node. It will start to accept Pod scheduling requests again.

$ kubectl uncordon node-1

Conclusion

Kubernetes v1.24 removed the Dockershim component that previously integrated CRI compatibility for Docker Engine. While most recent clusters will be unaffected, you should check whether you're using Dockershim before upgrading to the new release.

The runtime to switch to depends on how you currently use your cluster. containerd is usually a good choice if you're not using Docker features. You can use cri-dockerd to bring back Dockershim-like integration if you need to maintain compatibility with existing tooling that's reliant on Docker Engine. This also helps if you're mounting the Docker daemon socket (/var/run/docker.sock) into your containers to power Docker-in-Docker workflows.

Dockershim's removal doesn't impact how you build and use container images. Kubernetes can still run images created with docker build and they're compatible with all supported runtimes. CRI runtimes work with any OCI-format image, as output by Docker and other image builders.