Слово «containerd» чаще всего всплывает в двух ситуациях: в выводе docker info и в ошибках Kubernetes, когда pod висит в ContainerCreating. Между тем это самостоятельный рантайм: он тянет образы, готовит снапшоты файловой системы, запускает процессы через runc и следит за ними. Docker использует его внутри, Kubernetes общается с ним по CRI. Понимание containerd полезно и для отладки кластера, и для сервера, где Docker не нужен вовсе.
Место в стеке
Цепочка выглядит так: docker или kubelet → containerd → containerd-shim-runc-v2 → runc → процесс. containerd не создаёт контейнеры сам: он разбирает OCI-образ, распаковывает слои в snapshotter, формирует OCI-спецификацию и вызывает runc, который делает clone, pivot_root и execve. Дальше контейнер живёт под присмотром shim-процесса, а containerd может перезапуститься, не убив нагрузки.
Из этого следуют практические выводы. containerd не умеет собирать образы — для сборки нужен BuildKit или nerdctl. У него нет привычных -p и -v в низкоуровневом клиенте ctr. Зато есть CRI, снапшоты, пространства имён и понятная конфигурация в одном файле.
Установка и первые проверки
В Debian и Ubuntu пакет containerd есть в репозитории, но версия там отстаёт. Для сервера берите пакет из репозитория Docker или официальный архив с релизами. После установки проверьте, что служба поднялась и сокет на месте.
sudo apt install containerd
sudo systemctl enable --now containerd
containerd --version
sudo ctr version
ctr version показывает две строки: клиент и сервер. Если сервер не отвечает, смотрите journalctl -u containerd -n 50 — чаще всего это синтаксическая ошибка в /etc/containerd/config.toml или занятый сокет.
Конфигурация
Файл настроек генерируется командой containerd config default. В containerd 2.x используется третья версия схемы, в 1.x — вторая; перед обновлением мажорной ветки сверьте свой файл с выводом containerd config default и перенесите изменения вручную. После правок проверяйте результат через containerd config dump: он показывает уже собранную конфигурацию, а не только то, что вы написали.
sudo containerd config default | sudo tee /etc/containerd/config.toml
sudo containerd config dump | grep -E 'version|root|state'
sudo systemctl restart containerd
Ключевые параметры: root — каталог с образами и снапшотами, state — сокеты и временные файлы, snapshotter — обычно overlayfs. Для Kubernetes обязательно включите SystemdCgroup = true в опциях рантайма runc: без этого kubelet и systemd по-разному считают cgroup, и поды ведут себя нестабильно под нагрузкой.
Пространства имён и ctr
containerd разделяет объекты по namespace. Docker работает в moby, Kubernetes — в k8s.io, а ctr по умолчанию смотрит в default. Поэтому «пустой» список контейнеров на ноде Kubernetes — нормальная ситуация, если забыть про -n.
sudo ctr -n k8s.io containers ls
sudo ctr -n k8s.io tasks ls
sudo ctr images pull docker.io/library/alpine:latest
sudo ctr run --rm docker.io/library/alpine:latest demo /bin/sh
ctr run запускает процесс без сети и проброса портов, зато без лишних слоёв. Для повседневной работы удобнее nerdctl — он даёт синтаксис, похожий на Docker, и умеет run -p, compose и сборку через BuildKit. ctr оставьте для диагностики: он ближе к API и показывает то, что скрывают обёртки.
CRI и crictl
Kubelet не использует ctr: он ходит в CRI-плагин по тому же сокету. Для отладки есть crictl, который понимает поды, sandbox-контейнеры и логи. Настройте /etc/crictl.yaml, чтобы не передавать endpoint каждый раз.
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
sudo crictl ps -a
sudo crictl logs --tail=50 CONTAINER_ID
sudo crictl inspect CONTAINER_ID | jq '.info.runtimeSpec.process.args'
sudo crictl stats
Если pod застрял в ContainerCreating, смотрите события через kubectl describe pod, а затем проверяйте containerd: crictl images покажет, скачался ли образ, crictl pods — создан ли sandbox. Частая причина — недоступный реестр или несовпадение архитектуры образа.
Отдельная история — sandbox-образ. Каждому поду нужен контейнер-«песочница» на базе pause: он держит network namespace, а контейнеры пода присоединяются к нему. Если этот образ не скачивается, pod навсегда остаётся в ContainerCreating, хотя приложение ни при чём. Проверьте crictl images | grep pause и доступность реестра, указанного для sandbox в конфигурации CRI: во внутренней сети его часто забывают отзеркалить.
Реестры и зеркала
Настройки реестров в CRI задаются через каталог certs.d. Для каждого хоста создаётся подкаталог с файлом hosts.toml, где перечислены зеркала и параметры TLS. Это удобнее, чем править глобальные переменные окружения, и работает одинаково для Docker Hub и внутренних реестров.
mkdir -p /etc/containerd/certs.d/docker.io
cat >/etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://docker.io"
[host."https://mirror.example.com"]
capabilities = ["pull", "resolve"]
EOF
sudo systemctl restart containerd
Отладка и обслуживание
ctr plugin ls— какие плагины загружены; выключенный CRI объясняет, почему kubelet не видит рантайм.ctr -n k8s.io snapshots ls— снапшоты; их количество растёт, если образы давно не чистили.crictl rmi --prune— удаляет неиспользуемые образы, не трогая те, на которые ссылаются контейнеры.journalctl -u containerd -f— живой лог; ошибки видаfailed to pullобычно содержат точный код ответа реестра.
sudo ctr plugin ls | grep -v ok
sudo ctr -n k8s.io snapshots ls | wc -l
sudo crictl rmi --prune
sudo ss -lntp | grep containerd
Перезапуск systemctl restart containerd не убивает работающие контейнеры: shim-процессы остаются, а новый демон подхватывает их при старте. Но если вы удалили снапшот или слой, на котором держится файловая система контейнера, восстановить её не получится — сначала останавливайте нагрузку, потом чистите.
Итог
containerd — это средний слой, который стоит знать отдельно от Docker и Kubernetes. Он отвечает за образы, снапшоты и запуск процессов, а его конфигурация умещается в один файл. Для диагностики держите под рукой ctr -n k8s.io, crictl и journalctl -u containerd: этого набора хватает, чтобы отличить проблему рантайма от проблемы сети или реестра.