Files
admin/.wiki/entities/books-vds.md
vitya b0559fdbed wiki(books-vds): disk-full incident 2026-06-04 + Bookva->slovo job scoping
Provider Rusonyx alert (96% used). Reclaimed ~64G: truncated runaway
task-runner json-logs (38G) + removed orphan BuildKit builders/volumes
(26G). Added logrotate copytruncate guard.

Root cause: books-task-runner (slovo) error-looped on Ozon seller Bookva
(50542) whose key was intentionally swapped to slovo's as an access
revocation. Spam came from unscoped legacy seller-iterating agenda jobs.
Fixed: idSeller:2 on 5 jobs (deploy-durable, non-_fromConfig). Verified
6448 errors/min -> 0.

Group-V (channel-driven) confirmed non-issue: Bookva channel is type=ym,
no active Bookva-ozon channel exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 12:28:30 +03:00

13 KiB
Raw Blame History

title: Books VDS (client server — 89.253.255.133) type: entity tags: [hardware, vds, books, host4g, backup, portainer, elasticsearch] related: kreknin-synology, vds-kzntsv, ../concepts/portainer-stack-management-books-vds updated: 2026-05-25 (ES migration from Windows host)

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.sitebookva.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)

Все 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 logsbooks-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× *_state volumes. 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:

  1. Каноничный фикс — в репо books: legacy seller-перебирающие джобы стоит либо мигрировать в tasks.json со скоупом, либо вывести из эксплуатации (их функции, возможно, уже покрыты reconciler-gen). Риск mongo-only правки: если будущий релиз даст legacy-джобе реальный schedule в tasks.json без скоупа — reconciler пере-сеет её (data без idSeller) → доступ к Bookva вернётся.
  2. wiki-drift — bookva-* stack (bookva-api/db/es/minio/mongo/scheduler/task-runner/web) + books-task-runner/scheduler не в stack-inventory ниже; нужен re-inventory.

Стек (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: books-db (mariadb), mongo (shared), books-job-scheduler-mongo
  • ES snapshot via REST API (file repo kreknin)
  • rsync bind paths + dumps + /opt/books/ + /etc/{ssh,hosts,cron.d} + /root/.sshvitya@kreknin:/volume1/NetBackup/books-vds/<date>/
  • ntfy BOOKS-VDS backup OK <date> + email
  • Retention 7 daily snapshots

Live since 2026-05-25.

DNS

  • books.kzntsv.site → 89.253.255.133 (books-api host route via local traefik)
  • bookva.kzntsv.site → 89.253.255.133 (books-web host 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

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) switched default.json:elasticsearch.node с elasticold на elasticsearch.kzntsv.site. Source ES остаётся running (rollback). См. ../../tasks/migrate-elasticsearch-to-books-vds § Closure note.