Files
admin/.wiki/entities/books-vds.md
vitya c3e845831d wiki(books-vds): document public bookva-db endpoint :33306
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>
2026-06-29 11:54:50 +03:00

151 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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.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`), db `books` / user `books`.
- **bookva MariaDB:** `89.253.255.133:33306` (контейнер `bookva-db`, маппинг `0.0.0.0:33306->3306` — порт 33306, т.к. 3306 занят slovo). db `books` / user `books`, тот же 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× `*_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 не в stack-inventory~~ **частично закрыто 2026-06-12**: bookva-db/mongo/es/minio засечены и (кроме es) добавлены в backup (см. § Backup). Публичный endpoint bookva-db (`: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`): slovo `elasticsearch` + `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-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
- [[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) switched `default.json:elasticsearch.node` с elasticold на elasticsearch.kzntsv.site. Source ES остаётся running (rollback). См. [[../../tasks/migrate-elasticsearch-to-books-vds]] § Closure note.