Сборочный воркер выполняет чужой код с доступом к компиляторам, файловой системе и входным артефактам. Если после job его просто «почистили» и вернули в очередь, следующая сборка наследует риск от предыдущей. Надёжная модель другая: один job получает изолированную виртуальную машину, после завершения система забирает только ожидаемые результаты, уничтожает среду и создаёт новую из доверенного baseline.

Проблема не в скорости, а в памяти между job

Команды часто начинают с постоянных CI-workers. Так быстрее: кэш уже прогрет, toolchain установлен, диагностика привычна. Пока сборки принадлежат одной небольшой команде, цена компромисса кажется приемлемой. Но при росте платформы на воркере запускаются изменения из разных репозиториев, merge requests и внешние зависимости. Среда превращается в границу между недоверенными арендаторами.

В 2023 году команда Launchpad описала похожий случай с riscv64. Сборки выполнялись через полную эмуляцию на amd64, но виртуальные машины создавались вручную и не возвращались к чистому состоянию между заданиями. Контейнерный или chroot-escape позволил бы одной вредоносной сборке повлиять на последующие. Из-за этого доступ к riscv64 приходилось ограничивать, хотя сама эмуляция работала.

Исправление было архитектурным: riscv64-builders перевели на ту же модель, что и остальные архитектуры Launchpad. Каждый job запускается в изолированной временной VM, а после работы VM уничтожается и создаётся заново из чистого образа. Актуальная документация Launchpad 2026 года подтверждает этот принцип: менеджер собирает результаты, builder сбрасывается к baseline, а регионы поддерживают чистые VM-образы.

Сформулируйте контракт одноразового builder

Слово «ephemeral» полезно только вместе с проверяемыми условиями. Контракт сборочной платформы должен отвечать на пять вопросов.

Что создаётся заново? Не только контейнер процесса, но вся вычислительная граница, которую предыдущий job мог изменить: ядро, диски, служебные агенты и сетевые настройки. Если общий гипервизор или daemon остаётся, его защита входит в threat model.

Что разрешено вынести? После job оркестратор забирает заранее описанные артефакты, логи и метаданные. Произвольный домашний каталог воркера не становится кэшем следующей сборки.

Из чего рождается новая среда? Из версионированного baseline-образа с известным происхождением. Образ должен собираться отдельным процессом, проходить проверку и обновляться независимо от пользовательских job.

Когда среда уничтожается? После успеха, ошибки, отмены и тайм-аута. Аварийный путь важнее happy path: именно зависшая или прерванная сборка чаще оставляет неожиданное состояние.

Кто доказывает сброс? Планировщик не должен помечать worker свободным, пока инфраструктура не подтвердила уничтожение старой VM и готовность новой. Состояние «очистка не подтверждена» означает карантин, а не повторное использование.

Уничтожайте среду, а не угадывайте следы

Cleanup-скрипт знает только о следах, которые команда уже перечислила. Вредоносный job ищет всё остальное: фоновые процессы, изменённые системные файлы, сокеты, mount namespace, кэш пакетного менеджера и настройки служебного агента. Чем сложнее очистка, тем больше отрицательных утверждений приходится доказывать: «нигде ничего не осталось».

Уничтожение VM меняет задачу. Платформа доказывает более узкое свойство: новый job стартовал из доверенного образа и не подключил изменяемый диск предыдущего. Это не устраняет уязвимость гипервизора, но заметно уменьшает объём общего состояния.

Кэши при этом не запрещены. Вынесите их в отдельный сервис и считайте недоверенным входом. Ключ кэша должен включать важные параметры сборки, содержимое нужно проверять перед использованием, а job не должен получать право незаметно подменить кэш для другого проекта. Производительность остаётся оптимизацией поверх изоляции, а не причиной сохранить заражаемый worker.

Ограничьте сеть и полномочия

Одноразовая VM не спасает, если сборка получает долгоживущий токен с широкими правами. Выдавайте полномочия на один job, с минимальным scope и коротким сроком жизни. После завершения отзывайте или делайте их бесполезными независимо от удаления VM.

Сеть тоже часть границы. Текущая архитектура Launchpad не даёт builders прямой доступ в интернет: внешние ресурсы проходят через прокси с ограниченным набором URL. Для собственной платформы минимум — запрет доступа к control plane, метаданным облака и соседним workers. Разрешённый egress должен соответствовать рецепту сборки и оставлять журнал.

Не смешивайте управляющие и пользовательские каналы. Агент, который получает job и отдаёт результаты, не должен исполнять команды из лога или артефакта. Оркестратор принимает только данные ожидаемого типа, проверяет размер и связывает результат с конкретным Run.

Версионируйте baseline как production-компонент

Чистый образ может быть чистым, но устаревшим. Ведите для него отдельный жизненный цикл: источник базового cloud image, набор добавленных пакетов, версия агента, дата сборки, результаты сканирования и контрольная сумма. Launchpad, например, синхронизирует стандартные Ubuntu cloud images и модифицирует их для установки launchpad-buildd; регионы поддерживают свои чистые образы для builders.

Обновление baseline запускайте через canary-пул. Сначала соберите репрезентативные проекты, сравните длительность и выходные артефакты, затем расширяйте долю. Храните возможность быстро вернуть предыдущий проверенный образ, но не возвращайте VM, которая уже исполняла пользовательский job.

Проверяйте не только загрузку VM. Полезный smoke test подтверждает, что агент зарегистрировался, сеть соблюдает policy, рабочие диски пусты, ожидаемые toolchains доступны, а тестовый артефакт можно забрать. Только после этого builder переходит в состояние ready.

Спроектируйте отказ до внедрения

У Launchpad переход riscv64 на общую модель упёрся не в саму виртуализацию, а в зависания snap-сборок на более новых образах и медленных архитектурах. Причину связали с ошибкой LXD и нашли обходной путь. Это важный урок для миграции: безопасность нельзя выкатывать отдельно от наблюдаемости и диагностики.

Перед расширением пула измерьте:

  • время от назначения job до готовности VM;
  • долю ошибок создания, сброса и удаления среды;
  • число builders в карантине;
  • возраст baseline-образа;
  • расхождение длительности и результатов между старым и новым пулом;
  • попытки запрещённого egress и обращения к control plane.

Если удаление VM завершилось неизвестно, сначала проверьте состояние у провайдера. Не создавайте бесконечные повторы: они могут накопить «осиротевшие» машины или дважды отдать один job. Идентификатор Run, lease на выполнение и идемпотентная операция уничтожения должны быть частью оркестратора, а не договорённостью в shell-скрипте.

Ограничения

Временная VM не даёт абсолютной изоляции. Уязвимость гипервизора, общий firmware, неверно подключённый persistent disk или чрезмерные права агента сохраняют риск. Для особо чувствительных сборок может понадобиться отдельный physical pool или более сильная аппаратная граница.

Полный холодный старт также дороже постоянного worker. Сначала измерьте, какая часть времени уходит на VM, скачивание зависимостей и компиляцию. Оптимизируйте образы, локальные зеркала и проверяемые удалённые кэши, но не возвращайте межпроектное изменяемое состояние.

Старый материал Launchpad описывал конкретную миграцию riscv64 в 2023 году. Детали QEMU, OpenStack и LXD не обязаны совпадать с вашей платформой. Неизменной остаётся практика, подтверждённая текущей документацией Launchpad: изолированный builder, контролируемая сеть, сбор только ожидаемых результатов и сброс к чистому baseline после каждого job.

Вывод

CI-worker следует считать одноразовым исполнителем недоверенного кода. Если среда переживает job, платформа должна доказать, что разделяемое состояние безопасно; обычно это сложнее, чем уничтожить VM и начать с проверенного образа. Стройте кэши, сеть, полномочия и обработку ошибок вокруг этой границы. Тогда ускорение одной сборки не создаст скрытый риск для всех следующих.

Источник и лицензия

Это самостоятельная русская адаптация: материал переведён, сокращён, дополнен инженерными рекомендациями и переработан по структуре. Canonical, Colin Watson и команда Launchpad не одобряли эту публикацию и не связаны с её редакцией.