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. Проблем с работой контейнеров не наблюдаем. Возможность перезапустить демон без остановки нагрузки - уже используем при обновлениях конфигурации.
Архитектурное разделение нам нравится концептуально: каждый компонент делает одно дело. Насколько это скажется на стабильности в сложных ситуациях - увидим при накоплении наблюдений. Пока что просто обновляемся и смотрим.
- Docker 1.10: обновили production до новой сетевой подсистемы · 5 февраля 2016
- Docker Compose v2: именованные сети, volumes и первый взгляд на Swarm · 19 апреля 2016