Stack 26 (books-ops-mcp) in-place PUT в host-level compose без container churn. Stack 43 (bookva-ops-mcp) deleted. 2 Gitea secrets revoked. New canonical: `host-stacks/books-vds/ops-mcp.compose.yml` (management-plane, manual Portainer updates как traefik/portainer). Design locks: location=.admin/host-stacks, deploy=manual Portainer, MariaDB scope=slovo's only (Option A), config path unchanged. Companion commits: `victor/books ae3ab14`, `victor/bookva-overlay 50f5bbb`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
93 lines
10 KiB
Markdown
93 lines
10 KiB
Markdown
# books-ops-mcp-host-promote
|
||
|
||
Promote `books-docker-proxy-ro` + `books-ops-mcp` из slovo's stack в host-level stack на books VDS. Дропнуть idle-stub `bookva-ops-mcp`. Идеология: **books VDS = наш host, slovo/bookva = клиентские tenant-стеки**; ops-mcp видит host-wide docker socket → один экземпляр на VDS, не per-tenant.
|
||
|
||
## Контекст / триггер
|
||
|
||
В сессии 2026-05-26 auto-deploy `tenant=all` упал на `bookva-ops-mcp not running (state=restarting)`. Hot-fix — добавлен `command: ["tail","-f","/dev/null"]` в `bookva-overlay/deploy/ops-mcp.compose.yml` (commit `437d024`), контейнер стал idle-stub без proxy/creds → deploy зелёный, MCP non-functional у bookva tenant'а. User отверг этот long-term shape: "books VDS — наш хост, slovo и bookva — клиентские стэки приложений".
|
||
|
||
Current legacy layout (наследие времени когда books-* совпадало и с host, и с единственным tenant'ом):
|
||
- `books/deploy/ops-mcp.compose.yml` содержит **два** сервиса: `books-docker-proxy-ro` (host-level docker socket gateway) + `books-ops-mcp` (host-wide observability MCP). Deploy'ится через CI как часть slovo-tenant pipeline.
|
||
- `bookva-overlay/deploy/ops-mcp.compose.yml` — idle-stub без функциональности, deployed via tenant=bookva pipeline.
|
||
|
||
## Target state
|
||
|
||
- Новый host-level stack (имя tentative — `books-ops`) с `books-docker-proxy-ro` + `books-ops-mcp`. Naming `books-*` сохраняется потому что **books = имя хоста**, не tenant.
|
||
- Stack живёт в Portainer на books VDS как management-plane (как `traefik` + `portainer` сейчас — anti-pattern по project-discipline portainer rule, но user уже сделал исключение для management plane).
|
||
- Deploy pipeline отдельный от tenant deploy (либо `deploy-host.yml` в `victor/books`, либо manual через Portainer как traefik/portainer).
|
||
- `bookva-ops-mcp` stack удалён из Portainer + secret `PORTAINER_STACK_ID_BOOKVA_OPS_MCP` отозван из Gitea + `bookva-overlay/deploy/ops-mcp.compose.yml` удалён + `ops-mcp` убран из tenant=bookva service list в `books/.gitea/workflows/deploy.yml`.
|
||
- Slovo's `books/deploy/ops-mcp.compose.yml` либо удалён, либо превращён в pointer на host-level (slovo больше не owns эти контейнеры).
|
||
|
||
## Open design questions
|
||
|
||
1. **Где жить host-level compose?**
|
||
- (a) `.admin/host-stacks/books-vds/ops-mcp.compose.yml` (private ops-репо, единая точка для host-level VDS configs).
|
||
- (b) Новый `victor/books-vds-host` репо.
|
||
- (c) `victor/books/deploy/host/ops-mcp.compose.yml` (рядом, но другой sub-pipeline).
|
||
- Recommendation: **(a)** — `.admin/` уже private + ops-flavored, host configs естественно ложатся рядом с `.tasks/` и `.wiki/concepts/portainer-stack-management-vds.md`.
|
||
|
||
2. **Как deploy'ить?**
|
||
- Через CI: новый `deploy-host.yml` workflow в `.admin/` (или в books). Trigger: workflow_dispatch + push на host-stacks/.
|
||
- Manual через Portainer UI: matches current `traefik`/`portainer` exception. User уже зафиксировал исключение из portainer-rule для management plane.
|
||
- Recommendation: **manual** — management plane уже исключение, не плодим CI poll специально для одного stack'а.
|
||
|
||
3. **MARIADB_PASSWORD scope.** Сейчас slovo's books-ops-mcp получает `MARIADB_PASSWORD` (slovo's books-db root). Host-level ops-mcp должен видеть обе tenant DBs?
|
||
- Если yes: env превращается в `SLOVO_MARIADB_PASSWORD` + `BOOKVA_MARIADB_PASSWORD`, MCP lib/server.js должен поддержать обе connection strings (probably TENANT param per query).
|
||
- Если no (ops-mcp работает только с docker, без DB): drop `MARIADB_PASSWORD` env вообще, MCP не делает DB-queries.
|
||
- Check actual usage in `books-ops-mcp` package source — нужно прочитать `victor/books/packages/ops-mcp/` чтобы понять что server.js делает с MARIADB_PASSWORD.
|
||
|
||
4. **Config volume.** Сейчас mount `/opt/books/books-ops-mcp/config/default.json` → MCP config. Host-level — тот же path или `/opt/books-vds-ops/config/default.json` (если хочется убрать books- prefix как tenant-namespace)?
|
||
|
||
5. **Какие ещё host-level контейнеры спрятаны в slovo stack?** Полезно одним проходом аудит сделать. Текущие host-level (по моему знанию): `traefik`, `portainer`. Возможные кандидаты на промоушн: `ntfy` (push.kzntsv.site — single endpoint per VDS), backup-cron'ы. См. [infra-inventory.md](infra-inventory.md) — частично пересекается.
|
||
|
||
## Acceptance
|
||
|
||
- Новый stack (`books-ops` или similar) running на books VDS, contains `books-docker-proxy-ro` + `books-ops-mcp`, healthy.
|
||
- `bookva-ops-mcp` stack удалён из Portainer, secret отозван, compose-файл удалён из bookva-overlay.
|
||
- `ops-mcp` service исключён из tenant=bookva pipeline в `books/.gitea/workflows/deploy.yml` (либо tenant-loop, либо service-list).
|
||
- Slovo's `books/deploy/ops-mcp.compose.yml` либо удалён, либо ясно помечен как deprecated pointer. Slovo deploy pipeline больше не trying to deploy ops-mcp.
|
||
- `ssh + docker exec -i books-ops-mcp node lib/server.js` продолжает работать (slovo's caller path не сломан, имя контейнера `books-ops-mcp` сохранено).
|
||
- Smoke: MCP tool calls (например `mcp__books-ops__ops_docker_ps`) видят все tenant containers (`books-db`, `bookva-db`, `slovo-db` etc).
|
||
|
||
## Closure note (2026-05-26)
|
||
|
||
🟢 Closed одну сессию следом за заведением. **Решение design Qs:**
|
||
- **Q1 host compose location:** `.admin/host-stacks/books-vds/ops-mcp.compose.yml` (canonical, private ops-репо).
|
||
- **Q2 deploy mechanism:** manual через Portainer UI (как traefik/portainer — management plane exception).
|
||
- **Q3 MARIADB_PASSWORD scope:** Option A — host instance видит только slovo's books-db + slovo's Mongo. ops-mcp source connects к ОДНОМУ MariaDB pool и ОДНОМУ MongoClient; multi-tenant DB observability — design call отложен (если нужно: extend lib + separate pools per-tenant, либо drop DB tools оставив только docker).
|
||
- **Q4 config volume:** path unchanged (`/opt/books/books-ops-mcp/config/default.json` на host'е). Books-prefix остался — host = books VDS.
|
||
- **Q5 audit прочих host-level кандидатов:** punt, отдельная задача (kicks off `infra-inventory`).
|
||
|
||
**Executed:**
|
||
- ✅ Read `books/packages/ops-mcp/{server,mariadb,mongo}.js` + `config/{default,custom-environment-variables}.json` — locked design Qs.
|
||
- ✅ Created `.admin/host-stacks/books-vds/ops-mcp.compose.yml` (host-level canonical). Image hardcoded `:master` (manual update, не env-var).
|
||
- ✅ Portainer stack 26 (books-ops-mcp) **in-place PUT** с новой compose. Containers `books-docker-proxy-ro` + `books-ops-mcp` остались running без recreate. Env `MARIADB_PASSWORD` preserved.
|
||
- ✅ Portainer stack 43 (bookva-ops-mcp) **deleted**. Container bookva-ops-mcp gone (404).
|
||
- ✅ `victor/books ae3ab14`: deploy.yml drop ops-mcp service + STACK_ID_*_OPS_MCP envs + healthcheck case; `deploy/ops-mcp.compose.yml` deleted.
|
||
- ✅ `victor/bookva-overlay 50f5bbb`: `deploy/ops-mcp.compose.yml` deleted (idle-stub no longer needed).
|
||
- ✅ Gitea secrets revoked: `PORTAINER_STACK_ID_BOOKVA_OPS_MCP` + `PORTAINER_STACK_ID_OPS_MCP`. `SLOVO_OPS_MCP` was never set (fallback chain used OPS_MCP).
|
||
- ✅ Smoke: `books-ops-mcp State=running`, `books-docker-proxy-ro State=running`, `bookva-ops-mcp` 404.
|
||
|
||
**Open follow-ups (если возникнет потребность):**
|
||
- Multi-tenant DB observability через MCP — extend lib (per-tenant pool array, TENANT param на queries) ИЛИ drop DB tools оставив только docker.
|
||
- Stack name "books-ops-mcp" → "books-ops" в Portainer (cosmetic; rename требует recreate, не делал).
|
||
- Audit прочих host-level кандидатов (ntfy? backup-cron?) — через `infra-inventory`.
|
||
|
||
**Status:** closed 2026-05-26
|
||
**Where I stopped:** all 10 steps done; Stack id=26 промоутен в host-level семантически без container churn; bookva-side полностью удалён.
|
||
**Next action:**
|
||
1. Прочитать `victor/books/packages/ops-mcp/` источник — понять что MCP реально делает с `MARIADB_PASSWORD` (resolves design Q3).
|
||
2. Принять design decisions Q1–Q5 (recommendation drafts выше).
|
||
3. Создать host-level compose в выбранном месте.
|
||
4. В Portainer: создать новый stack `books-ops` (manual upload compose) → start → verify `docker exec books-ops-mcp` works.
|
||
5. В Portainer: stop + delete `bookva-ops-mcp` stack.
|
||
6. В Gitea secrets: отозвать `PORTAINER_STACK_ID_BOOKVA_OPS_MCP`.
|
||
7. В `victor/books/.gitea/workflows/deploy.yml`: убрать `ops-mcp` из tenant=bookva service list (или из `services="api web job-scheduler ops-mcp"` глобально + добавить host-level deploy в отдельный workflow).
|
||
8. В `victor/bookva-overlay`: `rm deploy/ops-mcp.compose.yml`.
|
||
9. В `victor/books`: `rm deploy/ops-mcp.compose.yml` (или превратить в README pointer).
|
||
10. Manual smoke: `mcp__books-ops__ops_docker_ps` видит bookva-* containers.
|
||
|
||
**Blocker:** —
|
||
**Branch:** master
|
||
<!-- created-by: vitya / 2026-05-26 / trigger: deploy run#437/439 fail + user ideology clarification «books VDS = host, slovo/bookva = клиентские стэки» -->
|