Files
admin/.wiki/concepts/mappa-vds-deploy-runbook.md

5.7 KiB
Raw Blame History

title: mappa — VDS deploy runbook (БД / образ / Portainer-стек 26 / редеплой / verify) type: concept tags: [mappa, vds, deploy, docker, portainer, traefik, postgres, runbook, meta] related: ../entities/vds-kzntsv, portainer-stack-management-vds, mappa-prod-infra updated: 2026-08-23

mappa → VDS deploy runbook

Развёртывание mappa (единый meta-сервис цеха: HTTP-ядро + MCP-адаптер, Postgres) на ../entities/vds-kzntsv (89.253.255.94) рядом с общей БД (решения 1/17/18 спеки concepts/mappa). Первый деплой — 2026-08-23 (#1012, закрыта).

Разделение труда (директива 2026-08-23): деплой/редеплой = АДМИН (таска + письмо); прод-импорт, verify счётчиков, read-only+фолбэк, ntfy-алерт, артефакты, MCP-регистрация = mappa (#985). Не делать чужую часть.

Артефакты

  • Код: OpeItcLoc03/mappa @ master. Сборка TS: npm ci && npm run builddist/.
  • Образ: registry.kzntsv.site/mappa:0.1.0. Dockerfile в корне репо (multi-stage node:22-alpine; обязательный COPY src/lib/schema.sqldist/src/lib/schema.sql — tsc не копирует .sql, без него migrate() падает «schema.sql not found»).
  • Стек Portainer: mappa (Id 26, endpoint 1). Source-of-truth compose: admin/host-stacks/vds-kzntsv/mappa.compose.yml.
  • БД: база mappa + role mappa на общем postgres:16 VDS (owner = mappa). Креды: pass show mappa/full-env (MAPPA_DB_PASS, DATABASE_URL docker-сетевой: postgres://mappa:…@postgres:5432/mappa; внешний host postgres.vds.kzntsv.site:5432 — TLS-verify-skip).
  • Endpoint: https://mappa.vds.kzntsv.site (traefik websecure, letsEncrypt, service-port 8731).

Реденплой (обновление образа) — быстрый путь

# 1. Собрать и запушить новый тег образа (с воркстейшена, Docker Desktop)
cd ~/projects/mappa
docker build -t registry.kzntsv.site/mappa:<tag> .
docker push registry.kzntsv.site/mappa:<tag>

# 2. НА VDS: docker pull ДО restart стека (иначе застрянет на старом digest!)
ssh vds.kzntsv.site 'docker pull registry.kzntsv.site/mappa:<tag>'

# 3. Поменять image: в host-stacks/vds-kzntsv/mappa.compose.yml И в Portainer stack 26
#    (обе копии синхронизировать — иначе следующий redeploy из гита откатит тег)

# 4. Restart стека через Portainer API (НЕ ad-hoc docker compose up!)
#    JWT: pass vds-kzntsv/full-env PORTAINER_ADMIN_PASS → POST /api/auth
curl -ksS -X POST "https://portainer.vds.kzntsv.site/api/stacks/26/restart?endpointId=1" \
  -H "Authorization: Bearer $JWT"

# 5. Verify
curl -ksS https://mappa.vds.kzntsv.site/health          # {"ok":true,"service":"mappa"}
curl -ksS https://mappa.vds.kzntsv.site/admin/status    # entities/by_type/projects
curl -ksS https://mappa.vds.kzntsv.site/graph/stats     # nodes/edges/components
ssh vds.kzntsv.site 'docker ps --filter name=mappa --format "{{.Status}}"'

Обновление env (пароль БД и т.п.)

Через Portainer → Stack 26 → Edit (env array), НЕ файлом на VDS (Portainer string-mode не материализует .env; env передаётся API-массивом — gotcha 1 portainer-stack-management-vds). MAPPA_DB_PASS — единственный env в стеке.

Первичный деплой с нуля (cold start)

  1. БД: на VDS docker exec postgres psql -U postgres: CREATE ROLE mappa LOGIN PASSWORD '<pw>'; CREATE DATABASE mappa OWNER mappa; Пароль → pass insert -m mappa/full-env.
  2. Образ: собрать (см. выше), docker push.
  3. Compose: admin/host-stacks/vds-kzntsv/mappa.compose.yml → Portainer create (standalone string-mode, endpointId=1, env [{"name":"MAPPA_DB_PASS","value":…}]).
  4. Проверка миграций: при старте migrate() сам создаёт таблицы (6 шт: projects/counters/ entities/edges/leases/logs, owner=mappa) — docker logs mappa + \dt в БД.
  5. Пинг mappa: письмо в mappa/.agents/inbox/ (event: mappa-deployed) — импорт и остальное за ними.

Gotchas (из первых деплоев)

  1. schema.sql в dist — tsc компилирует только .ts; migrate() ищет .sql рядом с dist/src/lib/. Без COPY в Dockerfile → контейнер в restart-loop («schema.sql not found»).
  2. docker pull до restart — Portainer start/stop НЕ тянет новый digest сам по себе; перезалитый :0.1.0 без pull на VDS = старый образ (ловил 2× за сессию 2026-08-23).
  3. Не ad-hoc compose — все стеки VDS через Portainer (VDS ops rule). docker rm -f вручную снёс контейнер — Portainer recreate поднял, но это не путь.
  4. mem_limit 512m — канон app-стеков, не убирать (cgroup-лимит vs вся память хоста).
  5. Внешний DATABASE_URL — контейнерный @postgres:5432 валиден только внутри docker-сети; для импорта с воркстейшена — postgres.vds.kzntsv.site:5432 (+ TLS-verify-skip, self-signed).
  6. Закрытие деплоя — всегда пара «таска + письмо mappa» (канон mappa-deploy-workflow).