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

Docker 1.11: containerd, runC и зачем они разобрали демон на части

Docker 1.11 перешёл на containerd и runC под стандарты OCI. Что это значит на практике: демон перезапускается, контейнеры остаются работать.

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

Docker 1.11 (апрель 2016) перешёл на containerd и runC в соответствии со стандартами OCI, разделив монолитный daemon на независимые компоненты

Docker 1.11 вышел в конце апреля, и главная новость в нём - не новые флаги и не переработанный UI, а то, что произошло под капотом. Монолитный docker daemon разобрали на несколько процессов. Мы это обновление прогнали на тестовом стенде, потом на нескольких боевых хостах - и хотим рассказать, что изменилось на практике, а не в changelog.

Что было раньше

В Docker до 1.11 весь рантайм жил в одном процессе - dockerd. Он делал всё: общался с клиентом, управлял образами, запускал и останавливал контейнеры, следил за сетью. Один большой демон с множеством ответственностей.

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

Что изменилось в 1.11

Теперь docker ps покажет вам не один процесс, а три:

dockerd          # верхний уровень: API, образы, volumes, network
containerd       # lifecycle контейнеров: start/stop/pause/rm
docker-containerd-shim  # по одному на каждый контейнер

containerd - это отдельный демон, который управляет жизненным циклом контейнеров. Он знает про запуск, остановку, паузу, удаление. dockerd общается с ним по gRPC, а сам занимается образами, сетью, API-слоем.

runC - это исполнитель. Именно он создаёт процесс в контейнере: namespaces, cgroups, seccomp. После запуска контейнера runC завершается, а docker-containerd-shim остаётся жить как тонкая прослойка, которая держит stdio и сигналы.

Всё это следует спецификациям OCI - Open Container Initiative. Docker, CoreOS и ещё несколько компаний договорились о стандарте на формат образа и рантайм. runC - референсная реализация этого стандарта. Стандарт свежий, но то, что он появился - само по себе интересно.

Что это даёт прямо сейчас

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

Мы проверили это на стенде, потом на боевом хосте. Последовательность такая:

# Смотрим, что работает
docker ps

# Перезапускаем демон
systemctl restart docker

# Контейнеры не умерли
docker ps

Контейнеры продолжают работать. containerd живёт своей жизнью, docker-containerd-shim держит каждый контейнер. dockerd перезапускается, подключается обратно к containerd - и видит те же контейнеры.

На практике это означает: обновление docker-пакета без остановки нагрузки. Мы так и проверяем теперь обновления - на хосте с работающими контейнерами, без окна обслуживания для каждого.

Оговорка: это верно для обновлений внутри поколения. Крупные изменения - другой разговор, там может потребоваться ручная остановка.

Что мы увидели при обновлении

Обновление с 1.10 до 1.11 на Ubuntu 14.04 и 16.04 прошло чисто через apt:

apt-get update && apt-get upgrade docker-engine

После установки в /usr/bin/ появились docker-containerd, docker-containerd-shim, docker-runc. Названия с префиксом docker- - чтобы не конфликтовать с пакетами containerd и runc из других источников.

Уже работающие контейнеры пережили обновление. systemctl restart docker после apt - контейнеры живы.

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

Что смущает

containerd и dockerd теперь разные демоны. Если containerd упадёт (а такое теоретически возможно), dockerd не сможет управлять контейнерами, хотя сами контейнеры продолжат работать через shim-ы. Как это поведение скажется на edge-кейсах - пока неясно. Мы это не воспроизводили намеренно.

Логи размазаны по компонентам. Раньше всё в одном journalctl -u docker. Теперь dockerd, containerd - разные процессы. journalctl -u docker показывает dockerd, для containerd нужно смотреть отдельно. Нужно обновить скрипты мониторинга - у нас были алерты на ошибки в docker-логах.

OCI-стандарт сам по себе не означает совместимости образов между разными рантаймами здесь и сейчас. Это спецификация, которая только начинает внедряться.

Где сейчас

Несколько боевых хостов уже работают на 1.11. Проблем с работой контейнеров не наблюдаем. Возможность перезапустить демон без остановки нагрузки - уже используем при обновлениях конфигурации.

Архитектурное разделение нам нравится концептуально: каждый компонент делает одно дело. Насколько это скажется на стабильности в сложных ситуациях - увидим при накоплении наблюдений. Пока что просто обновляемся и смотрим.

Контакт

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

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