Сколько RAM нужно для Drone CI
Drone CI — контейнерный CI/CD: каждый шаг пайплайна запускается в отдельном Docker-контейнере, а не в общем процессе-воркере, как у части других систем. Это удобно с точки зрения изоляции, но заставляет по-другому считать память: мало «зарезервировать RAM под CI-сервер», нужно учитывать ещё и то, что параллельно крутится десяток контейнеров сборки. Разберём, из чего складывается расход и сколько закладывать на VPS, чтобы Drone не падал по OOM в самый неподходящий момент.
Содержание
- Как устроен Drone CI и почему память считается иначе
- Из чего складывается общий расход памяти
- Сколько нужно для старта: минимальная конфигурация
- Таблица: реальные сценарии и ориентировочный расход на шаг
- Как считать RAM под конкурентность пайплайнов
- Лимиты памяти на шаги, чтобы не поймать OOM всей системой
- Что делать, когда памяти всё равно не хватает
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как устроен Drone CI и почему память считается иначе
Drone состоит из двух ролей: drone-server (веб-интерфейс, БД, приём вебхуков от Git-провайдера, распределение задач) и один или несколько drone-runner-docker — агентов, которые реально выполняют пайплайны. Раннер подключается к серверу по RPC, забирает задачу и для каждого шага .drone.yml поднимает отдельный Docker-контейнер через сокет /var/run/docker.sock.
Из этого вытекает главный момент: сам Drone (сервер + раннер) — лёгкие Go-бинарники, которые в простое едят немного, ориентировочно до пары сотен мегабайт суммарно. А вот память, которую съедает пайплайн, целиком зависит от того, что вы туда напихали — сборка Node.js-проекта, docker build многослойного образа или прогон интеграционных тестов с базой данных в соседнем контейнере. Поэтому вопрос «сколько RAM нужно для Drone CI» на самом деле раскладывается на два: сколько нужно самой платформе и сколько — вашим шагам с учётом параллелизма.
Из чего складывается общий расход памяти
Считайте RAM для сервера с Drone как сумму пяти слагаемых:
- drone-server — веб-UI, API, хранилище состояния пайплайнов (SQLite или внешняя Postgres/MySQL). В простое немного, но под нагрузкой (много одновременных вебхуков, большая история билдов) растёт база и кэш.
- drone-runner-docker — сам процесс раннера, лёгкий, но именно он инициирует все дочерние контейнеры и держит с ними связь.
- Docker-демон — накладные расходы на управление контейнерами, сетями, слоями образов. Растут пропорционально числу одновременно живых контейнеров.
- Контейнеры шагов пайплайна — самая тяжёлая и самая непредсказуемая часть. Один шаг
docker buildдля образа сnpm installвнутри может кратковременно требовать больше памяти, чем весь остальной стек вместе взятый. - Docker-in-Docker (DinD), если используется — отдельный слой, который сам по себе не бесплатен: DinD-контейнер держит собственный демон, кэш слоёв, временные образы.
Ключевая ошибка при прикидке — считать только первые два пункта и забывать, что реальные шаги сборки — это полноценные процессы со своим потреблением, которые Docker не «сжимает» магическим образом просто потому, что они внутри CI.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверСколько нужно для старта: минимальная конфигурация
Если вам нужен только сам факт работающего Drone — сервер плюс раннер, без тяжёлых пайплайнов, — минимум выглядит так:
| Компонент | Минимум RAM | Комментарий |
|---|---|---|
| drone-server (SQLite) | 256–512 МБ | На старте с SQLite Postgres не нужен |
| drone-runner-docker | 128–256 МБ | Сам процесс лёгкий |
| Docker-демон (простой) | 100–200 МБ | Без активных контейнеров |
| Запас на 1 лёгкий шаг (lint, unit-тесты на скриптовом языке) | 300–500 МБ | Ориентировочно, зависит от языка |
| Итого работоспособный минимум | ~1–1.5 ГБ | Только для «пощупать», без продакшен-нагрузки |
Это цифры для знакомства с системой, не для реального CI, который гоняет билды команды. На практике комфортный старт для одного разработчика или маленькой команды — это 2 ГБ RAM: сервер и раннер живут спокойно, и на один-два параллельных шага остаётся запас без свопа.
Пример минимального docker-compose.yml для быстрого старта (без внешней БД, с одним раннером):
version: "3.8"
services:
drone-server:
image: drone/drone:2
ports:
- "80:80"
volumes:
- drone-data:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_GITEA_SERVER=https://git.example.com
- DRONE_RPC_SECRET=change-me
- DRONE_SERVER_HOST=ci.example.com
- DRONE_SERVER_PROTO=https
mem_limit: 512m
restart: unless-stopped
drone-runner:
image: drone/drone-runner-docker:1
environment:
- DRONE_RPC_HOST=drone-server
- DRONE_RPC_PROTO=http
- DRONE_RPC_SECRET=change-me
- DRONE_RUNNER_CAPACITY=2
volumes:
- /var/run/docker.sock:/var/run/docker.sock
mem_limit: 256m
restart: unless-stopped
volumes:
drone-data:
DRONE_RUNNER_CAPACITY — ключевой параметр для расчёта RAM: он задаёт, сколько пайплайнов раннер может выполнять одновременно. Именно от него, а не от абстрактных «пары гигабайт с запасом», нужно плясать при планировании памяти.
Таблица: реальные сценарии и ориентировочный расход на шаг
Ниже — ориентировочные диапазоны для типовых шагов пайплайна. Это именно ориентир: точные цифры зависят от размера проекта, версий инструментов и того, что параллельно происходит в системе. Проверяйте на своей нагрузке через docker stats во время реального билда.
| Тип шага | Что происходит | Ориентировочный пик RAM |
|---|---|---|
| Линтер / статический анализ (eslint, golangci-lint) | Разбор AST небольшого проекта | 150–400 МБ |
| Unit-тесты на Go | Компиляция + прогон тестов | 200–500 МБ |
npm ci && npm run build для среднего фронтенд-проекта | Установка зависимостей, сборка бандла (webpack/vite) | 800 МБ – 1.5 ГБ |
docker build многослойного образа с компиляцией | Сборка слоёв, кэш buildkit | 1–2.5 ГБ |
| Интеграционные тесты с Postgres/Redis в соседних сервисах | Тестовый рантайм + соседние контейнеры | 1–2 ГБ суммарно |
| Сборка Java/Kotlin через Gradle | JVM-эвристики памяти по умолчанию агрессивны | 1.5–3 ГБ |
| Python-проект с тяжёлыми зависимостями (numpy, torch и т. п.) | Установка колёс, компиляция нативных расширений | 1.5–3 ГБ |
Обратите внимание на два «тяжеловеса» — Gradle и Python с ML-зависимостями. Gradle-демон по умолчанию резервирует память щедро, и если не ограничить его явно (org.gradle.jvmargs=-Xmx1g в gradle.properties), он попробует взять столько, сколько увидит в контейнере. Такая же история с Node — node --max-old-space-size стоит выставлять руками для сборок с большими бандлами, иначе процесс упрётся в дефолтный лимит V8 или, наоборот, попробует съесть всё, что найдёт.
Как считать RAM под конкурентность пайплайнов
Формула для планирования простая:
RAM_сервера ≈ RAM_drone_server + RAM_runner + Docker_overhead
+ (CAPACITY × средний_пик_шага) + буфер_20-30%
Пример для команды из 5 человек с фронтенд- и бэкенд-репозиториями, где типичный шаг — сборка Docker-образа (пик ~1.5 ГБ) и DRONE_RUNNER_CAPACITY=3:
RAM_server + runner + overhead ≈ 0.6 ГБ
3 × 1.5 ГБ (параллельные шаги) ≈ 4.5 ГБ
буфер 25% ≈ 1.3 ГБ
-----------------------------------------
Итого ≈ 6.4 ГБ → берём 8 ГБ
Важный нюанс: CAPACITY — это не «сколько пайплайнов», а именно сколько *шагов* может выполняться параллельно на раннере (в Drone шаги одного пайплайна по умолчанию идут последовательно, но разные пайплайны от разных коммитов вполне могут запуститься одновременно). Если у вас активная команда, где несколько человек пушат в течение дня, реальная параллельность легко достигает выставленного CAPACITY, и экономить на этом множителе не стоит — именно здесь чаще всего и вылезает OOM.
Если пайплайны сильно разные по весу (лёгкий lint и тяжёлый docker build), не берите средний пик — считайте по самому тяжёлому шагу, который реально может выполняться параллельно с другими. Недооценка здесь дороже переоценки: недостающий гигабайт RAM обваливает билд посреди сборки, а лишний просто немного простаивает.
Лимиты памяти на шаги, чтобы не поймать OOM всей системой
Без ограничений один прожорливый шаг может выесть память у соседних контейнеров и уронить не только себя, но и drone-server, если он на том же хосте. Есть три уровня защиты.
1. Лимит на сами контейнеры Drone — уже показан выше через mem_limit в docker-compose.yml. Это защищает управляющий слой: даже если шаги сборки съели всё, сервер и раннер не должны быть первыми жертвами OOM killer.
2. Лимиты на уровне шага в .drone.yml. Раннер Docker поддерживает блок resources для отдельных шагов — сверяйтесь с документацией именно вашей версии раннера, синтаксис между релизами Drone менялся:
kind: pipeline
type: docker
name: build
steps:
- name: build-image
image: docker:24-cli
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- docker build -t myapp:latest .
resources:
limits:
memory: 2GiB
requests:
memory: 1GiB
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
3. Приоритет OOM killer на уровне хоста. Если совсем поджимает память, полезно понизить приоритет служебных контейнеров Drone, чтобы ядро в первую очередь убивало тяжёлые контейнеры сборки, а не сам CI-сервер:
docker inspect --format '{{.State.Pid}}' drone-server
echo -900 > /proc/<PID>/oom_score_adj
Это грубый инструмент на крайний случай — правильнее не доводить до ситуации, когда ядро вообще выбирает, кого убить, а держать запас памяти и лимиты, описанные выше.
Что делать, когда памяти всё равно не хватает
Три рабочих направления, в порядке от дешёвого к дорогому:
- Понизить
DRONE_RUNNER_CAPACITY— самый быстрый способ снять пиковую нагрузку ценой скорости: пайплайны будут вставать в очередь вместо параллельного выполнения, но перестанут душить друг друга по памяти. - Разнести раннеры по разным хостам. Один
drone-serverможет обслуживать несколькоdrone-runner-dockerна разных машинах — тяжёлые сборки (Java, ML) уводите на отдельный сервер с большим объёмом RAM, лёгкие (lint, unit-тесты) оставляете на основном. - Проверить, не течёт ли память в самих шагах. Кэш зависимостей, который каждый раз качается заново вместо переиспользования volume, лишний DinD там, где хватило бы обычного Docker-социта с хоста, тестовые контейнеры, которые не гасятся между шагами — частые причины раздутого потребления, не связанные с самим Drone.
Если апгрейд RAM неизбежен — для CI с активной командой разумный ориентир: 4 ГБ на 1-2 параллельных лёгких шага, 8-16 ГБ на команду с регулярной параллельной сборкой Docker-образов, от 16 ГБ — если в пайплайнах живёт Gradle, ML-зависимости или несколько интеграционных сервисов одновременно. Общие принципы расчёта запаса под изменчивую нагрузку описаны в статье сколько оперативной памяти закладывать с запасом, а если сервер уже упирается в память — разбор конкретных действий есть в статье что делать при нехватке RAM. Общий подход к лимитам CPU и памяти в Docker подробно разобран в статье лимиты CPU и памяти в Docker, а если сравниваете VPS под CI/CD в целом — смотрите какой VPS выбрать для разработчика и CI/CD.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Хватит ли 2 ГБ RAM для Drone CI?
Для одного разработчика с лёгкими пайплайнами (lint, unit-тесты на скриптовых языках) — да, с запасом на один активный шаг. Для параллельной сборки Docker-образов или JVM-проектов уже тесно.
Нужна ли отдельная база данных вместо встроенного SQLite?
Для маленькой команды SQLite достаточно и он экономит память. Внешняя Postgres имеет смысл при большом числе одновременных вебхуков и долгой истории билдов — тогда лучше вынести БД на отдельный сервис, чтобы не конкурировать за память с раннером.
Почему один и тот же пайплайн иногда падает по памяти, а иногда нет?
Чаще всего дело в параллелизме: если несколько коммитов запускают билды одновременно, суммарный пик по памяти на хосте выше, чем при одиночном запуске. Проверьте DRONE_RUNNER_CAPACITY и реальную нагрузку через docker stats в момент падения.
Можно ли ограничить память для Docker-in-Docker отдельно от остальных шагов?
Да, DinD-контейнер — это тоже контейнер, для него так же работает mem_limit или resources.limits.memory. Но учтите, что DinD добавляет накладные расходы поверх памяти самой сборки внутри него.
Стоит ли переходить на Kubernetes-раннер Drone ради экономии памяти?
Сам по себе Kubernetes не экономит память — он даёт более гибкое масштабирование и изоляцию через namespaces/quotas, но добавляет собственные накладные расходы control plane. Для одного сервера Docker-раннер обычно проще и легче.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →