23 KiB
BUNDLED_SETUP — Bundled-тираж: весь стек одним комплектом (ORCH-103)
Golden source Bundled-тиража (Type B эпика ORCH-10). Маршрут «чистый хост → работающий конвейер» для заказчика без собственной инфраструктуры: один compose-комплект (
deploy/bundled/docker-compose.yml) поднимает оркестратор + watchdog + Gitea + Plane CE, один запускscripts/bootstrap_bundle.py applyдоводит стек до рабочего состояния. Каждый шаг — исполняемая команда + явная проверка результата (Проверка: / PASS / FAIL). Хост-специфика — только плейсхолдеры<...>и$ENV_VAR. Тираж stateless: данные/задачи/секреты боевого (исходного) хоста НЕ переносятся ни на одном шаге (§12). Границы слоёв тиража (10-common vs Lite vs Bundled) —docs/operations/REPLICATION.md§1; канон Lite (своя инфраструктура Plane/Gitea) —docs/deployment/LITE_SETUP.md.
1. Рамка Bundled
Что входит в комплект (compose-проект orchestrator-bundle, одна bridge-сеть):
orchestrator(конвейер, образ собирается из этого чекаута) иorchestrator-watchdog(независимый sidecar-мониторинг);- Gitea (git-хостинг, пиннованный официальный образ);
- Plane CE — ≈ 14 контейнеров (зеркало официального selfhost-комплекта: web/space/admin/api/worker/beat-worker/migrator/live + postgres/redis/ rabbitmq/minio/proxy) — это ресурсоёмко, см. §2.
Что НЕ входит (внешние предусловия заказчика):
- Claude CLI / LLM-доступ — дистрибутив claude-code, node и аутентификация живут на хосте и пробрасываются маунтами (§8); без них стек поднимется, но конвейер не поедет;
- Telegram-боты — опциональны (§9): пусто = деградация только нотификаций;
- HTTPS/домены/reverse-proxy — вне bundle: наружу публикуются три http-порта (§2), терминирование TLS — средствами заказчика.
Bundled vs Lite: Lite (LITE_SETUP.md) подключает оркестратор к вашим
Plane/Gitea; Bundled везёт их с собой на чистых томах. Staging-контур орка в
bundle отсутствует вовсе: заказчик Type B эксплуатирует платформу для своих
проектов, а не развивает её self-hosting'ом (нужен self-hosting — маршрут Lite,
LITE_SETUP.md §9.3). Репо orchestrator в bundle-инсталляции не
регистрируется как проект.
Осознанный компромисс (TR-7): git-доступ агентов — HTTP token-remote (токен бот-юзера в конфиге локальных чекаутов, права 600); ssh-контур сознательно не вводится; порты БД/брокера/minio наружу не публикуются.
2. Требования к хосту
Linux x86_64, один хост. Минимумы проверяет preflight bootstrap до любых
мутаций (пороги — константы scripts/bootstrap_bundle.py, ниже — те же цифры;
подтверждаются замером приёмочного развёртывания):
| Ресурс | Минимум | Почему |
|---|---|---|
| RAM | 8 GB | Plane CE — ≈ 14 контейнеров (миграции и брокер прожорливы) |
| Диск | 40 GB свободно | образы стека + тома postgres/minio/gitea + данные орка |
| CPU | 4 vCPU (рекомендация) | меньше — стек поднимется, но будет медленным |
Карта публикуемых портов (дефолты; конфигурируемы в
deploy/bundled/.env, ключи BUNDLE_*):
| Порт | Ключ | Сервис |
|---|---|---|
| 8500 | BUNDLE_ORCH_PORT |
API оркестратора (/health, /queue, /metrics, вебхуки) |
| 8080 | BUNDLE_PLANE_PORT |
Plane UI (proxy) |
| 3000 | BUNDLE_GITEA_HTTP_PORT |
Gitea web/API |
Postgres/redis/rabbitmq/minio наружу не публикуются (машинный трафик — внутрисетевой сервис-DNS).
free -g # RAM ≥ 8 GB
df -h . # свободно ≥ 40 GB
nproc # ≥ 4
ss -ltn | grep -E ':(8500|8080|3000)\b' || echo "ports free"
Проверка: ресурсы не ниже минимумов и ports free — PASS. Порт занят →
смените соответствующий BUNDLE_*-ключ в §5 (или освободите порт) — иначе
preflight откажет (FAIL до мутаций, это штатно).
3. Предусловия
Софт хоста: Docker Engine + Compose v2, git, python3 (+venv), sudo у оператора.
uname -sm # Linux x86_64
docker --version && docker compose version
git --version && python3 --version
python3 -m venv --help >/dev/null && echo "venv: ok"
getent group docker # третье поле — gid, понадобится в §5 (ORCH_DOCKER_GID)
id -u && id -g # uid/gid оператора (ORCH_RUN_UID / ORCH_RUN_GID)
Проверка: все команды отвечают без ошибок, gid группы docker известен — PASS; что-то отсутствует — FAIL (доставьте пакет средствами дистрибутива).
4. Получение кода
Переносится только код — чекаут репо orchestrator (норматив §12).
git clone <ORCHESTRATOR_GIT_URL> <путь-чекаута>
cd <путь-чекаута>
ls deploy/bundled/docker-compose.yml deploy/bundled/.env.example \
scripts/bootstrap_bundle.py scripts/gen_secrets.py scripts/onboard_project.py
Проверка: все пять файлов на месте — PASS. Канал дистрибуции
(<ORCHESTRATOR_GIT_URL>) согласуйте с поставщиком платформы (как в
LITE_SETUP.md §3).
5. Секреты
Все секреты инсталляции выпускаются заново на месте (§12): webhook-секреты —
scripts/gen_secrets.py, внутренние креды Plane/Gitea-стека — генерирует
bootstrap (в репо — только пустые плейсхолдеры, ни одного дефолтного пароля).
5.1. Конфиг bundle-инфры.
cd <путь-чекаута>
cp deploy/bundled/.env.example deploy/bundled/.env
chmod 600 deploy/bundled/.env
# заполнить НЕсекретные ключи: BUNDLE_PUBLIC_HOST (IP/имя хоста для браузера),
# карту портов BUNDLE_* (§2), ORCH_RUN_UID/ORCH_RUN_GID (из §3),
# ORCH_DOCKER_GID (getent group docker, §3), пути Claude CLI (§8).
Проверка:
docker compose -f deploy/bundled/docker-compose.yml config --quiet && echo "config: PASS"
config: PASS — интерполяция согласована; ошибка — FAIL (опечатка в
deploy/bundled/.env).
5.2. Секрет-значения (пустые ключи deploy/bundled/.env и корневого .env)
заполнит bootstrap_bundle.py apply (§7): webhook-секреты — субпроцессом
gen_secrets.py, креды postgres/rabbitmq/minio/SECRET_KEY Plane и пароль
админ-бота Gitea — stdlib-генератором. Значения не печатаются (только имена
ключей); повторный запуск не перетирает существующие секреты (явная
регенерация — флаг --force-secrets, допустим только ДО первого запуска стека).
grep -cE '^(POSTGRES_PASSWORD|SECRET_KEY|RABBITMQ_DEFAULT_PASS|MINIO_ROOT_PASSWORD|GITEA_ADMIN_PASSWORD)=$' \
deploy/bundled/.env
Проверка: до §7 счётчик 5 (пустые плейсхолдеры) — PASS; после §7 — 0.
6. Запуск bundle-compose
Одна команда поднимает весь стек (≈ 16 контейнеров; первый запуск тянет образы и гоняет миграции Plane — это минуты, не секунды).
docker compose -f deploy/bundled/docker-compose.yml up -d
docker compose -f deploy/bundled/docker-compose.yml ps
Проверка: все сервисы в состоянии Up/Up (healthy); migrator —
Exited (0) (одноразовая миграция) — PASS. Контейнер в рестарт-цикле — FAIL
(§14). Шаг идемпотентен; можно пропустить — bootstrap_bundle.py apply выполнит
up -d сам (§7).
7. Bootstrap
Доводка «одним запуском»: preflight → секреты → up/готовность → init Gitea
(полностью автоматом: админ-бот + API-токен) → init Plane → онбординг
sandbox-проекта строго кирпичом onboard_project.py (22 канонических
статуса, включая fail-closed Confirm Deploy и STOP, лейблы,
репо+webhook — golden source docs/operations/ONBOARDING.md §1) → git-доступ
агентов → сборка .env/.env.watchdog → health.
python3 scripts/bootstrap_bundle.py # план + preflight-диагностика (ноль мутаций)
python3 scripts/bootstrap_bundle.py apply # полный прогон
Manual-step чекпоинты Plane CE (API первичной инициализации в CE нет; каждый чекпоинт: точная инструкция → подтверждение → верификация результата API-пробой, молчаливый пропуск запрещён):
- instance setup — открыть Plane UI, зарегистрировать первого пользователя (станет администратором инстанса);
- workspace — создать workspace, ввести его slug в bootstrap;
- API-токен — Workspace Settings → API tokens, вставить значение в
bootstrap (ввод скрыт; уходит в
ORCH_PLANE_API_TOKEN); - workspace-webhook — bootstrap регистрирует сам (запись в Postgres
инсталляции, путь Б канона
LITE_SETUP.md§5.4) и проверяет; при отказе — честный ручной шаг с той же проверкой; - порядок статусов на доске — drag-and-drop по отчёту onboard
(
docs/operations/ONBOARDING.md).
Exit-коды: 0 — успех; 2 — остановка на manual-step/предусловии (выполните
шаг и перезапустите apply — завершённые шаги пропускаются, повторный запуск
безопасен); 1 — ошибка. Пароль админ-бота Gitea — ключ GITEA_ADMIN_PASSWORD
в deploy/bundled/.env (права 600; вход в UI Gitea под
GITEA_ADMIN_USERNAME).
Проверка:
python3 scripts/bootstrap_bundle.py verify && echo "bootstrap: PASS"
verify зелёный (health/queue/metrics + onboard verify) — PASS; exit 2 —
остались ручные пункты отчёта; exit 1 — FAIL (§14).
8. LLM (claude CLI)
Канон — LITE_SETUP.md §7 (полностью применим; не дублируется). Кратко: на
хост ставятся claude-code + node, выполняется интерактивный логин CLI; пути
прописываются в deploy/bundled/.env (это источники маунтов контейнера орка):
ORCH_HOST_CLAUDE_CODE_DIR, ORCH_HOST_NODE_BIN, ORCH_HOST_CLAUDE_DIR,
ORCH_HOST_CLAUDE_JSON.
claude --version
docker compose -f deploy/bundled/docker-compose.yml exec orchestrator /usr/bin/claude --version
Проверка: обе команды печатают версию — PASS; вторая падает — пути в
deploy/bundled/.env не указывают на фактические каталоги хоста (§14.4).
9. Telegram
Канон — LITE_SETUP.md §8 (два независимых бота, C-1: токен орка для watchdog
переиспользовать запрещено). Ключи орка (ORCH_TELEGRAM_BOT_TOKEN,
ORCH_TELEGRAM_CHAT_ID) — в корневой .env; ключи watchdog
(WATCHDOG_TG_BOT_TOKEN, WATCHDOG_TG_CHAT_ID) — только в .env.watchdog
(файл-носитель, LITE_SETUP.md §4.3). Шаг опционален: пустые токены =
деградация только нотификаций.
grep -E '^ORCH_TELEGRAM_(BOT_TOKEN|CHAT_ID)=' .env
grep -E '^WATCHDOG_TG_(BOT_TOKEN|CHAT_ID)=' .env.watchdog
docker compose -f deploy/bundled/docker-compose.yml up -d orchestrator orchestrator-watchdog
Проверка: ключи заполнены и контейнеры пересозданы → тестовое сообщение от
обоих ботов (getMe — команды в LITE_SETUP.md §8) — PASS; пусто — осознанный
PASS без нотификаций.
10. Онбординг следующих проектов
Sandbox-проект создал bootstrap (§7). Каждый следующий проект заказчика —
штатный runbook docs/operations/ONBOARDING.md поверх bundle-инсталляции; для
команд из чекаута: Plane/Gitea доступны на localhost-портах §2, webhook-URL —
in-network http://orchestrator:8500/webhook/gitea.
. .venv/bin/activate # venv создан bootstrap'ом (§7)
python3 scripts/onboard_project.py plan \
--name "<имя проекта>" --repo <repo> --prefix <PREFIX> \
--stack "<стек>" --test-cmd "<команда тестов>" \
--prod-port <порт-прода> --staging-port <порт-staging> \
--webhook-url http://orchestrator:8500/webhook/gitea
# план устроил → apply → verify (как в LITE_SETUP.md §10), затем:
# строку ORCH_PROJECTS_JSON из отчёта — в .env и пересоздать орк:
docker compose -f deploy/bundled/docker-compose.yml up -d --force-recreate orchestrator
Проверка: verify зелёный; GET /queue отвечает после пересоздания — PASS.
11. Smoke
Процедура — чек-лист docs/operations/REPLICATION.md §4 (шаги 0–5; шаг 6 «до
done» — опционально) поверх bundle-инсталляции, без форка. Минимальный сигнал
«конвейер доехал»: issue в sandbox-проекте Plane → статус To Analyse →
артефакты 01–04 в ветке задачи.
curl -fsS http://127.0.0.1:8500/health
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -30
curl -fsS http://127.0.0.1:8500/metrics | python3 -m json.tool | head -10
# создать issue в Plane (порт 8080) → перевести в «To Analyse», затем:
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -40 # job появился
git -C deploy/bundled/repos/sandbox fetch origin
git -C deploy/bundled/repos/sandbox ls-tree --name-only origin/<ветка-задачи> "docs/work-items/<id>/"
Проверка: оба направления связности живы — job в /queue (Plane→орк
доехал), ls-tree показывает 01-brd.md … 04-test-plan.yaml (орк→Gitea
пишет; Gitea→орк события идут) — PASS. Любой шаг FAIL → тираж FAIL: соберите
docker compose -f deploy/bundled/docker-compose.yml logs --tail 100 orchestrator
и снапшот GET /queue, разбор — §14. (Порты замените, если меняли BUNDLE_*.)
12. Stateless-проверка
Нормативно: данные/задачи/секреты/БД боевого (исходного) хоста НЕ
переносятся (зеркало docs/operations/REPLICATION.md §5). Все тома bundle
созданы заново при первом up; секреты — только свежевыпущенные (§5); в
Plane/Gitea инсталляции нет чужих задач/репо/пользователей.
docker volume ls --format '{{.Name}}' | grep '^orchestrator-bundle' # только тома этой инсталляции
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool # счётчики нулевые
Проверка: в /queue нулевые счётчики и ни одной чужой задачи (никаких
work-item исходного хоста) — PASS. Чужая задача/перенесённый файл БД — FAIL:
инсталляция собрана не stateless, выполните полный сброс (§13) и повторите.
13. Остановка и полный сброс
Teardown — только эта документированная процедура (в bootstrap delete-режима сознательно нет, ADR-001 D9).
Остановка (обратимая):
docker compose -f deploy/bundled/docker-compose.yml down
Проверка: docker compose -f deploy/bundled/docker-compose.yml ps пуст;
тома целы (docker volume ls | grep orchestrator-bundle) — PASS.
Полный сброс (НЕОБРАТИМО — удаляет все данные Plane/Gitea/орка):
docker compose -f deploy/bundled/docker-compose.yml down -v
rm -rf deploy/bundled/data deploy/bundled/repos
rm -f deploy/bundled/.env .env .env.watchdog
Проверка: docker volume ls --format '{{.Name}}' | grep -c '^orchestrator-bundle'
→ 0; live-конфигов нет — PASS (хост чист, можно разворачивать заново с §5).
14. Траблшутинг
Формат: симптом → диагностика → лечение.
14.1. Webhook не доходит (issue в Plane есть, job в /queue нет).
docker compose -f deploy/bundled/docker-compose.yml logs --tail 50 orchestrator | grep -i "webhook\|signature"
docker compose -f deploy/bundled/docker-compose.yml exec -T plane-db \
psql -U plane -d plane -c "SELECT url, is_active FROM webhooks;"
Лечение: (а) нет строки webhook → §7 чекпоинт 4; (б) URL не
http://orchestrator:8500/webhook/plane → исправьте на in-network URL;
(в) 401/HMAC → секрет в Plane обязан байт-в-байт совпадать с
ORCH_PLANE_WEBHOOK_SECRET корневого .env. Для Gitea-направления проверьте
Recent Deliveries в настройках hook'а репо; помните про
GITEA__webhook__ALLOWED_HOST_LIST=orchestrator в bundle-compose (без него
Gitea молча режет вебхуки в приватные адреса).
14.2. Не хватает RAM / OOM (контейнеры Plane в рестарт-цикле).
free -g && docker stats --no-stream | head -20
docker compose -f deploy/bundled/docker-compose.yml ps
Лечение: минимум §2 (8 GB; Plane ≈ 14 контейнеров). Меньше — добавьте RAM/swap; preflight bootstrap отказывает заранее именно поэтому.
14.3. Порт занят (up падает с bind error).
ss -ltnp | grep -E ':(8500|8080|3000)\b'
Лечение: смените BUNDLE_ORCH_PORT/BUNDLE_PLANE_PORT/BUNDLE_GITEA_HTTP_PORT
в deploy/bundled/.env и повторите up/bootstrap.
14.4. claude не найден / агент падает на старте.
docker compose -f deploy/bundled/docker-compose.yml exec orchestrator /usr/bin/claude --version
ls "$(grep '^ORCH_HOST_CLAUDE_CODE_DIR=' deploy/bundled/.env | cut -d= -f2)"
Лечение: пути ORCH_HOST_* в deploy/bundled/.env обязаны указывать на
фактические каталоги хоста; креды CLI читаемы uid'ом ORCH_RUN_UID (канон —
LITE_SETUP.md §7/§13.3); после правки — up -d --force-recreate orchestrator.
14.5. Миграции Plane не завершились (bootstrap падает на ожидании).
docker compose -f deploy/bundled/docker-compose.yml logs --tail 50 migrator plane-db
docker compose -f deploy/bundled/docker-compose.yml ps plane-db plane-mq plane-redis
Лечение: чаще всего — нехватка RAM/диска (§14.2) или невыпущенные секреты
(пустой POSTGRES_PASSWORD → postgres не стартует; прогоните §7, который
заполняет креды ДО up). После лечения — повторный apply (идемпотентен).
14.6. PR задачи не мержится / HOLD. Branch protection на main в Gitea
НЕ включать — норматив LITE_SETUP.md §6.4 (ломает PR-merge API
merge-актора); bundle-Gitea конфигурируется тем же правилом.
curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" \
"http://127.0.0.1:3000/api/v1/repos/<owner>/<repo>/branch_protections" | python3 -m json.tool
Лечение: непустой список правил → удалить (канон LITE_SETUP.md §6.4/§13.7).
Golden source Bundled-тиража (ORCH-103, ADR-001 D10). Норматив сопровождения
(NFR-5): меняешь шаги Bundled-тиража (состав bundle-compose, ключи
deploy/bundled/.env.example, шаги bootstrap, smoke) → обнови этот док В ТОМ ЖЕ
PR. Полноту и гигиену держит tests/test_bundled_setup_doc.py; кирпичи-каноны:
LITE_SETUP.md (§5–§8 — подключения), docs/operations/ONBOARDING.md (статусы
§1, онбординг), docs/operations/REPLICATION.md (карта env §2, секреты §3,
smoke §4), deploy/bundled/.env.example + .env.example /
.env.watchdog.example (каноны ключей).