ADG Оставить заявку
Блог DevOps 4 мин чтения

Kubernetes 1.24: dockershim удалён, миграция на containerd обязательна

Kubernetes 1.24 выходит в мае 2022 и удаляет dockershim окончательно. Кто не мигрировал на containerd или CRI-O - обновление кластера будет заблокировано. Разбираем чеклист и типичные проблемы.

Контекст момента

Kubernetes 1.24 (апрель 2022): dockershim удалён из кодовой базы, переход на containerd или CRI-O обязателен для обновления

Kubernetes 1.24 выходит в мае, и то, о чём предупреждали с версии 1.20, наконец происходит: dockershim убирается из кодовой базы. Не deprecated, не «планируется убрать» - удалён. Узлы, на которых в качестве container runtime настроен Docker через dockershim, после обновления кластера просто не поднимутся.

Мы ведём кластеры в рамках managed-сервиса и уже несколько месяцев отрабатываем миграцию по клиентским окружениям. Примерно треть из них подошла к этому апрелю с Docker на узлах. Вот что происходит на практике.

Почему так вышло

История с dockershim тянется давно. Docker как container runtime работал в Kubernetes через прослойку - dockershim, которую поддерживала сама команда Kubernetes внутри основного репозитория. Docker не реализовывал CRI (Container Runtime Interface) напрямую, поэтому шим переводил CRI-вызовы kubelet в Docker API. В 1.20 это объявили deprecated, в 1.23 - последний релиз с поддержкой. В 1.24 код вырезан.

Само по себе это не катастрофа: containerd существует давно, и ironically - Docker под капотом сам использует containerd для управления контейнерами. Образы совместимы, формат OCI никуда не делся. Но в каждом конкретном кластере надо пройти руками.

Что происходит без миграции

Если попытаться обновить узел с Docker/dockershim до 1.24 - preflight-проверки kubelet обнаружат несовместимый container runtime и откажутся запускаться. Обновление кластера встаёт.

Типичная картина в логах:

validate service connection: CRI v1 runtime API is not implemented for endpoint
"unix:///var/run/dockershim.sock": rpc error: code = Unimplemented

Это не временная ошибка, которую можно переждать. Надо менять runtime перед обновлением.

Чеклист миграции с Docker на containerd

Порядок имеет значение - особенно если узлы в production под нагрузкой.

Первое - проверить текущий runtime на каждом узле. kubectl get nodes -o wide показывает container runtime в колонке CONTAINER-RUNTIME. Если там docker://XX.X.X - узел требует миграции. Если containerd://X.X.X - уже хорошо.

Второе - установить containerd. На большинстве дистрибутивов это containerd.io из Docker-репозитория или containerd из стандартных пакетов. Версия должна быть 1.6.x или новее для нормальной поддержки CRI v1.

Третье - настроить конфиг containerd. По умолчанию после установки /etc/containerd/config.toml может отсутствовать или быть пустым. Нужно сгенерировать: containerd config default > /etc/containerd/config.toml. Затем важный момент - в секции [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] выставить SystemdCgroup = true, если kubelet настроен на systemd как cgroup driver. Несоответствие cgroup driver между containerd и kubelet - одна из самых частых причин, почему узел после миграции не поднимает поды.

Четвёртое - поменять конфиг kubelet. В /var/lib/kubelet/config.yaml или в /etc/sysconfig/kubelet (зависит от дистрибутива) нужно указать новый endpoint:

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

Или в config.yaml:

containerRuntimeEndpoint: unix:///run/containerd/containerd.sock

Пятое - drain узла, рестарт сервисов, uncordon. Стандартная процедура: kubectl drain <node> --ignore-daemonsets --delete-emptydir-data, потом рестарт containerd и kubelet, потом kubectl uncordon. Поды вернутся, образы перекачаются - containerd не читает Docker daemon layer cache.

Шестое - проверить что узел зарегистрировался с правильным runtime. kubectl get node <name> -o wide должен показывать containerd://.

Типичные проблемы

Cgroup driver mismatch. Самая частая. kubelet работает с cgroupDriver: systemd, containerd настроен с SystemdCgroup = false или наоборот. Симптом - поды CrashLoop сразу после запуска, в логах containerd ошибки про cgroup. Лечится правкой конфига containerd и рестартом.

Приватный registry без TLS или с самоподписанным сертификатом. Docker умел работать с insecure-registry через /etc/docker/daemon.json. containerd настраивается иначе - через /etc/containerd/certs.d/<registry>/hosts.toml. Если про это забыть, образы из внутреннего registry перестанут тянуться после миграции.

Образы не кешированы. containerd не видит Docker layer cache. После drain все образы на узле надо перекачать. На узлах с медленным каналом до registry это затягивает возврат в строй. Стоит заранее прикинуть объём и, если нужно, предзагрузить образы через ctr images pull до drain.

Crictl вместо docker. После миграции docker ps на узле перестаёт показывать контейнеры - dockerd там больше не главный. Для отладки нужен crictl - он работает напрямую через CRI. crictl ps, crictl logs, crictl inspect. Операционная команда должна про это знать, иначе первый же инцидент с «а где docker ps» создаст ненужную панику.

Где сейчас

По нашим кластерам большинство узлов уже на containerd - начали переводить заблаговременно, как только 1.24 стало на выходе. Но несколько окружений с нестандартными конфигурациями registry ещё в работе. Обновление самого Kubernetes до 1.24 ставим на паузу до завершения миграции runtime - смысла торопиться нет, и подставляться с заблокированным обновлением не хочется.

Если кластер ещё на Docker - окно для спокойной миграции есть, но оно конечное: следующее обновление Kubernetes без неё просто не пройдёт.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.