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>
151 lines
15 KiB
Markdown
151 lines
15 KiB
Markdown
---
|
||
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.
|