Drone CI в Docker Compose: готовый файл
Jenkins требует отдельный сервер под агенты и постоянно тянет за собой плагины с конфликтующими версиями Java. GitLab CI удобен, но заставляет переезжать на GitLab целиком. Если репозиторий уже живёт на GitHub или в самостоятельно поднятой Gitea, а нужен просто рабочий CI/CD без лишней инфраструктуры — Drone CI решает это за один docker-compose up. Ниже — рабочий файл конфигурации и пошаговая настройка от установки до первого зелёного пайплайна.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Drone CI и почему он контейнерный по своей природе
Drone CI построен вокруг одной идеи: каждый шаг пайплайна — это отдельный Docker-контейнер. Не процесс на общем агенте, не shell-скрипт в чужом окружении, а изолированный запуск конкретного образа с конкретной задачей. Шаг test выполняется в контейнере golang:1.22, шаг build — в node:20-alpine, шаг deploy — в вашем собственном образе с ansible или kubectl. Никакого расползания зависимостей между шагами и никакого "у меня на агенте работало".
Архитектурно Drone состоит из двух компонентов:
- drone-server — веб-интерфейс, база данных, OAuth-авторизация через Git-провайдера, приём вебхуков и постановка задач в очередь;
- drone-runner-docker — воркер, который забирает задачи из очереди и запускает их как Docker-контейнеры на своей машине.
Оба компонента разворачиваются как обычные сервисы в Docker Compose и легко масштабируются: нужно больше параллельных сборок — добавьте ещё один runner на соседнем сервере с тем же RPC-секретом. Runner сам стучится к серверу по gRPC, никакой отдельной службы вроде Jenkins-агента через SSH не требуется.
По сравнению с GitLab CI на VPS Drone не тянет за собой весь GitLab: если у вас уже есть репозиторий на GitHub, GitLab.com, Gitea или Gogs, Drone подключается к нему через OAuth и ничего не дублирует. Это в буквальном смысле CI-сервер и ничего больше — плюс для тех, кому не нужен встроенный issue-tracker и wiki, а нужен именно конвейер сборки и деплоя.
Готовый docker-compose.yml: сервер и runner
Перед запуском сгенерируйте общий секрет, которым сервер и runner будут друг другу доверять:
openssl rand -hex 16
Сохраните значение — оно пойдёт в DRONE_RPC_SECRET обоих сервисов. Дальше — сам файл:
# docker-compose.yml
version: "3.8"
services:
drone-server:
image: drone/drone:2
container_name: drone-server
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
volumes:
- drone-data:/data
environment:
- DRONE_GITEA_SERVER=https://git.example.com
- DRONE_GITEA_CLIENT_ID=your_client_id
- DRONE_GITEA_CLIENT_SECRET=your_client_secret
- DRONE_RPC_SECRET=ваш_секрет_из_openssl
- DRONE_SERVER_HOST=drone.example.com
- DRONE_SERVER_PROTO=https
- DRONE_USER_CREATE=username:ваш_логин,admin:true
- DRONE_LOGS_DEBUG=false
drone-runner:
image: drone/drone-runner-docker:1
container_name: drone-runner
restart: unless-stopped
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_HOST=drone-server
- DRONE_RPC_PROTO=http
- DRONE_RPC_SECRET=ваш_секрет_из_openssl
- DRONE_RUNNER_CAPACITY=2
- DRONE_RUNNER_NAME=runner-1
volumes:
drone-data:
Два момента здесь важны. Во-первых, drone-server слушает только 127.0.0.1:8080 — наружу его отдаёт reverse-proxy с HTTPS, напрямую в интернет держать CI-сервер незачем. Во-вторых, runner получает доступ к /var/run/docker.sock: контейнеры пайплайнов запускаются рядом с самим runner-контейнером, а не внутри него (docker-out-of-docker). Отсюда практическое ограничение: DRONE_RUNNER_CAPACITY реально упирается в ресурсы хоста, а не в какую-то внутреннюю квоту раннера.
Секцию DRONE_GITEA_* замените на нужный провайдер: DRONE_GITHUB_CLIENT_ID/DRONE_GITHUB_CLIENT_SECRET для GitHub, DRONE_GITLAB_* для GitLab. Про сам OAuth — в следующем разделе.
Поднимаем:
docker compose up -d
docker compose logs -f drone-server
Если в логах runner видно successfully pinged the remote server — связка сервер/runner работает.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПодключение GitHub или Gitea через OAuth
Drone не хранит собственных пользователей — авторизация полностью делегирована Git-провайдеру. Для GitHub заведите OAuth App в Settings → Developer settings → OAuth Apps:
- Homepage URL:
https://drone.example.com - Authorization callback URL:
https://drone.example.com/login
Полученные Client ID и Client Secret идут в переменные DRONE_GITHUB_CLIENT_ID и DRONE_GITHUB_CLIENT_SECRET.
Для самостоятельно размещённой Gitea процесс тот же, но раздел называется Settings → Applications → OAuth2 Applications, а callback URL — https://drone.example.com/login. Если Gitea крутится в том же docker-compose-стеке или на соседнем сервере, укажите её внутренний адрес в DRONE_GITEA_SERVER — трафик авторизации пойдёт напрямую, без выхода в интернет.
После рестарта сервера с новыми переменными зайдите на https://drone.example.com, авторизуйтесь через провайдера — Drone запросит доступ к репозиториям и вебхукам. Затем в интерфейсе Drone откройте нужный репозиторий и нажмите Activate: сервис сам создаст вебхук в настройках репозитория, который будет присылать события push и pull request.
Первый вход создаёт вас как обычного пользователя, а не администратора — поэтому в docker-compose.yml выше стоит DRONE_USER_CREATE=username:ваш_логин,admin:true. Строка при старте сервера один раз создаёт учётку-администратора по указанному логину (он должен совпадать с логином у Git-провайдера). После первого входа переменную можно убрать.
Пишем .drone.yml: пайплайн шаг за шагом
Конфигурация пайплайна лежит в корне репозитория в файле .drone.yml. Базовый пример для Node.js-проекта с тестами и сборкой образа:
kind: pipeline
type: docker
name: default
steps:
- name: install
image: node:20-alpine
commands:
- npm ci
- name: test
image: node:20-alpine
commands:
- npm run test
- name: build-image
image: plugins/docker
settings:
repo: registry.example.com/myapp
tags: ${DRONE_COMMIT_SHA:0:8}
registry: registry.example.com
username:
from_secret: registry_username
password:
from_secret: registry_password
when:
branch:
- main
trigger:
branch:
- main
- develop
event:
- push
- pull_request
Каждый step — это отдельный контейнер: install и test используют образ node:20-alpine заново (кеш зависимостей между шагами не переиспользуется автоматически, если явно не подключить volume или встроенный кеш-плагин). Секция build-image использует готовый плагин plugins/docker — это обёртка над docker build + docker push, которая сама разбирается с логином в реестр. Если вы уже развернули приватный Docker Registry на VPS, просто укажите его адрес в registry и repo.
Секция trigger определяет, на какие события и ветки реагировать. Без неё пайплайн запускается на всё подряд, включая push в любую фиче-ветку — обычно это не то, что нужно.
Проверить синтаксис файла без реального запуска можно локально через drone-cli:
drone lint .drone.yml
Секреты: пароли и токены отдельно от кода
Хранить пароль от реестра или токен деплоя прямо в .drone.yml нельзя — файл лежит в репозитории и виден всем, у кого есть к нему доступ. Секреты добавляются через интерфейс Drone (Repository → Settings → Secrets) или через drone-cli:
drone secret add \
--repository yourorg/myapp \
--name registry_password \
--data "супер-секретный-пароль"
В пайплайне секрет подключается конструкцией from_secret, как в примере выше с registry_password. Значение никогда не попадает в логи сборки в открытом виде — Drone маскирует известные ему секретные строки при выводе.
Для деплоя по SSH тем же способом хранится приватный ключ:
drone secret add --repository yourorg/myapp --name ssh_key --data "$(cat ~/.ssh/deploy_key)"
- name: deploy
image: appleboy/drone-ssh
settings:
host: 203.0.113.10
username: deploy
key:
from_secret: ssh_key
script:
- cd /opt/myapp && docker compose pull && docker compose up -d
when:
branch:
- main
event:
- push
Секреты в Drone привязаны к конкретному репозиторию по умолчанию — это осознанное ограничение безопасности: скомпрометированный пайплайн одного проекта не даёт доступа к секретам другого. Если нужны общие секреты на несколько репозиториев, в self-hosted Drone это делается через organization secrets на уровне сервера (drone orgsecret), но начинайте с репозиторных — их проще держать под контролем.
HTTPS через reverse-proxy и типичные проблемы запуска
Drone сам не умеет TLS — эту работу отдают reverse-proxy. Если в стеке уже есть Traefik как reverse proxy для докера, добавьте лейблы к drone-server вместо проброса порта на хост:
drone-server:
image: drone/drone:2
restart: unless-stopped
networks:
- traefik-net
volumes:
- drone-data:/data
environment:
- DRONE_SERVER_HOST=drone.example.com
- DRONE_SERVER_PROTO=https
# остальные переменные как выше
labels:
- "traefik.enable=true"
- "traefik.http.routers.drone.rule=Host(`drone.example.com`)"
- "traefik.http.routers.drone.tls.certresolver=letsencrypt"
- "traefik.http.services.drone.loadbalancer.server.port=80"
networks:
traefik-net:
external: true
Без Traefik подойдёт обычный Nginx с проксированием на 127.0.0.1:8080 и сертификатом от certbot — принцип тот же: DRONE_SERVER_PROTO=https и реальный домен в DRONE_SERVER_HOST обязательны, иначе callback OAuth и вебхуки будут собираться по неверному адресу.
Частые проблемы при первом запуске:
| Симптом | Причина | Решение |
|---|---|---|
no matching runner в логах пайплайна | Runner не законнектился к серверу | Сверьте DRONE_RPC_SECRET в обоих сервисах — значения должны совпадать посимвольно |
| Вебхук от GitHub не доходит | Сервер недоступен снаружи или неверный DRONE_SERVER_HOST | Проверьте, что домен резолвится и HTTPS отвечает: curl -I https://drone.example.com |
| Пайплайн виснет в статусе pending | У runner закончилась DRONE_RUNNER_CAPACITY или закончились ресурсы хоста | Увеличьте capacity или добавьте второй runner на другом сервере |
permission denied при обращении к docker.sock | Runner-контейнер запущен не тем пользователем | Убедитесь, что volume /var/run/docker.sock:/var/run/docker.sock смонтирован, и не меняйте user: в сервисе runner |
| OAuth редиректит на 404 | Неверный callback URL в настройках приложения | Callback должен быть точно https://ваш-домен/login, без завершающего слеша и без порта |
Runner под нагрузкой стоит держать на отдельном сервере от drone-server, чтобы сборки не съедали ресурсы у веб-интерфейса. Для этого укажите в DRONE_RPC_HOST внешний адрес сервера с drone-server вместо имени сервиса из compose и откройте порт между машинами (или пустите трафик через VPN).
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем Drone CI лучше Jenkins для небольшой команды?
Меньше эксплуатационных расходов: не нужно администрировать плагины, Java-окружение и агентов — весь стек это два Docker-образа, а конфигурация пайплайна лежит в репозитории, а не в веб-интерфейсе.
Можно ли использовать Drone с self-hosted Gitea?
Да, это одна из самых частых связок: и Drone, и Gitea легко разворачиваются в Docker Compose на одном сервере, OAuth настраивается за пять минут через раздел Applications в Gitea.
Нужен ли отдельный сервер под runner?
Не обязательно — для небольших проектов сервер и runner прекрасно живут на одной машине. Отдельный сервер под runner имеет смысл, когда сборки тяжёлые (компиляция, интеграционные тесты с базами) и не должны мешать веб-интерфейсу.
Что делать, если пайплайн собирает секреты в логи по ошибке?
Drone маскирует значения, добавленные через drone secret add, но не маскирует переменные, заданные напрямую в .drone.yml или переданные через echo внутри команды — никогда не выводите секреты явными командами вроде echo $TOKEN.
Поддерживает ли Drone мультиплатформенную сборку (arm64/amd64)?
Да, через plugins/docker с параметром platform или через отдельные пайплайны с platform.arch, но для этого runner должен уметь запускать buildx — на большинстве облачных VPS это работает из коробки при современной версии Docker.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →