bookva MariaDB is published at 89.253.255.133:33306 (0.0.0.0:33306->3306, port 33306 since 3306 is slovo books-db). Added public DB-endpoints block to § Доступ (slovo + bookva), noted mongo/ES stay internal, and a .Ports-vs-.Networks reminder. Closes the port wiki-drift in follow-up #2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
15 KiB
Books VDS
Клиентский VDS (отдельный от инфра-VDS vds-kzntsv). Хостит production books app stack (api/web/scheduler/task-runner) + shared infra (mongo, minio, elasticsearch, traefik, portainer).
Это НЕ vds-kzntsv. Books-стек живёт здесь, не на vds.kzntsv.site. Domain books.kzntsv.site (и bookva.kzntsv.site) разрешаются в этот IP.
Доступ
- Public IP: 89.253.255.133
- Provider: host4g.ru (DNS вендорский
vps-21075162-277731.host4g.ru) - OS: CentOS 7, kernel 3.10.0-1160.36.2.el7.x86_64 (древний, EOL)
- SSH:
root@89.253.255.133:22— only via ed25519 key (~/.ssh/id_ed25519_books_opsна workstation) - Sudo: root, passwordless (мы под root напрямую)
- Portainer:
https://portainer.kzntsv.site(отдельный Portainer, не путать сportainer.vds.kzntsv.siteдля vds-kzntsv)
Публичные DB-endpoints (наружу, для локальных packages/tools):
- slovo MariaDB:
mariadb.kzntsv.site:3306(контейнерbooks-db), dbbooks/ userbooks. - bookva MariaDB:
89.253.255.133:33306(контейнерbookva-db, маппинг0.0.0.0:33306->3306— порт 33306, т.к. 3306 занят slovo). dbbooks/ userbooks, тот же pw. DNS-алиаса нет — коннект по IP. (Verified 2026-06-27.) - bookva mongo / ES: НЕ опубликованы —
bookva-mongo(27017) иbookva-es(9200/9300) только docker-internal, наружу не торчат. Локальные tools их не читают; при нужде — SSH-туннель.
Проверка экспозиции порта:
docker inspect <c> --format '{{json .NetworkSettings.Ports}}'илиdocker ps --format '{{.Ports}}'— НЕ.NetworkSettings.Networks(та показывает только IP, не публикацию).
Все creds — в pass show books-vds/full-env.
Disk
/dev/vda1 122G 53G used (44%) 69G free # 2026-06-04, после disk-full remediation
Single root partition, нет отдельной /data. Docker hub data под /var/lib/docker/.
Disk-full incident 2026-06-04 (provider Rusonyx monitoring alert, 4.95% free / 96% used). Culprits:
- 38 GB unrotated container logs —
books-task-runner(32.8 GB) +bookva-task-runner-история (5.6 GB).books-task-runner(тенант slovo) в tight error-loop с ~2026-05-31 (~9 GB/day, no log rotation). Truncated live (:> json.log, no restart). - ~26 GB orphaned BuildKit cache — 11 running
buildx_buildkit_builder-*daemon-контейнеров, которых нет вdocker buildx ls(orphan builders), держали 9×*_statevolumes. Removed (docker rm -f+docker volume rm). - Result: 96% → 44%.
Durable guard added 2026-06-04: /etc/logrotate.d/docker-containers (copytruncate, size 200M, rotate 3, compress) + hourly /etc/cron.d/docker-logrotate. Caps любой runaway container-log без docker restart.
Root cause залипшего лога (НЕ «протухший ключ» — правка-самообман, см. ниже):
- На books VDS два зеркальных стека: books-* (тенант slovo,
books-db) и bookva-* (тенант bookva,bookva-db), один и тот же образ. Ozon-вызовы шлёт*-task-runner(не scheduler — он лишь триггерит agenda-джобу, HTTP делает task-runner). Толькоbooks-task-runnerсыпал ошибки;bookva-task-runner— 0. - В таблице
sellers(обе БД) два Ozon-продавца: Slovo (client_id 94191) и Bookva (client_id 50542). Вbooks-dbу строки Bookva вapi_keyнамеренно вписан ключ Slovo (9683…вместо живогоddc2…) — это сознательная ревокация доступа books→Ozon-аккаунт Bookva (сделано в прошлой сессии). Ozon наClient-Id:50542+чужой ключ →code 5 Invalid Api-Key. НЕ чинить этот ключ — это и есть защита. (Живой ключ Bookva — вbookva-dbи в конфиге, для тенанта bookva.) - Спам шёл оттого, что часть books-agenda-джоб не заскоуплены на slovo: при пустом
data.idSellerтаски итерируют ВСЕХ продавцов (data.idSeller ? filter : allSellers), включая Bookva с битым ключом. Раньше заскоупили толькоozon stocks syncronization(idSeller:2).
Фикс 2026-06-04: проставлен data.idSeller=2, otherSellers:[] в books-job-scheduler-mongo (agendaDb.agendaJobs) пяти джобам: ozon fbs postings syncronization, generate old prices, ozon fbs products syncronization, create ozon products in incorrect state report, put on sale products. Залипшие overdue-прогоны оборваны: unlock (lockedAt:null) + docker restart books-task-runner. Результат: Invalid-Api-Key 6448/мин → 0, джобы отработали по slovo и перепланировались.
Durability фикса: правка в mongo переживает деплой. Эти джобы — легаси (data._fromConfig отсутствует), reconciler (lib/reconciler.js, RECONCILER_ENABLED=true) трогает только _fromConfig:true-джобы; в tasks.json (запечён в образ) у них schedule:null → reconciler их пропускает. Эмпирика: stocks-sync idSeller:2 (только в mongo) пережил деплой 31.05.
Полнота скоупа (сверено 2026-06-04). На books-стороне сосуществуют ДВА поколения джоб:
- Legacy (без
_fromConfig, расписание в persisted mongo): seller-перебирающие —seller.findAllпри пустомdata.idSeller→ ВСЕ продавцы. Ровно 7 таких:ozonFbsPostingsSyncronization,ozonFbsProductsSyncronization,ozonStocksSyncronization,generateOldPrices,createOzonProductsInIncorrectStateReport,putOnSaleProducts,syncStocksWithWarehouse. Все заскоупленыidSeller:2(5 сегодня + stocks + warehouse ранее). Других seller-перебирающих нет. - Reconciler-gen (
_fromConfig:true, изtasks.json): фильтруются поsalesChannels:[2](ozon-канал Slovo), не по idSeller —load products to ozon,ozonProductSalesChannelSync,unarchiveAutoArchivedProducts,addFixedPriceProductsToAction,fbsPicking*и т.д. Уже корректно указывают только на канал Slovo.
Группа В — НЕ дыра (изначальная гипотеза снята). sales_channels в books-db: канал 2 = Slovo type=ozon; канал 3 = Bookva type=ym (Яндекс.Маркет, не Ozon). Активного Bookva-ozon-канала нет → channel-driven Ozon-джобы физически не могут залезть в Bookva. Bookva-ym использует отдельный ym_api_key, к инциденту не относится.
Follow-ups:
- Каноничный фикс — в репо books: legacy seller-перебирающие джобы стоит либо мигрировать в
tasks.jsonсо скоупом, либо вывести из эксплуатации (их функции, возможно, уже покрыты reconciler-gen). Риск mongo-only правки: если будущий релиз даст legacy-джобе реальныйscheduleвtasks.jsonбез скоупа — reconciler пере-сеет её (data без idSeller) → доступ к Bookva вернётся. wiki-drift —частично закрыто 2026-06-12: bookva-db/mongo/es/minio засечены и (кроме es) добавлены в backup (см. § Backup). Публичный endpoint bookva-db (bookva-*stack не в stack-inventory:33306) задокументирован в § Доступ (2026-06-27). Полный stack-inventory bookva-* (api/web/scheduler/task-runner/ntfy) — всё ещё TODO.
Стек (2026-05-25 inventory)
Portainer-managed stacks (endpoint 1)
Post-migration 2026-05-25 (см. ../concepts/portainer-stack-management-books-vds).
| Id | Stack | Container(s) | Bind data path | Purpose |
|---|---|---|---|---|
| 22 | books-job-scheduler |
books-job-scheduler + books-job-scheduler-mongo | /opt/books/job-scheduler/{config,mongo/{db,configdb}} |
agenda jobs (cron + on-demand) |
| 23 | books-api |
books-api | /opt/books/api/{config,data,upload} |
books API server (Nitro, port 3021) |
| 24 | books-web |
books-web | — | books-Nuxt front-end |
| 25 | books-ntfy |
books-ntfy | /opt/books/ntfy/ |
local ntfy (separate from shared ntfy.vds.kzntsv.site) |
| 26 | books-ops-mcp |
books-ops-mcp + books-ops-mcp-bootstrap-1 + books-docker-proxy + books-docker-proxy-ro | /opt/books/books-ops-mcp/ |
books ops-mcp endpoint |
| 27 | chrome |
chrome | — | Puppeteer browser for PDFs / scraping |
| 28 | proxy-chain |
proxy-chain | /usr/docker/proxy-chain/config |
upstream HTTP-proxy chain, internal-only |
| 29 | imgproxy |
imgproxy + imgproxy-nginx | /usr/docker/imgproxy/nginx/{cache,nginx.conf} |
serves imgproxy.kzntsv.site, S3→minio |
| 30 | minio |
minio | /usr/docker/minio/data |
S3 blob store, minio.kzntsv.site |
| 31 | mongo |
mongo | /usr/docker/mongo/data/{db,configdb} |
shared MongoDB 4.2 |
| 32 | books-db |
books-db | /usr/docker/books-db/data |
MariaDB latest, books application DB |
| 33 | elasticsearch |
elasticsearch | /usr/docker/elasticsearch/{data,snapshots} |
ES 7.10.0 + path.repo=/snapshots для backup + reindex.remote.whitelist=elasticold.kzntsv.site:443 (added 2026-05-25 для migration) |
SSH-managed (management plane — Portainer migration skipped)
Recreate ломает access всему остальному (traefik) или себя (portainer). Оставлены ad-hoc compose в /usr/docker/<svc>/.
| Compose dir | Containers | Bind data path |
|---|---|---|
/usr/docker/traefik/ |
traefik | ./letsencrypt (acme.json) + ./data/traefik.yml |
/usr/docker/portainer/ |
portainer | named volume portainer_data |
Build helpers (transient)
buildx_buildkit_builder-*× 7 — buildkit builders, oneshot заdocker buildx. Не data.
App-config paths
/opt/books/api/{config,data,upload}
/opt/books/job-scheduler/{config,mongo/{db,configdb}}
/opt/books/task-runner/
/opt/books/ntfy/ # local ntfy data
/opt/books/books-ops-mcp/
config/default.json overrides image-baked config (MySQL/ES/S3 endpoints).
Backup
Daily 06:00 MSK → kreknin. См. ../sources/books-vds-backup-daily-kreknin-2026-05-25. Pipeline:
- DB dumps (slovo): books-db (mariadb), mongo (shared), books-job-scheduler-mongo
- DB dumps (bookva, added 2026-06-12): bookva-db (mariadb, тот же root pw что books-db), bookva-mongo (no-auth)
- ES snapshots via REST API (file repo
kreknin): slovoelasticsearch+bookva-es(раздельные репо/каталоги) - rsync bind paths + dumps +
bookva-minio-data(named volume, raw) +/usr/docker/bookva-es(snapshot repo) +/opt/books/+/etc/{ssh,hosts,cron.d}+/root/.ssh→vitya@kreknin:/volume1/NetBackup/books-vds/<date>/ - ntfy
BOOKS-VDS backup OK <date>+ email - Retention 7 daily snapshots
Live since 2026-05-25. bookva tenant добавлен 2026-06-12 (db/mongo/minio/es verified на kreknin: bookva-mariadb 257M, bookva-mongo 3.2M, bookva-minio 1.4G, bookva-es snapshot 801M).
bookva-es snapshot (added 2026-06-12): Portainer stack 37 пересоздан с path.repo=/snapshots + bind /usr/docker/bookva-es/snapshots (uid 1000:0), repo kreknin зарегистрирован. Gotcha: оба ES-каталога называются snapshots → в rsync источник bookva-es = родительский /usr/docker/bookva-es (basename bookva-es), иначе слились бы в один dest/snapshots/ и испортили оба репо. На kreknin: snapshots/ (slovo) и bookva-es/snapshots/ — раздельно. Deploy script: /opt/stacks/backup/run.sh (== repo scripts/books-vds-backup-daily-kreknin/run.sh).
DNS
books.kzntsv.site→ 89.253.255.133 (books-apihost route via local traefik)bookva.kzntsv.site→ 89.253.255.133 (books-webhost route)elasticsearch.kzntsv.site→ 89.253.255.133 (ES via traefik basicAuth)portainer.kzntsv.site→ 89.253.255.133 (Portainer UI)edge.kzntsv.site→ 89.253.255.133 (Portainer edge agent)
Auth-зоны DNS — в reg.ru под kzntsv.site.
Cross-refs
- kreknin-synology — backup target.
- vds-kzntsv — наш инфра-VDS (отдельный, gitea/registry/etc.) — НЕ хост books'а.
- ../concepts/vds-kzntsv-ssh-access — SSH-аудит инфра-VDS (был misnamed как
books-ssh-accessдо 2026-05-25; ошибочно claimed vds-kzntsv hosts books). - ../sources/books-vds-backup-daily-kreknin-2026-05-25 — backup standup chronology.
Making-of-history notes
- Discovered 2026-05-25 во время backup-таски. Wiki ../concepts/books-ssh-access до этого ошибочно говорила что books на vds-kzntsv (89.253.255.94). User: «books VDS - это совсем другой сервер, клиентский». Inventory + IP probe confirmed.
- Workstation SSH key
id_ed25519_books_opsуже был задеплоен под root до этой сессии (видимо, прошлый bootstrap забыт-без-документации). - Hybrid stack-management (Portainer + SSH-compose) — pre-existing, не наш design. Migrated 6 SSH-compose стеков → Portainer 2026-05-25 (
books-vds-stacks-to-portainer🟢).traefik+portainerостаются SSH-managed (management plane). imgproxy(2 containers,imgproxy.kzntsv.site) был missing из этого entity wiki до Phase 0 probe 2026-05-25 — fixed in flight.- ES indices populated 2026-05-25 — migration с Windows host (
elasticold.kzntsv.site, ES 7.10.1) → books VDS via reindex-from-remote. 3 indices:artmone(2621),epz(820604),products(105922). Consumers (books-api + books-task-runner + books-job-scheduler) switcheddefault.json:elasticsearch.nodeс elasticold на elasticsearch.kzntsv.site. Source ES остаётся running (rollback). См. ../../tasks/migrate-elasticsearch-to-books-vds § Closure note.