Files
orchestrator/docs/deployment/BUNDLED_SETUP.md

23 KiB
Raw Permalink Blame History

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); migratorExited (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-пробой, молчаливый пропуск запрещён):

  1. instance setup — открыть Plane UI, зарегистрировать первого пользователя (станет администратором инстанса);
  2. workspace — создать workspace, ввести его slug в bootstrap;
  3. API-токен — Workspace Settings → API tokens, вставить значение в bootstrap (ввод скрыт; уходит в ORCH_PLANE_API_TOKEN);
  4. workspace-webhook — bootstrap регистрирует сам (запись в Postgres инсталляции, путь Б канона LITE_SETUP.md §5.4) и проверяет; при отказе — честный ручной шаг с той же проверкой;
  5. порядок статусов на доске — 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 (шаги 05; шаг 6 «до done» — опционально) поверх bundle-инсталляции, без форка. Минимальный сигнал «конвейер доехал»: issue в sandbox-проекте Plane → статус To Analyse → артефакты 0104 в ветке задачи.

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.md04-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 (каноны ключей).