Files
orchestrator/docs/deployment/BUNDLED_SETUP.md

437 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).
```bash
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 у оператора.
```bash
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).
```bash
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-инфры.**
```bash
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).
```
**Проверка:**
```bash
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`, допустим только ДО первого запуска стека).
```bash
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 — это минуты, не секунды).
```bash
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.
```bash
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`).
**Проверка:**
```bash
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`.
```bash
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). Шаг опционален: пустые токены =
деградация только нотификаций.
```bash
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`.
```bash
. .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**
артефакты `01``04` в ветке задачи.
```bash
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 инсталляции нет чужих задач/репо/пользователей.
```bash
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).
**Остановка (обратимая):**
```bash
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/орка):**
```bash
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` нет).**
```bash
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 в рестарт-цикле).**
```bash
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).**
```bash
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 не найден / агент падает на старте.**
```bash
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 падает на ожидании).**
```bash
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 конфигурируется тем же правилом.
```bash
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` (каноны ключей).*