В середине марта стало известно, что компания Docker предложила свой проект containerd независимому фонду Cloud Native Computing Foundation (кстати, произошло это одновременно с rkt от CoreOS). Событие последовало за обещанием компании, данным в декабре прошлого года, когда containerd был официально отделён от Docker Engine. Что же это за компонент и зачем его отделили?
Как устроен containerd
containerd — это бывшая часть Docker, а ныне самостоятельное решение, реализующее исполняемую среду для запуска контейнеров. При его создании, как утверждают разработчики, они стремились к простоте, надёжности и портируемости.
«Физически» это демон на хост-системе, который управляет всем жизненным циклом контейнера: от получения и хранения образа до запуска контейнера (через runC — подробнее см. ниже) и контролирования его работы. С демоном containerd можно взаимодействовать по низкоуровневому gRPC API через локальный UNIX-сокет, а для экспериментов и отладки также доступна консольная утилита ctr (она тоже использует gRPC API). Исходный код написан на Go и доступен на GitHub под лицензией Apache License 2.0.
Основные примитивы, с которыми работает containerd, — это bundles («комплекты») и контейнеры. Запуском контейнеров занимается runC — утилита, написанная на Go, использующая libcontainer и отделённая от Docker в 2015 году. Она работает в соответствии со спецификацией OCI Runtime Specification и запускает контейнеры как свои дочерние процессы. Для запуска требует только корневую файловую систему и конфигурацию (всё остальное: получение образа, его распаковка и т.п. — остаётся для неё «за кадром»).
Bundles содержат конфигурацию, метаданные и данные корневой файловой системы. Они являются дисковым представлением запущенного контейнера (в простейшем случае — это обычный каталог в ФС), которое можно переносить на другие системы, упаковывать и распространять. По сути всё устройство containerd заключается в том, чтобы координировать создание и запуск bundles.
Подробнее об архитектуре
Компоненты containerd образуют следующие подсистемы:
- Distribution — сервис, обеспечивающий получение образов контейнеров.
- Bundle — сервис, позволяющий извлекать (и упаковывать) bundles из дисковых образов.
- Runtime — сервис для запуска bundles (вызывает runC для запуска контейнеров с переданными им параметрами).
Каждая подсистема имеет один или несколько компонентов, реализующих её поведение. Именно к сервисам, предоставляемым подсистемами, и обращаются пользователи containerd через gRPC API.
Компоненты containerd, работающие одновременно с разными подсистемами, называются модулями и представлены следующими:
- Executor, реализующий непосредственный запуск контейнера.
- Supervisor, контролирующий и отражающий статус контейнера.
- Metadata, хранящий метаданные в графовой базе данных.
- Content, предоставляющий доступ к адресуемому хранилищу контента (постоянных данных).
- Snapshot, управляющий снапшотами файловой системы для образов контейнера. Аналог graphdriver в сегодняшнем Docker. Слои распаковываются в снапшоты.
- Events, реализующий событийное поведение и возможность аудита.
- Metrics, обеспечивающий доступность (по API) метрик различных компонентов.
Всё вместе это выглядит так:
Как это работает
Схема и её описание взяты из документа containerd/design/architecture:
- В контроллер подсистемы Distribution приходит запрос забрать нужный образ. Он помещает содержимое образа в хранилище контента (Content store), а указатели на имя образа и корневой манифест регистрируются в хранилище метаданных (Metadata store).
- Когда образ получен, пользователь может (через контроллер Bundle) распаковать образ в bundle. Слои этого образа (полученные из хранилища контента) распаковываются в модуль Snapshot.
- Когда снапшот корневой системы контейнера готов, контроллер Bundle, используя манифест и конфиг образа, формирует конфигурацию для запуска (в частности, готовится список mount'ов, полученных из модуля Snapshot).
- Подготовленный bundle передаётся в подсистему Runtime для запуска. Она считывает готовую конфигурацию для создания работающего контейнера.
Роль в Docker, связь с другими компонентами
Вынесение containerd в отдельный проект началось около года назад, когда разработчики Docker предприняли попытку «сделать Docker Engine компактнее, лучше, быстрее и сильнее» и так объясняли свои действия: «Имея автономную среду исполнения вроде runc, мы нуждались ещё в аккуратной точке для интеграции, чтобы добавить runc к общему стеку и управлять сотнями контейнеров».
На момент выделения containerd из Docker (1.12) в плане развития проекта значился «рефакторинг кодовой базы Docker Engine для выноса большей части логики размещения, сетевой работы и хранения на единственном хосте в компонент, предназначенный для многократного использования, который будет применяться в Docker и которым смогут пользоваться другие проекты оркестровки контейнеров и сервисы размещения контейнеров».
Набор функций, выполнение которых поручили containerd, получился следующим:
- размещение образов в Docker Registry;
- поддержка сети для создания системных интерфейсов и API для управления сетевым пространством имён контейнера;
- хранилище (на уровне хоста) для файловых систем образа и контейнера;
- gRPC API (именно по нему и сам Docker Engine общается с containerd);
- новый API для метрик в формате Prometheus, используемых внутри и на уровне контейнера;
- полная поддержка спецификации образов OCI (Open Container Initiative) и эталонной реализации runC.
Так в Docker видят использование будущего containerd 1.0 (его релиз запланирован на июнь 2017 года) внутри Docker Engine:
Подводя итог, авторы утверждают, что в containerd сосредоточены возможности, в которых «нуждается любая ориентированная на контейнеры платформа», и ничего лишнего. По факту это достаточно низкоуровневое решение, взаимодействовать с которым предлагается не конечным разработчикам и пользователям, а более крупным системам, таким как Kubernetes (в рамках Kubelet) и Mesos, а также сервисам вроде Amazon ECS и Google Container Engine. Встраивание в продукты предлагается всё через тот же gRPC API (утилита ctr предназначена только для отладки и экспериментов).
P.S. На прошлой неделе Cloud Native Computing Foundation формально подтвердила принятие containerd в число своих проектов. И в тот же день такая участь постигла проект rkt, который решает аналогичные containerd задачи в CoreOS: является реализацией открытой спецификации App Container (appc) и использует формат ACI (Application Container Image) для образов. CNCF будет поддерживать развитие обоих проектов.
Автор: Флант