How to Migrate Kubernetes from Docker to cri‑o: Step‑by‑Step Guide
This guide explains why Kubernetes dropped dockershim, clarifies OCI and CRI standards, compares Docker, containerd and cri‑o runtimes, and provides a step‑by‑step procedure—including node draining, Docker removal, kernel tuning, cri‑o installation, kubelet reconfiguration, and verification—to successfully migrate a cluster from Docker to cri‑o before upgrading to version 1.24.
1. Introduction
Kubernetes is a portable, extensible open‑source platform for managing containerised workloads and services, supporting declarative configuration and automation. Its ecosystem is large and rapidly growing.
2. Technical Truth
Kubernetes only deprecated the dockershim module, not Docker itself. The containerd component of Docker complies with the CRI standard and can continue to be used as a container runtime. Docker images follow the OCI standard and can run in Kubernetes clusters.
2.1 What are OCI and CRI?
OCI (Open Container Initiative) defines open standards and specifications for container lifecycle management. Implementations such as runC are low‑level runtimes.
CRI (Container Runtime Interface) defines the plugin interface between kubelet and container runtimes, enabling decoupling.
2.2 Which runtimes does Kubernetes support?
Kubernetes supports any runtime that implements the CRI standard. Before version 1.23 the common runtimes were Docker, containerd, and cri‑o.
Docker – uses the dockershim module (removed in 1.24) to translate CRI calls to the Docker daemon.
containerd – a standalone runtime that uses runC to create containers.
cri‑o – a lightweight CRI‑only implementation that calls runC directly.
Kubelet calls dockershim via CRI.
dockershim forwards the request to the Docker daemon.
Docker invokes containerd.
containerd creates a containerd‑shim process, which then calls runC to complete container creation.
3. Migration Considerations and Detailed Steps
❖Notes:
Pods using Docker‑in‑Docker with a host‑mounted
docker.sockwill fail after migration unless they run an independent Docker daemon.
Synchronise
/etc/docker/daemon.jsonsettings to the new runtime (e.g., image registry).
Update any operational scripts that contain Docker commands.
Adjust stdout/stderr log collection (Fluentd or Filebeat) configurations.
Modify log directory and format settings: Docker typically uses JSON format, while other runtimes default to text. Update
containerLogMaxSizeand
containerLogMaxFilesas needed.
Refer to the official FAQ: https://kubernetes.io/zh/blog/2022/02/17/dockershim-faq/
Step 1: Drain the node
<code>kubectl drain --delete-emptydir-data --force --ignore-daemonsets <NODE_NAME></code>Step 2: Uninstall Docker
<code>systemctl stop kubelet
systemctl stop docker
systemctl disable docker
yum remove -y docker-ce
# Keep Docker data temporarily if needed
rm -rf /var/lib/docker</code>Step 3: Kernel settings
<code>cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl --system
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter</code>Step 4: Install cri‑o
<code>export OS=CentOS_7
export VERSION=1.22
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/devel:kubic:libcontainers:stable.repo
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo
yum install -y cri-o</code>Step 5: Configure cri‑o
<code># Find conmon path
which conmon
# Edit /etc/crio/crio.conf
[crio.runtime]
conmon = "/usr/bin/conmon"
[crio.image]
insecure_registries = ["xxx"]
pause_image = "xxx/k8s/pause:3.6"
# Edit /etc/containers/registries.conf
[[registry]]
prefix = "xxx"
insecure = true
blocked = false
location = "xxx"</code>Step 6: Start cri‑o
<code>systemctl enable crio
systemctl start crio
systemctl status crio</code>Step 7: Adjust kubelet
<code># /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint='unix:///var/run/crio/crio.sock'</code>Remove obsolete parameters (
--cgroup-driver,
--cni-plugin,
--pod-infra-container-image) from
/var/lib/kubelet/kubeadm-flags.env. Set the following in
/var/lib/kubelet/config.yamland sync to the corresponding ConfigMap:
<code>cgroupDriver: systemd
runtimeRequestTimeout: 5m
containerLogMaxSize: 100Mi
containerLogMaxFiles: 3</code>Step 8: Restart kubelet and verify
<code>systemctl start kubelet
systemctl status kubelet</code>Step 9: Update kubeadm CRI socket
<code># Check current CRI socket
kubectl get node <NODE_NAME> -o jsonpath='{.metadata.annotations.kubeadm\.alpha\.kubernetes\.io/cri-socket}'
# Annotate node to use cri‑o
kubectl annotate node <NODE_NAME> --overwrite kubeadm.alpha.kubernetes.io/cri-socket=/var/run/crio/crio.sock</code>Step 10: Install podman (optional)
<code>yum install -y podman
podman info</code>Step 11: Reboot the host
Reboot to clear any leftover iptables rules after Docker removal.
Step 12: Re‑join the node to the cluster
<code>kubectl uncordon <NODE_NAME></code>After completing these steps the node runs with cri‑o, and the cluster can be upgraded to Kubernetes 1.24.
4. Postscript
Although dockershim has been removed, the Docker‑Kubernetes workflow has matured over years and remains widely used. It is still suitable for development and testing environments. Production environments should remain stable and consider migration only when the timing is appropriate.
Efficient Ops
This public account is maintained by Xiaotianguo and friends, regularly publishing widely-read original technical articles. We focus on operations transformation and accompany you throughout your operations career, growing together happily.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.