Files
orchestrator/docs/operations/REPLICATION.md
claude-bot ffe02df484
Some checks failed
CI / test (push) Failing after 57s
CI / test (pull_request) Failing after 56s
developer(ET): auto-commit from developer run_id=627
2026-06-11 01:53:21 +03:00

156 lines
13 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.
# REPLICATION — тираж платформы на новую инфраструктуру (ORCH-101)
> RUNBOOK фундамента тиража (эпик ORCH-10, слой **10-common**). Как развернуть
> оркестратор на чужом хосте **без правки кода**: переменные → секреты → smoke.
> Тираж **stateless**: данные/БД/секреты боевого хоста НЕ переносятся ни на одном
> шаге — на целевой инфре всё создаётся заново.
---
## 1. Границы: 10-common vs Lite vs Bundled
| Слой | Что это | Статус |
|------|---------|--------|
| **10-common** (этот док) | фундамент: все хост-значения параметризованы (env/конфиг), секреты выпускаются заново, smoke-процедура с PASS/FAIL | ✅ ORCH-101 |
| **Type A — Lite** | инструкция «поставь Plane+Gitea сам, подключи оркестратор» поверх 10-common | ✅ ORCH-102 — [`docs/deployment/LITE_SETUP.md`](../deployment/LITE_SETUP.md) |
| **Type B — Bundled** | комплект «всё в одном» (Plane+Gitea+оркестратор) поверх 10-common | ✅ ORCH-103 — [`docs/deployment/BUNDLED_SETUP.md`](../deployment/BUNDLED_SETUP.md) |
Этот док НЕ описывает установку Plane/Gitea — только параметризацию, секреты и
smoke самого оркестратора (анти-скоуп-крип Р-5).
### Платформенные конвенции тиража (нормативно, ADR-001 D3/D4)
- **Репо платформы обязан называться `orchestrator`.** Имя — узел безопасности
(`SELF_HOSTING_REPO`, на него завязаны все `*_repos`-leaf'ы «empty CSV →
self-hosting only»); оно сознательно НЕ конфигурируется.
- Имена compose-сервисов/профиля (`orchestrator`, `orchestrator-staging`,
`orchestrator-watchdog`, профиль `staging`) — константы платформы.
- Контейнерные пути (`/app/data`, `/repos`, `/opt/claude-code`) — layout
контейнера, не хост-значения; не параметризуются.
---
## 2. Карта переменных нового хоста
Принцип (ADR-001 D1): **дефолт каждой переменной = боевое значение текущего
хоста** — пустой `.env` ⇒ поведение байт-в-байт; на новом хосте задаёшь только
то, что отличается. Одно env-имя = один факт: pydantic `Settings` читает имя из
`env_file`, compose-интерполяция `${VAR:-default}` — из **`.env` проекта/shell**
(⚠️ НЕ из `env_file` сервиса: `.env.staging` на интерполяцию не влияет —
значения для маунтов/uid/портов живут в `.env`).
### 2.1. Хост-параметризация (новое в ORCH-101)
| Переменная | Дефолт | Назначение |
|-----------|--------|------------|
| `ORCH_AGENT_HOME_DIR` | `/home/slin` | HOME всех акторов (агенты, finalizer, monitor) + таргет маунтов `.claude`/`.claude.json`/`.ssh` + `ARG APP_HOME` (группа ORCH-040 двигается вместе) |
| `ORCH_AGENT_GIT_NAME` | `claude-bot` | git-имя коммитов агентов |
| `ORCH_GIT_EMAIL_DOMAIN` | `mva154.local` | домен git-email всех акторов (`claude-bot@…`, `deploy-finalizer@…`, `post-deploy-monitor@…`) |
| `ORCH_STAGING_PORT` | `8501` | порт staging; читают `image_freshness` И compose `command:`; guard: совпадение с прод-портом → отказ fail-closed (ORCH-058 AC-9) |
| `ORCH_HOST_REPOS_DIR` | `/home/slin/repos` | каталог репозиториев на хосте (источник маунта `/repos`) |
| `ORCH_HOST_CLAUDE_DIR` | `/home/slin/.claude` | источник маунта `~/.claude` |
| `ORCH_HOST_CLAUDE_JSON` | `/home/slin/.claude.json` | источник маунта `~/.claude.json` |
| `ORCH_HOST_SSH_DIR` | `/home/slin/.orchestrator-ssh` | источник маунта ssh-ключей (`→ $HOME/.ssh:ro`) |
| `ORCH_HOST_CLAUDE_CODE_DIR` | `/usr/lib/node_modules/@anthropic-ai/claude-code` | дистрибутив claude-code на хосте |
| `ORCH_HOST_NODE_BIN` | `/usr/bin/node` | бинарь node на хосте |
| `ORCH_RUN_UID` / `ORCH_RUN_GID` | `1000` / `1000` | uid:gid контейнера (`user:` + `ARG APP_UID/APP_GID`); = uid владельца `ORCH_HOST_REPOS_DIR` (ORCH-040) |
| `ORCH_DOCKER_GID` | `999` | gid группы docker хоста (`group_add`, «МИНА 1» — обязателен для docker.sock); узнать: `getent group docker` |
| `ORCH_DEPLOY_PROD_TARGET_PORT` | `8500` | (реюз) прод-порт; интерполируется в `command:` прод-сервиса |
| `ORCH_DEPLOY_SSH_USER` / `ORCH_DEPLOY_HOST_REPO_PATH` | `slin` / `/home/slin/repos/orchestrator` | (реюз) ssh-юзер хука и чекаут платформы на хосте; `REPO=` передаётся хуку явно из конфига |
### 2.2. Обязательные ключи идентичности нового хоста
| Переменная | Где взять |
|-----------|-----------|
| `ORCH_PLANE_API_URL` / `ORCH_PLANE_WEB_URL` / `ORCH_PLANE_WORKSPACE_SLUG` | инсталляция Plane целевого хоста |
| `ORCH_GITEA_URL` / `ORCH_GITEA_PUBLIC_URL` / `ORCH_GITEA_OWNER` | инсталляция Gitea целевого хоста |
| `ORCH_PROJECTS_JSON` | **обязателен на новом хосте**: встроенный fallback (`src/projects.py`) несёт Plane-UUID *исходного* хоста — чужие UUID безвредны (не сматчатся), но без своего реестра конвейер не увидит проекты. Сгенерировать: `scripts/onboard_project.py apply` печатает merged-значение |
| Когерентность портов | сменил прод-порт → синхронно `ORCH_DEPLOY_PROD_TARGET_PORT``WATCHDOG_METRICS_URL``ORCH_POST_DEPLOY_BASE_URL` |
Полный справочник всех остальных флагов — `.env.example` (канон) и
`docs/operations/INFRA.md` (карта env).
---
## 3. Секреты нового хоста (FR-4 / AC-5)
**Нормативно: боевые секреты текущего хоста НЕ копируются ни на одном шаге.**
Для нового хоста всегда выпускается новый комплект.
### 3.1. Генерация локальных webhook-секретов
```bash
python3 scripts/gen_secrets.py # печать .env-фрагмента в stdout
python3 scripts/gen_secrets.py --write # создать .env (существующий → отказ exit=2)
python3 scripts/gen_secrets.py --write --force # перезапись только явно
```
Скрипт stdlib-only (`secrets.token_hex(32)` — 32 байта энтропии); повторный
запуск даёт другие значения; существующий `.env` **никогда не перезаписывается
молча**.
| Секрет | Генерируется | Куда вписать |
|--------|--------------|--------------|
| `ORCH_PLANE_WEBHOOK_SECRET` | локально (gen_secrets) | `.env` + настройка webhook в Plane (см. `SETUP_WEBHOOKS.md`) |
| `ORCH_GITEA_WEBHOOK_SECRET` | локально (gen_secrets) | `.env` + webhook Gitea (создаёт `onboard_project.py apply`) |
### 3.2. Чек-лист внешних токенов
| Секрет | Где выпустить | Куда вписать | Как проверить |
|--------|---------------|--------------|---------------|
| `ORCH_PLANE_API_TOKEN` | Plane UI → Workspace Settings → API tokens | `.env` | `curl -H "X-API-Key: $TOKEN" $ORCH_PLANE_API_URL/api/v1/workspaces/<slug>/projects/` → 200 |
| `ORCH_PLANE_BOT_*` (7, опциональны) | Plane UI: bot-аккаунты per-агент; пусто → fallback на `ORCH_PLANE_API_TOKEN` | `.env` | комментарий в Plane от имени бота |
| `ORCH_GITEA_TOKEN` | Gitea UI → Settings → Applications → Generate Token (scope: repo, admin:repo_hook) | `.env` | `curl -H "Authorization: token $TOKEN" $ORCH_GITEA_URL/api/v1/user` → 200 |
| `ORCH_TELEGRAM_BOT_TOKEN` | BotFather (`/newbot`) | `.env` | `curl https://api.telegram.org/bot$TOKEN/getMe` → ok |
| `ORCH_TELEGRAM_CHAT_ID` (несекретный) | id чата оператора | `.env` | тестовое сообщение |
| `WATCHDOG_TG_BOT_TOKEN` / `WATCHDOG_TG_CHAT_ID` | отдельный бот sidecar-watchdog (ORCH-100, независимый канал) | `.env.watchdog` | алерт от sidecar |
### 3.3. Полнота
`.env.example` — канон 100% ключей старта (секретные значения — только
плейсхолдеры). Состав вывода `gen_secrets.py` сверяется с `.env.example`
тестом (`tests/test_secrets_gen.py`).
---
## 4. Smoke-верификация тиража (FR-5 / AC-3)
Процедура «инстанс → тестовый проект → тестовая задача → конвейер доехал» из
существующих кирпичей; **каждый шаг имеет явный PASS/FAIL**. Итог — однозначный
вердикт: все шаги PASS ⇒ тираж PASS; любой шаг FAIL ⇒ тираж FAIL (собери логи
контейнера `docker logs orchestrator` и снапшот `GET /queue` и разбирайся).
Воспроизводимость без нового железа: процедура прогоняется на текущей инфре —
staging-песочница (порт `ORCH_STAGING_PORT`, дефолт 8501) + sandbox-проект.
Stateless: ни один шаг не предполагает перенос данных/БД/секретов.
| # | Шаг | Команда | PASS | FAIL |
|---|-----|---------|------|------|
| 0 | Конфиг резолвится | `docker compose config` | резолв без ошибок; при пустом env значения = боевым дефолтам | ошибка интерполяции / неожиданные значения |
| 1 | Инстанс жив | `curl -fsS http://127.0.0.1:<port>/health` | HTTP 200, `"status":"ok"` | не-200 / таймаут |
| 2 | Контракты отвечают | `curl -fsS …/queue` и `…/metrics` | JSON со штатными блоками; `/metrics``schema_version: 1` | не-JSON / 5xx |
| 3 | Тестовый проект | `python3 scripts/onboard_project.py plan``apply``verify` (sandbox) | `verify` зелёный (статусы/лейблы/repo/webhook на месте) | `verify` красный / manual-step не выполнен |
| 4 | Тестовая задача | создать issue в Plane → статус «To Analyse» | задача в БД, analyst-job виден в `GET /queue` | задача не появилась (webhook/секрет/реестр) |
| 5 | **Минимальный сигнал «конвейер доехал»** | дождаться окончания `analysis` | артефакты `01``04` в ветке задачи: `git ls-tree origin/<branch> docs/work-items/<id>/` | стадия не завершилась / артефактов нет |
| 6 | Расширенный режим (опционально) | Approved → … → Confirm Deploy | задача дошла до `done` | застряла (разбор по `GET /queue` + логам) |
> Ручной запуск deploy-хука на нестандартных портах — всегда с явными
> `REPO=`/`TARGET_PORT=` (wired-путь оркестратора передаёт их сам из конфига;
> дефолты хука — staging текущего хоста).
---
## 5. Что НЕ переносится (stateless, решение 10.06)
- БД (`data/orchestrator.db`) — создаётся пустой при первом старте;
- `.env` / `.env.staging` / `.env.watchdog` — собираются заново (§2§3);
- worktree'ы/runs/логи — рабочее состояние, не данные;
- боевые секреты — никогда (§3).
---
*RUNBOOK ORCH-101. Поддерживать при добавлении хост-переменных (карта §2 +
`.env.example` + INFRA.md в том же PR). Анти-регресс возврата хардкодов —
`tests/test_no_host_hardcodes.py`; параметризация инфра-файлов —
`tests/test_infra_parametrization.py`.*