Understanding Docker Architecture, CRI, and Containerd: Installation, Configuration, and CLI Usage
This article provides a comprehensive overview of Docker's evolution, its modern architecture involving containerd, runc, and CRI, explains how containers are created and managed, and offers step‑by‑step instructions for installing, configuring, and operating containerd with the ctr command‑line tool on Linux.
Docker
Since Docker 1.11, container execution is no longer performed directly by the Docker daemon; instead, Docker integrates containerd and runc among other components. The Docker daemon (client‑side) communicates with the Docker client and manages images, while containerd handles the lifecycle of containers on each node and exposes a gRPC interface to Docker.
When a container is created, Docker asks containerd to create it. containerd spawns a containerd-shim process, which becomes the parent of the actual container process. This design prevents the entire host from losing containers if containerd crashes. The shim then invokes runc , which implements the OCI (Open Container Initiative) specification to create a standards‑compliant container. After runc finishes, the shim remains to collect the container’s status and clean up child processes.
Docker moved container operations to containerd to decouple higher‑level orchestration (Swarm, later Kubernetes) from low‑level runtime management. When Docker contributed containerd to the CNCF, the architecture shifted to rely on containerd as the core runtime.
CRI (Container Runtime Interface)
Kubernetes introduced the CRI to abstract the container runtime. Early Kubernetes hard‑coded Docker’s API, but later adopted a generic CRI so that any runtime implementing the interface could be used. The dockershim component acted as an adapter between Docker and CRI, allowing Kubernetes to manage Docker containers without native CRI support.
CRI defines two gRPC services: ImageService (pull, list, delete images) and RuntimeService (manage pods and containers, exec, attach, port‑forward). These services are configured via the --container-runtime-endpoint and --image-service-endpoint flags on the kubelet.
With the deprecation of the built‑in dockershim (removed in Kubernetes 1.20‑1.24), users can either use the external cri-dockerd project or switch to a native CRI runtime such as containerd or CRI‑O .
Containerd
Originally part of Docker Engine, containerd has been extracted as an independent, CNCF‑graduated project that provides a stable, industrial‑grade container runtime. Its responsibilities include container lifecycle management, image pull/push, storage handling, invoking runc , and network interface management.
The architecture follows a client‑server model: a daemon exposes a Unix domain socket with a low‑level gRPC API, and clients (e.g., ctr ) use this API to manage containers. Plugins implement functionality such as content storage, snapshotters, and the CRI integration.
Installation
On a Linux Mint 20.2 system, install the libseccomp2 package, then download the pre‑built cri-containerd-cni-1.5.5-linux-amd64.tar.gz archive from the official releases. Extract the archive to the root filesystem, add /usr/local/bin and /usr/local/sbin to $PATH , and source ~/.bashrc :
apt-get update
apt-get install libseccomp2 -y
wget https://github.com/containerd/containerd/releases/download/v1.5.5/cri-containerd-cni-1.5.5-linux-amd64.tar.gz
tar -C / -xzf cri-containerd-cni-1.5.5-linux-amd64.tar.gz
export PATH=$PATH:/usr/local/bin:/usr/local/sbin
source ~/.bashrcGenerate the default configuration file:
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.tomlUse the provided containerd.service unit file (installed under /etc/systemd/system/ ) to run containerd as a systemd service. Important options include Delegate=yes (allowing the daemon to manage its own cgroups) and KillMode=process (ensuring that only the main process is killed during upgrades).
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target
[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=1048576
TasksMax=infinity
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.targetEnable and start the service:
systemctl enable containerd --nowConfiguration Highlights
The generated /etc/containerd/config.toml contains many sections. The most relevant are the plugins block, especially plugins."io.containerd.grpc.v1.cri" , which configures the CRI integration, CNI paths, and registry mirrors. Example registry mirror configuration:
[plugins."io.containerd.grpc.v1.cri".registry]
[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"]Two important storage paths are root = "/var/lib/containerd" (persistent data) and state = "/run/containerd" (runtime state).
Using the ctr CLI
The ctr tool provides low‑level debugging and administrative commands. Common commands include:
ctr image pull docker.io/library/nginx:alpine – pull an image.
ctr image ls – list local images.
ctr image tag SOURCE TARGET – add a new tag.
ctr image rm IMAGE – delete an image.
ctr image export FILE IMAGE / ctr image import FILE – export/import images.
ctr container create IMAGE NAME – create a container (static).
ctr container ls – list containers.
ctr container info NAME – inspect container metadata.
ctr container rm NAME – remove a container.
ctr task start -d NAME – start the container as a task.
ctr task ls – list running tasks.
ctr task exec --exec-id ID -t NAME sh – execute a command inside the container.
ctr task pause NAME / ctr task resume NAME – pause or resume.
ctr task kill NAME – kill the task (no graceful stop).
ctr task metrics NAME – view memory/CPU/PID usage.
ctr ns create TEST / ctr ns ls – manage namespaces (default, test, etc.).
Namespace selection is done with the -n flag (e.g., ctr -n test image ls ). Docker uses the moby namespace, while Kubernetes uses k8s.io .
Summary
By understanding Docker’s component split, the role of CRI, and the capabilities of containerd , operators can replace the heavyweight Docker daemon with a lightweight, CNCF‑native runtime, configure it via a TOML file, and manage containers directly with the ctr CLI.
Cloud Native Technology Community
The Cloud Native Technology Community, part of the CNBPA Cloud Native Technology Practice Alliance, focuses on evangelizing cutting‑edge cloud‑native technologies and practical implementations. It shares in‑depth content, case studies, and event/meetup information on containers, Kubernetes, DevOps, Service Mesh, and other cloud‑native tech, along with updates from the CNBPA alliance.
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.