Зачем переписывать контейнерный рантайм на Си
Попробуйте запустить Docker или Podman с ограничением по памяти в 512 килобайт. Стандартный рантайм runc на таком лимите просто упадёт с ошибкой. Ему не хватит памяти даже на то, чтобы поднять базовые структуры процесса и прочитать файловые дескрипторы.
Причина кроется в архитектуре. Подавляющая часть современного стека контейнеризации написана на Go. Для утилит верхнего уровня это отлично, но на самом нижнем слое Go тянет за собой собственный рантайм, сборщик мусора и накладные расходы на память. Чтобы настроить неймспейсы ядра Linux до старта основного процесса, runc вынужден перезапускать сам себя и обращаться к Си-костылям.
Проект crun от организации Containers решает эту задачу в лоб. Это легковесная реализация спецификации OCI (Open Container Initiative), полностью написанная на чистом Си.
Что даёт переход на чистый Си
Основная идея проста: убрать всё лишнее с критического пути создания контейнера. Когда рантайм не обременён лишними прослойками, цифры потребления ресурсов и скорости отклика меняются в разы.
Потребление памяти и запуск на грани лимитов
В бенчмарках разработчиков видна показательная разница:
# Запуск через runc падает
$ podman --runtime /usr/bin/runc run --rm --memory 4M fedora echo it works
Error: container_linux.go:346: starting container process caused "process_linux.go:327: getting pipe fds for pid 13859 caused \"readlink /proc/13859/fd/0: no such file or directory\"": OCI runtime command not found error
# Запуск через crun отрабатывает без запинки
$ podman --runtime /usr/bin/crun run --rm --memory 512k fedora echo it works
it works
Контейнер с crun стабильно запускается даже с жестким лимитом памяти в 512 КБ, тогда как runc спотыкается даже на 4 МБ. Для тяжелых микросервисов в облаке разница в мегабайт может показаться мелочью, но на IoT-устройствах, роутерах и edge-серверах такая экономия освобождает драгоценную память под полезную нагрузку.
Скорость последовательного старта
Когда требуется часто запускать короткоживущие задачи (например, в serverless-платформах или CI/CD раннерах), время инициализации контейнера становится узким местом. В тесте на последовательный запуск 100 контейнеров с командой /bin/true разница во времени почти двукратная:
| Рантайм | Время выполнения (100 контейнеров) | Разница | | :--- | :--- | :--- | | crun | 0:01.69 | -49.4% | | runc | 0:03.34 | базовый уровень |
Си-реализация стартует в два раза быстрее за счёт отсутствия оверхеда инициализации среды Go и лишних системных вызовов.
Использование в виде библиотеки
Большинство рантаймов существуют только как отдельные исполняемые файлы. Если вам нужно запустить OCI-контейнер из своей программы, обычно приходится делать fork/exec и вызывать внешнюю утилиту.
В crun есть возможность собрать разделяемую библиотеку libcrun. Это открывает прямой C API для работы с контейнерами:
- встраивание запуска изолированных окружений внутрь собственных демонов
- отсутствие накладных расходов на вызовы отдельных процессов
- работа с биндингами для Python и Lua прямо из коробки
Сборка и установка
Утилита доступна в большинстве дистрибутивов через штатные пакетные менеджеры, но собрать её из исходников тоже несложно.
Для сборки на Ubuntu понадобятся базовые заголовочные файлы:
$ sudo apt-get install -y make git gcc build-essential pkgconf libtool \
libsystemd-dev libprotobuf-c-dev libcap-dev libseccomp-dev libjson-c-dev \
go-md2man autoconf python3 automake
Сам процесс компиляции стандартный:
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
Если планируете использовать библиотеку в коде, флаг конфигуратора изменится на ./configure --enable-shared.
Для воспроизводимых окружений и серверов без лишних зависимостей проект поддерживает статическую сборку через Nix Flakes:
$ nix --extra-experimental-features "nix-command flakes" build "path:.#crun-static-amd64"
$ ./result/bin/crun --version
На выходе получается компактный статический бинарник под x86_64 с glibc, который готов работать на любой целевой машине.
Кому пригодится
Переключаться на crun имеет смысл в нескольких сценариях:
- Edge computing и IoT. Когда на устройстве распаяно 256 МБ или 512 МБ оперативной памяти, отдавать мегабайты под рантайм непозволительно.
- Serverless и Functions-as-a-Service. В средах с частым созданием и уничтожением изолированных контейнеров ускорение старта на 50% напрямую снижает задержки cold start.
- Плотная упаковка контейнеров. Если на одном хосте крутятся сотни мелких воркеров, суммарная экономия RAM исчисляется гигабайтами.
Подменить рантайм в Podman можно буквально одним флагом --runtime /usr/bin/crun или изменением параметра в /etc/containers/containers.conf. Никаких правок в манифестах или образах при этом не требуется.
