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

containerd 1.2 и CRI: перевели кластер с docker shim - стало тише и быстрее

Перевели один кластер с dockershim на containerd напрямую через CRI-плагин: потребление памяти узлов снизилось, pull образов ускорился за счёт параллельного скачивания слоёв.

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

containerd 1.2 стабилизирует встроенный CRI-плагин, позволяя Kubernetes работать напрямую без dockerd

До недавнего времени схема была такая: kubelet общается с dockershim, dockershim общается с dockerd, dockerd общается с containerd, containerd общается с runc. Четыре звена на пути от «запусти контейнер» до реального процесса. Когда в containerd 1.2 CRI-плагин объявили стабильным, мы решили проверить: а что если убрать два лишних звена?

Взяли один из managed-кластеров - не самый нагруженный, но и не игрушечный, несколько десятков нод, несколько сотен подов - и перевели его с dockershim на containerd напрямую.

Зачем вообще это трогать

До 1.1 containerd CRI поддерживался отдельным бинарником (cri-containerd). В 1.1 плагин встроили прямо в демон, в 1.2 объявили стабильным - один процесс, один сокет, kubelet ходит туда напрямую. Kubernetes умеет работать с любым CRI-совместимым runtime уже давно, но большинство кластеров по инерции продолжает жить с dockerd, потому что «работает - не трогай».

Нас интересовали два конкретных вопроса. Первый - память: dockerd сам по себе процесс не маленький, и на каждой ноде он постоянно висит со своим overhead-ом. Второй - скорость pull: у containerd уже была репутация runtime, который умеет скачивать слои образа параллельно, в отличие от последовательной логики в Docker.

Как переводили

Простого sed -i для перехода недостаточно. Алгоритм примерно такой:

  • Cordon + drain ноды. Всё стандартно: сначала убираем ноду из ротации, выселяем поды.
  • Останавливаем dockerd, настраиваем containerd. Конфигурация containerd минимальная - главное убедиться что CRI-плагин включен (по умолчанию да) и прописан правильный путь к сокету.
  • Меняем конфиг kubelet. Параметры --container-runtime=remote и --container-runtime-endpoint=unix:///run/containerd/containerd.sock. На systemd-нодах это в /etc/systemd/system/kubelet.service.d/.
  • Перезапускаем kubelet, uncordon. Нода возвращается в строй уже с containerd.

Образы, которые были скачаны через dockerd, containerd не видит - у них разные places хранения. Это ожидаемо, но значит что после перевода ноды будут тянуть образы заново при первом запуске подов. На первые несколько минут возможен всплеск сетевой нагрузки на registry.

Один нюанс: docker CLI после перевода на ноде больше не показывает запущенные контейнеры - он ходит в dockerd, которого нет. Для диагностики нужен crictl - утилита для работы с CRI-совместимыми runtime. Команды похожие, но не идентичные: crictl ps, crictl logs, crictl exec. Инженерам пришлось перестроиться.

Что получили

Потребление памяти на нодах снизилось заметно. Прежде всего за счёт отсутствия dockerd - это процесс, который даже в idle состоянии ест несколько сотен мегабайт. На нодах с 8 GB RAM разница чувствуется, на больших - менее критично, но всё равно приятно.

Pull образов действительно стал быстрее на больших образах. containerd скачивает слои параллельно, и это хорошо видно на образах с десятком-другим слоёв: суммарное время снижается не линейно от числа слоёв, а ограничено шириной канала. Docker исторически делал это последовательно, хотя ситуация менялась от версии к версии.

Логи событий стали чище. Это неожиданный бонус: раньше в системных логах ноды был шум от dockerd - его собственные события, ротации, вещи не связанные с k8s напрямую. Теперь тише.

Время старта подов - субъективно чуть быстрее, но это надо измерять нормально, не на глаз.

Где пришлось повозиться

Мониторинг надо было адаптировать. cAdvisor в нашей конфигурации ходил в docker socket для получения метрик контейнеров. После перехода нужно переключить его на containerd - поддержка есть, но параметры другие. Потратили время на диагностику пустых метрик, пока не разобрались.

docker-compose и ручные docker run на нодах перестали работать - там нет dockerd. Это нас не удивило, но стоит явно проговаривать заранее если в команде есть привычка что-то делать на нодах вручную через docker.

Несколько admission webhook-ов у клиентов проверяли версию рантайма через специфичные для docker поля. Пришлось разбираться и патчить.

Итого

Перевод одного кластера прошёл без инцидентов за несколько часов работы. Нода за нодой, drain-перевод-uncordon. Суммарно - меньше процессов на каждой машине, меньше памяти в idle, быстрее pull больших образов.

Переводить ли все кластеры? Пока смотрим. На этом пилоте всё хорошо, но производственные кластеры с большим числом специфичных инструментов требуют более тщательной подготовки - особенно в части мониторинга и всего что ходит в docker socket напрямую. Сначала аудит зависимостей, потом переезд.

crictl вместо docker на нодах - это, пожалуй, самая ощутимая перемена для инженеров в повседневной работе. Не сложнее, просто другое.

Контакт

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

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