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>
10 KiB
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. Namingbooks-*сохраняется потому что 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-mcpstack удалён из Portainer + secretPORTAINER_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
-
Где жить 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.
- (a)
-
Как deploy'ить?
- Через CI: новый
deploy-host.ymlworkflow в.admin/(или в books). Trigger: workflow_dispatch + push на host-stacks/. - Manual через Portainer UI: matches current
traefik/portainerexception. User уже зафиксировал исключение из portainer-rule для management plane. - Recommendation: manual — management plane уже исключение, не плодим CI poll специально для одного stack'а.
- Через CI: новый
-
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_PASSWORDenv вообще, MCP не делает DB-queries. - Check actual usage in
books-ops-mcppackage source — нужно прочитатьvictor/books/packages/ops-mcp/чтобы понять что server.js делает с MARIADB_PASSWORD.
- Если yes: env превращается в
-
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)? -
Какие ещё host-level контейнеры спрятаны в slovo stack? Полезно одним проходом аудит сделать. Текущие host-level (по моему знанию):
traefik,portainer. Возможные кандидаты на промоушн:ntfy(push.kzntsv.site — single endpoint per VDS), backup-cron'ы. См. infra-inventory.md — частично пересекается.
Acceptance
- Новый stack (
books-opsили similar) running на books VDS, containsbooks-docker-proxy-ro+books-ops-mcp, healthy. bookva-ops-mcpstack удалён из Portainer, secret отозван, compose-файл удалён из bookva-overlay.ops-mcpservice исключён из 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-dbetc).
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. EnvMARIADB_PASSWORDpreserved. - ✅ 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.ymldeleted. - ✅
victor/bookva-overlay 50f5bbb:deploy/ops-mcp.compose.ymldeleted (idle-stub no longer needed). - ✅ Gitea secrets revoked:
PORTAINER_STACK_ID_BOOKVA_OPS_MCP+PORTAINER_STACK_ID_OPS_MCP.SLOVO_OPS_MCPwas never set (fallback chain used OPS_MCP). - ✅ Smoke:
books-ops-mcp State=running,books-docker-proxy-ro State=running,bookva-ops-mcp404.
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:
- Прочитать
victor/books/packages/ops-mcp/источник — понять что MCP реально делает сMARIADB_PASSWORD(resolves design Q3). - Принять design decisions Q1–Q5 (recommendation drafts выше).
- Создать host-level compose в выбранном месте.
- В Portainer: создать новый stack
books-ops(manual upload compose) → start → verifydocker exec books-ops-mcpworks. - В Portainer: stop + delete
bookva-ops-mcpstack. - В Gitea secrets: отозвать
PORTAINER_STACK_ID_BOOKVA_OPS_MCP. - В
victor/books/.gitea/workflows/deploy.yml: убратьops-mcpиз tenant=bookva service list (или изservices="api web job-scheduler ops-mcp"глобально + добавить host-level deploy в отдельный workflow). - В
victor/bookva-overlay:rm deploy/ops-mcp.compose.yml. - В
victor/books:rm deploy/ops-mcp.compose.yml(или превратить в README pointer). - Manual smoke:
mcp__books-ops__ops_docker_psвидит bookva-* containers.
Blocker: — Branch: master