--- title: "books VDS — оптимизация памяти (mongo wiredTiger / ES heap / CI-остатки)" type: concept tags: [runbook, books, vds, mongo, elasticsearch, oom, memory, backup, admin] related: [concepts/runbooks-index.md, concepts/gitea-project-create-runbook.md] updated: 2026-08-28 --- # books VDS — оптимизация памяти (OOM → ECONNREFUSED) > **⚙️ СЛУЖЕБНЫЙ РАНБУК — только для администратора проекта `.admin`.** > Применять/выполнять шаги может только **`.admin`** (оператор проекта admin). Другим проектам/агентам — читать по запросу, не выполнять. > Из проекта не выносить: не копировать в другие вики, не пересказывать, не публиковать. > Нужен деплой или прод-операция по этому ранбуку — **поставить задачу админу (`.admin`) и написать письмо**. Админ выполняет, остальные верифицируют. ## Scope Остановить **OOM-цикл** на books VDS (`89.253.255.133`, host4g VPS, CentOS 7), который вызывает `[sched:slovo] sync.failed: ECONNREFUSED 172.20.0.5:27017`. Причина — дефицит ОЗУ: хост 3.9GB RAM, free ~136MB, **swap 100%**, OOM-kill `mongod` (dmesg `Out of memory: Kill process (mongod) score ~114`). Симптом-задача — «ozon fbs products syncronization» (books-sched, прод slovo). ## Артефакты - **Хост:** `89.253.255.133` (books VDS). SSH: `ssh -i ~/.ssh/id_ed25519_books_ops root@89.253.255.133`. - **Креды:** `<из pass books-vds/full-env>` (`BOOKS_DB_*`, `BOOKS_MONGO_*`, `BOOKS_PORTAINER_*`). - **mongo (БД sched):** `books-job-scheduler-mongo` = `172.20.0.5:27017`, image `mongo:4.2`, БД `agendaDb` (sched-задачи: `sched_tasks`, `sched_schedules`, `sched_runs`). Source-of-truth compose: `/usr/docker/books-job-scheduler/docker-compose.yml` (**files-based**, НЕ Portainer-kzntsv). - **ES:** `elasticsearch` (стек 33, `/usr/docker/elasticsearch/docker-compose.yml`, `ES_JAVA_OPTS=-Xmx256m`), `bookva-es` (**Portainer стек 37, endpoint 1**; `ES_JAVA_OPTS` урезан 512m→`-Xmx256m`). - **CI-остатки:** `buildx_buildkit_builder-*` (`moby/buildkit:buildx-stable-1`), удалены. ## Диагностика (как понять, что это OOM) ```bash ssh root@89.253.255.133 ' free -m; dmesg | grep -iE "oom|killed process" | tail; # free ~136MB, swap 100%, Killed (mongod) docker stats --no-stream --format "{{.MemUsage}}\t{{.Name}}" | sort -rh | head; # кто жрёт ' ``` Кто жрёт (на 2026-08-28): `books-job-scheduler-mongo` 715MiB (главный), `minio` 248+140, `elasticsearch` 242, `bookva-es` 223. ## Шаги (по порядку эффекта) ### 1. Урезать `books-job-scheduler-mongo` wiredTiger (главный фикс, ~+450MB) ```bash cd /usr/docker/books-job-scheduler cp docker-compose.yml docker-compose.yml.bak # вставить после " restart: always" в сервисе books-job-scheduler-mongo: # command: ["mongod", "--wiredTigerCacheSizeGB", "0.25"] docker compose up -d books-job-scheduler-mongo ``` Verify: `docker logs books-job-scheduler-mongo | grep -i "cache_size"` → `cache_size=256M`; `docker exec books-job-scheduler-mongo mongo --quiet --eval "db.runCommand({ping:1}).ok"` = 1. ### 2. Снести CI-остатки `buildx_buildkit_builder-*` (сироты от CI) ```bash docker rm -f buildx_buildkit_builder-1ba06752-bf56-4b50-88c5-ce1b48f44a4e0 buildx_buildkit_builder-f61c259c-48e3-4401-b2ca-2404c5cd76070 ``` ### 3. Урезать ES heap (вторично; elasticsearch уже 256m) `bookva-es` → `ES_JAVA_OPTS=-Xms512m -Xmx512m` → `-Xms256m -Xmx256m`. Сделано: Portainer PUT стека 37 (`PUT /api/stacks/37?endpointId=1` с реконструированным compose). Verify: `docker logs bookva-es | grep heap` → `heap size [256mb]`. ## Verify / smoke - `free -m` — free растёт, swap падает (mongo cache_size=256M). - Нет новых `Out of memory: Kill (mongod)` в dmesg на следующем окне (~06:06). - `docker stats` — `books-job-scheduler-mongo` ≪715MiB. - sched-задачи (`ozon fbs products syncronization`) больше не дают `ECONNREFUSED 172.20.0.5:27017`. ## Rollback - mongo: `git checkout`-возврат `command` в compose (или `cp docker-compose.yml.bak docker-compose.yml`) → `docker compose up -d books-job-scheduler-mongo`. - ES: вернуть `ES_JAVA_OPTS`. ## Gotchas 1. **OOM ежедневно ~06:06** — пик в бэкап-окно (mongodump+mariadb-dump+ES snapshot в 06:00 MSK). Урезка снижает, но ХОСТ обделён (3.9GB) — радикальный фикс = апгрейд RAM или Manticore вместо ES. 2. **Книги-стеки — через `portainer.kzntsv.site` endpoint 1 (= books VDS)**: `GET /api/stacks` показывает id 22–51 (books/web/api/ntfy/ops-mcp/chrome/proxy/imgproxy/minio/mongo/db/elasticsearch + bookva-*). Часть из них — **files-based** `/usr/docker/*/docker-compose.yml` (напр. books-job-scheduler-mongo урезан файлом + `docker compose up -d`). При правке стека держать Portainer-источник и файл синхронными, не плодить расхождения. 3. **`com.docker.compose.project.config_files=/data/compose/22`** (label) — старый путь Portainer; `/data/compose/*` на диске НЕ существует. Реальный источник — `/usr/docker/*/docker-compose.yml`. 4. **compose-файлы в CRLF** (`\r\n`) — python-патч учитывать line-ending (иначе needle не матчит). 5. **`bookva-es`** — **Portainer стек 37 (endpoint 1)**. Доступ есть: `BOOKS_PORTAINER_URL` + `BOOKS_PORTAINER_API_KEY` из `pass books-vds/full-env`. НО `GET /api/stacks/:id` НЕ отдаёт `stackFileContent` (в этой версии Portainer), поэтому compose реконструируется из `docker inspect` и обновляется через `PUT` стека. Не менять вслепую без этих данных. 6. **mongo warning XFS** — предупреждение, не ошибка (данные на ext4). 7. **`command` массив** в compose: `command: ["mongod", "--wiredTigerCacheSizeGB", "0.25"]` — строку тоже можно, но список надёжнее. ## Будущее направление — замена ES → Manticore ES в стеке книг — источник перегруза (Java JVM). **Manticore Search** — без Java, легче (сотни MB против 500+MB), быстрее на полнотекстовом поиске. Оправдано, если ES используется для полнотекстового поиска товаров/книг (не анализа/DSL-агрегаций). Тогда урезать оба ES, переписать поиск на Manticore-протокол (SphinxQL/HTTP) и бэкапы. Отдельная задача/ранбук.