Skip to main content

Container Runtime (containerd)

Production Purpose: Every Kubernetes node needs a container runtime. containerd is the CNCF-standard runtime used by all major managed Kubernetes services (EKS, GKE, AKS). Choosing it bare-metal mirrors exactly what cloud providers run under the hood.


Why containerd, Not Docker?

Kubernetes removed Docker as a supported runtime in v1.24 (Dockershim removal). The reason:

LayerDockercontainerd
Developer UXdocker build, docker runNo CLI needed at runtime
Kubernetes interfaceDockershim (deprecated)CRI (Container Runtime Interface)
OverheadExtra daemon layerDirect CRI, lower overhead
CNCF standardNoYes — graduated project

containerd is what Docker itself uses internally. Using it directly removes unnecessary abstraction.


Architecture

  • kubelet — the node agent, talks to containerd via CRI
  • containerd — manages image pull, container lifecycle
  • runc — the actual Linux process isolation (namespaces, cgroups)

Pre-Flight Checklist

Run on all 3 VMs (control-plane + 2 workers).

Verify Hostnames Are Set Correctly

Input:

hostnamectl set-hostname k8s-control   # on VM1
hostnamectl set-hostname k8s-worker1 # on VM2
hostnamectl set-hostname k8s-worker2 # on VM3

No output is expected.

Update /etc/hosts on All Nodes

Input:

cat >> /etc/hosts << 'EOF'
192.168.90.26 k8s-control
192.168.90.27 k8s-worker1
192.168.90.28 k8s-worker2
EOF

Run this on all 3 VMs.

Verify Connectivity Between Nodes

Input:

ping -c 3 k8s-worker1   # run from control-plane

Output:

3 packets transmitted, 3 received, 0% packet loss

This confirms nodes can talk to each other.


Disable Swap

Kubernetes requires swap to be disabled. Swap breaks memory pressure assumptions in the scheduler.

Input:

swapoff -a
sed -i '/swap/d' /etc/fstab

Verify:

free -h | grep Swap

Output:

Swap:           0B          0B          0B

Load Kernel Modules

Input:

cat << 'EOF' > /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

Verify modules are loaded:

lsmod | grep -E "overlay|br_netfilter"

Output:

br_netfilter           32768  0
bridge 425984 1 br_netfilter
overlay 233472 0

Configure Sysctl (Network)

Kubernetes networking requires IP forwarding and bridge netfilter.

Input:

cat << 'EOF' > /etc/sysctl.d/99-kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sysctl --system

Output (last lines):

* Applying /etc/sysctl.d/99-kubernetes.conf ...
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

Install containerd

Input:

apt update && apt install -y containerd

Configure containerd with Default Config

Input:

mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

Enable SystemdCgroup (Critical!)

The kubelet uses systemd cgroup driver by default. containerd must match.

Input:

sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

Verify:

grep "SystemdCgroup" /etc/containerd/config.toml

Output:

SystemdCgroup = true

Start and Enable containerd

Input:

systemctl enable --now containerd

Verify:

systemctl status containerd

Output:

● containerd.service - containerd container runtime
Active: active (running)

Install runc

runc is the OCI-compatible container runtime that containerd delegates to.

Input:

apt install -y runc

Verify:

runc --version

Output:

runc version 1.1.x

Install CNI Plugins

CNI plugins are required for Pod networking. They'll be used later by Calico.

Input:

mkdir -p /opt/cni/bin
apt install -y containernetworking-plugins

Validation

Test containerd directly (no Kubernetes yet)

ctr image pull docker.io/library/nginx:alpine
ctr image ls

Output:

REF                              TYPE   DIGEST   ...
docker.io/library/nginx:alpine ...

Run a container with containerd

ctr run --rm docker.io/library/nginx:alpine test-nginx echo "containerd works"

Output:

containerd works

This confirms containerd is fully operational.


Troubleshooting

SymptomCauseFix
containerd fails to startConfig syntax errorRun containerd config default again
SystemdCgroup = false after restartsed didn't applyEdit /etc/containerd/config.toml manually
ctr command not foundcontainerd not installedapt install -y containerd
Module not loaded on reboot/etc/modules-load.d/ file missingRe-create the file and reboot
Swap still enabled/etc/fstab not updatedRun swapoff -a && sed -i '/swap/d' /etc/fstab

Production Best Practices

PracticeReason
Always use SystemdCgroup = truePrevents OOM kill mismatches between kubelet and runtime
Pin containerd versionAvoid unexpected upgrades breaking nodes
Enable containerd metricsExpose to Prometheus for runtime monitoring
Use private registry mirrorAvoid Docker Hub rate limits in production
Disable swap permanentlyRequired for Kubernetes scheduler correctness