Cloud Native 17 min read

Switching Kubernetes Container Runtime from Docker to containerd and Managing It with crictl

This guide explains how to safely switch a Kubernetes cluster's container runtime from Docker to containerd by putting nodes into maintenance mode, updating configuration files, restarting services, verifying the change, and then using crictl for efficient container and pod management, including command comparisons and log handling.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
Switching Kubernetes Container Runtime from Docker to containerd and Managing It with crictl

After learning the history and basic usage of containerd, this section demonstrates how to replace Docker with containerd as the container runtime in a Kubernetes cluster.

Node Maintenance : Mark the target node (e.g., node1 ) as unschedulable using kubectl cordon node1 , then drain it with kubectl drain node1 --ignore-daemonsets to evict running Pods.

# 将 node1 标记为 unschedulable
➜  ~ kubectl cordon node1
node/node1 cordoned
➜  ~ kubectl get nodes
NAME   STATUS                     ROLES    AGE   VERSION
master Ready                     master   85d   v1.19.11
node1  Ready,SchedulingDisabled
85d   v1.19.11
node2  Ready
85d   v1.19.11

Switching to containerd : Stop kubelet , docker , and containerd , then generate the default containerd config with containerd config default > /etc/containerd/config.toml . Modify the pause image and registry mirrors in the [plugins."io.containerd.grpc.v1.cri"] section.

# Generate default config
➜  ~ containerd config default > /etc/containerd/config.toml
# Set custom pause image
[plugins."io.containerd.grpc.v1.cri"]
  sandbox_image = "registry.aliyuncs.com/k8sxio/pause:3.2"
# Registry mirrors
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
    endpoint = ["https://bqr1dr1n.mirror.aliyuncs.com"]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
    endpoint = ["https://registry.aliyuncs.com/k8sxio"]

Update /etc/sysconfig/kubelet to use containerd as the remote runtime:

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

Reload systemd and restart both containerd and kubelet , then verify the node runtime with kubectl get nodes -o wide , confirming containerd://1.4.4 is active on node1 .

# Restart services
➜  ~ systemctl daemon-reload
➜  ~ systemctl restart containerd
➜  ~ systemctl restart kubelet
# Verify
➜  ~ kubectl get nodes -o wide
NAME   STATUS                     ROLES    AGE   VERSION    INTERNAL-IP   EXTERNAL-IP   OS-IMAGE               KERNEL-VERSION                CONTAINER-RUNTIME
master Ready                     master   85d   v1.19.11   192.168.31.30
CentOS Linux 7 (Core) 3.10.0-1160.25.1.el7.x86_64 docker://19.3.9
node1  Ready,SchedulingDisabled
85d   v1.19.11   192.168.31.95
CentOS Linux 7 (Core) 3.10.0-1160.25.1.el7.x86_64 containerd://1.4.4

After confirming, uncordon the node with kubectl uncordon node1 and repeat the process for other nodes.

➜  ~ kubectl uncordon node1
node/node1 uncordoned

Using crictl : Install crictl from the cri-tools releases, configure /etc/crictl.yaml to point to the containerd socket, and use it for pod, container, and image management.

# Install crictl
VERSION="v1.22.0"
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz
tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C /usr/local/bin
rm -f crictl-$VERSION-linux-amd64.tar.gz
crictl version
# Config file
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint:   unix:///var/run/containerd/containerd.sock
debug: false
pull-image-on-create: false
disable-pull-on-run: false

Common crictl commands include crictl pods , crictl ps , crictl images , crictl exec , crictl logs , and crictl stats . The guide also provides a comparison table of Docker, containerd (via crictl), and ctr commands.

Log Configuration Differences : With Docker, logs are stored under /var/lib/docker/containers and linked via /var/log/pods and /var/log/containers . With containerd, kubelet writes logs directly to /var/log/pods and creates symlinks in /var/log/containers . Log rotation is configured via Docker's log-driver options or kubelet's --container-log-max-files and --container-log-max-size flags.

# Docker log config (json-file)
{"log-driver":"json-file","log-opts":{"max-size":"100m","max-file":10}}
# containerd/kubelet log config
--container-log-max-files=10 --container-log-max-size="100Mi"

Finally, the article notes that after switching to containerd, the Docker socket is unavailable, so alternative image build methods that do not rely on docker.sock must be used.

Cloud NativeKubernetescontainerdcrictlruntime-switch
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.