Трендовые github проекты в нашем телеграм канале. Подпишись → 0,22 секунды против семи: замеры runc, gVisor и Kata для песочницы агента
Агенту для программирования дают терминал, и главный страх — что он что-то испортит за пределами рабочей папки. Обычный ответ — поставить между агентом и ядром хоста дополнительный слой. gVisor перехватывает системные вызовы и обслуживает их собственным ядром в пространстве пользователя. Kata Containers запускает каждый контейнер в легковесной виртуальной машине со своим ядром. Оба варианта подключаются к Docker как альтернативный рантайм, и вопрос в цене: сколько времени и памяти уходит на каждый контейнер.
Ниже — результаты измерений на одной машине с одинаковыми ограничениями. Замерялись только накладные расходы; стойкость песочниц к побегу не проверялась.
Стенд и правила
- Ноутбук с Intel Core i5-8250U (4 ядра, 8 потоков), 11 ГиБ RAM, SATA SSD, NixOS с ядром Linux 6.18 и KVM.
- Docker 29.8 с runc 1.5.1 как базовая линия.
- gVisor (runsc 20260406.0 из nixpkgs) с платформой по умолчанию.
- Kata Containers 4.3.0, официальные статические бинарники, конфигурация по умолчанию с QEMU 11.0, плюс та же Kata с официальным конфигом для Cloud Hypervisor v54.
- Каждый контейнер запускался с
--cpus 2 --memory 2g --network none, образ одинаковый для всех вариантов.
Пять сценариев:
- Запуск —
docker run --rmс командойtrue, время на хосте, 30 прогонов. - Память в простое — контейнер с
sleep 300, через 10 секунд суммируется PSS его процессов на хосте, 10 прогонов. - Повтор команд агента — команды из шести реальных сессий агента, проигранные побайтно в свежем контейнере с git-репозиторием, 10 прогонов.
- Компиляция — полная сборка Lua 5.4.7 через
makeиmake clean. - Мелкие файлы — создание venv, установка JupyterLab из локального индекса без сети и распаковка дерева из 5000 файлов.
Для каждого варианта считалась медиана и отношение к runc с 95% доверительным интервалом (бутстрэп, 10 000 выборок). «Медленнее» — если интервал не включает 1 и отношение больше 1,10. «Сопоставимо» — только если весь интервал лежит в диапазоне 0,95–1,05.
Результаты
| Сценарий (медиана) | runc | gVisor | Kata · QEMU | Kata · CLH |
|---|---|---|---|---|
Запуск docker run --rm true | 0,224 с | 0,330 с (1,48×) | 7,09 с (31,7×) | 6,12 с (27,3×) |
| Память в простое, PSS | 11,5 МиБ | 48,6 МиБ (4,2×) | 302 МиБ (26,4×) | 199 МиБ (17,4×) |
| Повтор команд агента | 0,444 с | 0,835 с (1,88×) | 7,80 с (17,6×) | 6,82 с (15,4×) |
| Компиляция, время внутри контейнера | 7,21 с | 8,31 с (1,15×) | 10,04 с (1,39×) | 10,70 с (1,48×) |
| Мелкие файлы, время внутри контейнера | 14,98 с | 15,44 с (сопоставимо) | 24,21 с (1,62×) | 25,34 с (1,69×) |
Старт Kata занимает секунды
Старт виртуальной машины доминирует во всех коротких сценариях. Переход с QEMU на Cloud Hypervisor экономит около секунды, но 6 секунд на true всё равно на порядок больше, чем у gVisor. В сценарии с командами агента полезная работа занимает меньше секунды, поэтому Kata проигрывает runc в 15–18 раз почти целиком из-за загрузки ВМ.
Куда уходит память
Для runc хост видит процесс контейнера и shim containerd — вместе 11,5 МиБ. gVisor добавляет два процесса: sandbox, то есть ядро в пространстве пользователя (22,5 МиБ), и Gofer, который обслуживает файловые операции (13,9 МиБ). У Kata почти всё съедает гипервизор: QEMU — 275 МиБ, Cloud Hypervisor — 172 МиБ, сверху shim Kata на 24 МиБ и два процесса virtiofsd.
В учёт попадали процессы из cgroup контейнера, потомки его init и вспомогательные процессы рантайма, у которых в командной строке есть ID контейнера. Память ядра хоста и page cache сюда не входят, так что реальная нагрузка на машину выше.
Для homelab-сервера это прямая арифметика: десять параллельных песочниц на Kata с QEMU — около 3 ГиБ только на оверхед, на gVisor — меньше 500 МиБ.
Файловая система gVisor оказалась быстрой
Ожидание было обратным: тысячи мелких файлов через Gofer должны бить по gVisor сильнее всего. На деле при замере изнутри контейнера отношение к runc составило 1,03 с интервалом 0,97–1,03, то есть разницы нет. Kata на том же сценарии медленнее в 1,6–1,7 раза. Дороже всего gVisor обходятся запуск контейнера и серии коротких команд.
На компиляции gVisor теряет 15%, Kata — 39–48%. Если мерить время с хоста, к Kata прибавляются те же 6–7 секунд старта, и отношение растёт до 2,27 (QEMU) и 2,15 (CLH).
Грабли, которые всплыли по ходу
Протокол эксперимента менялся четыре раза, и каждое изменение полезно знать тем, кто будет повторять замеры у себя.
- Запуск без контейнера как нижняя граница не сработал. На NixOS на хосте не было утилит из Debian-образа, пути при повторе сессий ломались, а компилятор был другой версии GCC. От этого варианта отказались, базой остался runc.
- Тесты молча врали. В первых версиях репозиторий для команд агента не был git-репозиторием, часть команд неправильно экранировалась, а ошибки в тесте с файлами обвязка не ловила. Помогло правило: прогон засчитывается, только если каждый шаг вернул тот же exit code, что и под runc. После исправлений все 40 прогонов прошли эту проверку.
- Отбор процессов для памяти. Ошибки поиска процессов не должны теряться, а для каждого учтённого процесса пишется причина его включения. После этой доработки замеры памяти повторили, цифры совпали.
- Kata зависала на stdin. Некоторые команды ждали ввода и не завершались. Теперь каждый шаг запускается с закрытым stdin и таймаутом 120 секунд — это стоит сразу заложить в любую обвязку вокруг Kata.
Ограничения замеров
- Одна машина с SATA SSD: абсолютные цифры на сервер не переносятся, смотреть стоит на соотношения.
- Тестировались конфигурации по умолчанию. Другие платформы gVisor, тюнинг памяти и CPU виртуалки в Kata и шаблоны ВМ для быстрого старта не проверялись.
- Одинаковые лимиты не дают одинаковую картину внутри:
nprocпоказывает 8 под runc, 2 под gVisor и 3 под Kata. Если сборка ориентируется наnproc, параллелизм будет разным. - Сценарий с командами агента — отфильтрованный набор без обращений к модели; команды, трогающие конфигурацию самого агента (например, часть запусков pytest), выброшены. Это стоимость повтора команд, полную рабочую сессию он не описывает.
- Безопасность и возможность побега не оценивались.
Как выбирать
- Агент поднимает новый контейнер на каждую команду — Kata в конфигурации по умолчанию не подходит: 6–7 секунд и 200–300 МиБ на запуск.
- Агент часами живёт в одном контейнере — старт Kata становится разовой платой, остаётся замедление задач в 1,4–1,7 раза. Взамен контейнер получает собственное ядро.
- Нужна изоляция сильнее runc при небольшом бюджете — gVisor: треть секунды на старт, около 50 МиБ памяти, минус 15% на компиляции и без потерь на мелких файлах.
- runc остаётся самым быстрым, но только в нём агент делит ядро с хостом.