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

Kubernetes 1.20: dockershim устарел - составляем план перехода на containerd

Kubernetes 1.20 помечает dockershim как deprecated. Разбираем, что это значит для кластеров заказчиков на Docker-рантайме и как тестировать переход на containerd.

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

Kubernetes 1.20 помечает dockershim deprecated - кластеры на Docker-рантайме получают срок жизни, переход на containerd или CRI-O обязателен

С выходом Kubernetes 1.20 в декабре нас встретило предупреждение, к которому сообщество шло давно, но которое всё равно оказалось неприятным сюрпризом: dockershim помечен как deprecated. Пока это только warning в логах kubelet, но направление понятно - рантайм через Docker в Kubernetes доживает последние версии.

У части заказчиков, которых мы ведём в managed-сопровождении, кластеры собраны именно на Docker-рантайме. Это исторически понятное решение: Docker был очевидным выбором, документация kubeadm долгое время показывала Docker как дефолтный пример, и менять работающее никто не торопился. Теперь торопиться придётся.

Почему dockershim вообще существовал

Kubernetes общается с рантаймом через Container Runtime Interface (CRI) - стандартизированный gRPC-интерфейс. containerd и CRI-O реализуют его напрямую. Docker - нет: у него своё API, и именно для того чтобы kubelet умел с ним работать, был написан dockershim - прослойка внутри самого kubelet, которая переводит CRI-вызовы в Docker API.

Проблема в том, что dockershim живёт в исходниках Kubernetes, и его поддержка - головная боль команды upstream. Docker-рантайм добавляет latency (лишний hop через dockerd и containerd, который Docker всё равно использует под капотом), усложняет отладку и тянет за собой ограничения Docker API. containerd в Kubernetes работает без лишнего посредника.

Deprecation в 1.20 - это сигнал: в одной из следующих версий dockershim уберут из kubelet. Когда именно - пока не объявлено, но ждать вечно не стоит.

Что конкретно надо проверить в кластере

Первый шаг - понять, какие кластеры вообще затронуты. Смотрим на рантайм каждой ноды:

kubectl get nodes -o wide

Колонка CONTAINER-RUNTIME покажет docker://20.x.x или containerd://1.x.x. Всё что начинается с docker:// - кандидат на миграцию.

Дополнительно на самой ноде:

crictl info 2>/dev/null || docker info | grep -i runtime

Если crictl не установлен или ругается на отсутствие сокета - почти наверняка Docker-рантайм без CRI-сокета containerd.

У одного из заказчиков обнаружился смешанный кластер: control plane ноды уже на containerd (так получилось при добавлении новых мастеров), worker-ноды на Docker. Формально работает, но это именно тот тип несоответствия, который выстреливает в самый неудобный момент.

Что ломается при миграции - и что нет

Хорошая новость: для подавляющего большинства рабочих нагрузок смена рантайма прозрачна. Pod с nginx, деплойменты, StatefulSet с базой - не должны заметить разницы. Образы OCI-совместимы, и containerd их запускает без вопросов.

Плохая новость: есть несколько классов вещей, которые ломаются:

docker на ноде как инструмент. Если CI/CD, скрипты или операторы заходят на ноду и вызывают docker build, docker ps, docker logs - это перестаёт работать. Не потому что Docker исчезает из системы, а потому что после миграции он больше не управляет контейнерами Kubernetes. docker ps покажет пустой список. Вместо него нужен crictl.

DinD (Docker-in-Docker) и монтирование Docker-сокета. Паттерн с монтированием /var/run/docker.sock в под для сборки образов - классическая схема в Jenkins и GitLab Runner. После перехода на containerd сокета docker там нет, схема ломается. Альтернативы - kaniko, buildah, или переход на отдельный build-кластер.

Прямые вызовы Docker API из приложений. Редкость, но бывает: некоторые операторы или sidecar-контейнеры используют Docker SDK напрямую. Надо проверить.

План тестирования перехода

Перепрыгнуть сразу в продакшн нельзя. Наш подход - последовательная проверка на изолированных нодах.

Сначала поднимаем тестовый кластер с containerd - идентичной конфигурации по железу и версии Kubernetes. На нём прогоняем все критичные деплойменты заказчика: смотрим, всё ли поднялось, корректно ли работают liveness/readiness пробы, нет ли неожиданных OOM. Отдельно проверяем CI/CD - именно здесь обычно сидит docker-сокет или DinD.

Если тест чистый - переходим к постепенной миграции в staging. Схема стандартная для rolling-замены рантайма: cordon ноды, drain, смена рантайма, uncordon, наблюдение. Меняем по одной ноде, не торопимся.

Для containerd конкретные шаги на ноде:

# Останавливаем Docker, включаем containerd
systemctl stop docker
systemctl disable docker

# containerd должен быть установлен, проверяем конфиг
containerd config default > /etc/containerd/config.toml
# В config.toml: SystemdCgroup = true (обязательно если systemd cgroup driver)

systemctl enable containerd
systemctl start containerd

# Обновляем kubelet
# В /etc/default/kubelet или /etc/sysconfig/kubelet:
# KUBELET_EXTRA_ARGS="--container-runtime=remote \
#   --container-runtime-endpoint=unix:///run/containerd/containerd.sock"

systemctl restart kubelet

Отдельный пункт - cgroup driver. Если кластер использует systemd как cgroup driver (рекомендуется с kubeadm), containerd надо настроить аналогично. Несоответствие cgroup driver - одна из самых частых причин того что поды зависают в Pending после миграции.

Где сейчас

Мы прошли полный цикл на одном из dev-кластеров заказчика - containerd работает, никаких регрессий в приложениях не обнаружено. CI/CD пришлось переписать с DinD на kaniko - это заняло отдельный спринт, но результат даже лучше с точки зрения изоляции сборок.

Продакшн-кластеры пока стоят на Docker - migration window запланирован на следующий квартал. Запас времени есть: 1.20 это deprecation, не removal. Но именно сейчас момент проверить все предположения о рантайме в своей инфраструктуре, пока дедлайн не дышит в спину.

Контакт

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

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