From 443713e8b304761fe960626a647f6116106c700d Mon Sep 17 00:00:00 2001 From: vitya Date: Thu, 30 Jul 2026 13:23:23 +0300 Subject: [PATCH] =?UTF-8?q?feat(on-snolla):=20MinIO=20split=20S3-direct-pa?= =?UTF-8?q?th=20cutover=20=E2=80=94=20stack=2022=20bump=20473923e494db->07?= =?UTF-8?q?db7e3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track B3 pilot (on-snolla, S3-direct reads, no imgproxy). In-place image bump of live stack 22 by snolla-live-prod-inplace-image-bump recipe: - build on VDS (07db7e3 = config endpoint minio.kzntsv.site -> minio.vds.kzntsv.site, snolla 0.42.1 v2, NO package.json bump — user decision: split without snolla version bump) - staging :5077 from live env: robots/home/sitemap 200 - S3-direct gate: theme asset /themes/c406.../bootstrap.min.css -> 200, 97324 B == live (aws-sdk v2 -> vds minio 2025 — compat CONFIRMED, no @smithy pin needed, no SigV4 403) - sitemap parity staging==live 3/3 (/, /c, /yandex); /c 404 parity - Portainer PUT stack 22 env-preserving (8/8), prune:false pullImage:true -> 200 - live container 07db7e3 healthy, mem_limit 512m preserved - live-smoke https://on.snolla.com: theme asset 200 97324 B, TLS cert untouched (LE YR1, Oct 18) Rollback: PUT stack 22 back to 473923e494db (rollback tag in registry, pred endpoint minio.kzntsv.site/books-vds, live mirror). RUVDS DECOMM 2026-07-21 — image-tag rollback only. scripts/minio-split-cutover/runbook.md: Track B progress table (B1 mirror DONE, B2 imgproxy DNS-flip LIVE, B3 on-snolla pilot LIVE, remaining 5 sites + Track A TODO). Co-Authored-By: Claude --- host-stacks/vds-kzntsv/on-snolla.compose.yml | 17 +- scripts/minio-split-cutover/runbook.md | 205 +++++++++++++++++++ 2 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 scripts/minio-split-cutover/runbook.md diff --git a/host-stacks/vds-kzntsv/on-snolla.compose.yml b/host-stacks/vds-kzntsv/on-snolla.compose.yml index 6c0e650..79e04ea 100644 --- a/host-stacks/vds-kzntsv/on-snolla.compose.yml +++ b/host-stacks/vds-kzntsv/on-snolla.compose.yml @@ -4,13 +4,15 @@ # (VDS-rule: стеки через Portainer, не ssh+compose). # # Модель: монолит @snollajs/snolla (0.42.1) / core / liquid / data — читает БД-контент MoreThenCms -# (mssql.kzntsv.site) + server-side Liquid render. Ассеты из MinIO (minio.kzntsv.site). In-process sharp, -# delivery=serve-bytes, cache=local-variant. imgproxy в путях нет (как tandemmebel — сырой /imgproxy -> 404 норма). +# (mssql.kzntsv.site) + server-side Liquid render. Ассеты из MinIO (minio.vds.kzntsv.site — CMS-media split +# 2026-07-30, был minio.kzntsv.site/books-vds). In-process sharp, delivery=serve-bytes, cache=local-variant. +# imgproxy в путях нет (как tandemmebel — сырой /imgproxy -> 404 норма). # node-config: NODE_ENV=production -> default(dev,gitignored)+production.json+ENV-override. # ЛЕНДИНГ (1 Page / + 2 StaticPages), БЕЗ блога/каталога/e-commerce. # # Образ собирается АДМИНОМ на VDS из victor/on.snolla.com/deploy/Dockerfile (build-arg VERDACCIO_TOKEN, -# @snollajs/* из verdaccio.kzntsv.site). Тег = git-sha репо on.snolla.com (текущий 473923e494db). +# @snollajs/* из verdaccio.kzntsv.site). Тег = git-sha репо on.snolla.com (текущий 07db7e3 — s3.endpoint +# -> minio.vds.kzntsv.site; pred. 473923e494db). # Homepage render byte-identical прод-HTML (verified direct diff + completeness-gate 3/3 loc parity 2026-07-20). # # Домен: LIVE — on.snolla.com (cert LE HTTP-01, CN=on.snolla.com, issued 2026-07-20, until 2026-10-18). @@ -18,10 +20,11 @@ # авторит. NS ns1/ns2.reg.ru verified -> VDS), traefik rule staging->боевой (LE-порядок соблюдён), # LE-серт issued on first hit. Live-smoke GREEN, homepage byte-identical прод-HTML, sitemap 3/3 parity. # Предыстория: жил на RUVDS IIS (80.64.31.36) до 2026-07-20. RUVDS IIS не тронут (rollback = revert DNS). -# План отката = revert DNS reg.ru -> 80.64.31.36 (RUVDS IIS жив) ИЛИ PUT стека 22 назад на тег (пока -# единственный тег 473923e494db = :latest — первый deploy; образных rollback-тегов пока нет). +# План отката = PUT стека 22 назад на тег 473923e494db (rollback-тег в registry, pred. endpoint +# minio.kzntsv.site/books-vds — жив, миррор там). RUVDS IIS DECOMM 2026-07-21 — rollback только образ-тегом. # -# Runtime egress: mssql.kzntsv.site:1433, minio.kzntsv.site:443 (оригиналы + variant-cache), smtp.yandex.ru:465. +# Runtime egress: mssql.kzntsv.site:1433, minio.vds.kzntsv.site:443 (theme assets — CMS-media split 2026-07-30, +# только чтение aws-sdk v2; был minio.kzntsv.site/books-vds), smtp.yandex.ru:465. # # Секреты — env на VDS (Portainer stack env), НЕ в образ, НЕ в git. Значения ${...} инжектит Portainer # из stack-переменных (переиспользованы verbatim из tandemmebel стека 20 — общая snolla-инфра). Контракт: @@ -31,7 +34,7 @@ services: on-snolla: container_name: on-snolla - image: registry.kzntsv.site/on-snolla:473923e494db # git-sha victor/on.snolla.com master 473923e494db (snolla 0.42.1). rollback-теги в registry: 473923e494db (:latest он же — первый deploy, отката пока нет, revert DNS -> RUVDS). + image: registry.kzntsv.site/on-snolla:07db7e3 # git-sha on.snolla.com master 07db7e3 (snolla 0.42.1, s3.endpoint -> minio.vds.kzntsv.site). rollback-тег в registry: 473923e494db (pred. endpoint minio.kzntsv.site/books-vds, жив). restart: unless-stopped mem_limit: 512m # guardrail: snolla baseline ~100-200M; OOM -> restart. standalone-compose -> mem_limit (НЕ deploy.resources, swarm-only). environment: diff --git a/scripts/minio-split-cutover/runbook.md b/scripts/minio-split-cutover/runbook.md new file mode 100644 index 0000000..36acd24 --- /dev/null +++ b/scripts/minio-split-cutover/runbook.md @@ -0,0 +1,205 @@ +# MinIO split — cutover runbook + +> **Режим:** подготовка. **На железе — НИЧЕГО без команды user.** Все команды готовы к выполнению по «го». +> Owner: admin. Создано 2026-07-29. + +## Goal + +Разнести MinIO-контент на 2 сервера: +- **books-vds shared `minio`** (`minio.kzntsv.site`) → только slovo-бакеты books-приложений. Апгрейд 2020→2025. +- **books-vds `bookva-minio`** → bookva-бакеты (уже там с 2026-05). Апгрейд 2020→2025. Консоль наружу НЕ выставляем. +- **vds `minio`** (`minio.vds.kzntsv.site`, уже 2025-09-07) → CMS-медиа snolla-сайтов + artmone + obsidian + modules + modulair + maxmind + strapi. +- Креды **не трогаем** (один root-ключ `AKIAJ2YJP72W6ZHCRE6Q` на все инстансы). + +## Два независимых трека + +- **Track A — апгрейд books-vds minio (shared + bookva).** Не требует правок apps (transparent: hostname/creds/контент сохраняются). Можно делать отдельно, до/после Track B. +- **Track B — сплит CMS-медиа на vds.** Координированный кутовer: миррор → DNS-flip imgproxy → snolla deploy. Требует гейтов. + +## Progress (2026-07-30) + +| Шаг | Статус | Примечание | +|---|---|---| +| B1 миррор CMS-медиа books-vds → vds minio | ✅ DONE | galleries/pilorama98/themes/assets смиррорены (свежим миррором поверх stale 2026-05) | +| B2 DNS-flip `imgproxy.kzntsv.site` → 89.253.255.94 | ✅ LIVE | imgproxy.kzntsv.site/ → 200 webp (vds imgproxy ← vds minio); CMS-сайты берут картинки через vds | +| **B3 on-snolla пилот (S3-direct-path)** | ✅ LIVE 2026-07-30 | in-place bump стека 22 `473923e494db→07db7e3` (snolla 0.42.1 v2, config endpoint → minio.vds.kzntsv.site, без бампа package.json). Theme-assets `/themes/c406…/…` → 200 97324 B == live (aws-sdk v2 → vds minio 2025 — compat подтверждена, главный риск закрыт). Sitemap 3/3 parity, `/c` 404 parity. Rollback-тег `473923e494db` в registry. | +| B3 остальные 5 сайтов (kupimknigi/tandemmebel/labtools/labtools.pro/emspb) | ⏳ TODO | тот же паттерн bump config-endpoint; labtools/labtools.pro/emspb — image-bump (уже боевые на vds); tandemmebel BLOCKED (визуальный дефект sharp-staging); kupimknigi — проверить cutover-статус | +| Track A (books-vds minio 2020→2025) | ⏳ TODO | transparent, отдельно от Track B | +| B4 .NET catch-all админ s3 endpoint | ⏳ TODO | `C:\sites\snolla\Web.config` serviceURL → minio.vds.kzntsv.site | +| B5 Obsidian Remotely Save endpoint | ⏳ TODO | после B1 миррора obsidian (готово) — менять endpoint | + +**Решение user (2026-07-30):** сплит без бампа snolla-пакетов — сайты остаются на 0.42.1 + aws-sdk v2, только config endpoint. snolla-евый v3-трек (0.43.1/0.7.8/0.17.2) — HOLD, отдельная история (stostayer). Пилот on-snolla подтвердил: **v2 vs MinIO 2025 работает** (без @smithy pin) — SignatureDoesNotMatch нет. + +--- + +## S3-клиент совместимость (audit — сделано) + +| consumer | S3 client | версия | риск vs MinIO 2025 | +|---|---|---|---| +| books (api/web/...) | @aws-sdk/client-s3 (v3) | ^3.450.0 | ✅ низкий | +| modules-db | minio (minio-js) | ^8.0.7 | ✅ низкий | +| snolla (7 сайтов + framework) | **aws-sdk v2** | ^2.1257.0 | ⚠️ EOL, тест обязателен | +| modulair-rag | client inside lightrag:latest image | — | проверить (bucket пуст) | +| obsidian Remotely Save | bundled | 0.5.25 | ✅ низкий | +| .NET admin (AWSSDK) | AWSSDK.S3 | ? | проверить (region `local`) | + +**snolla ДО кутовера** запускает локальный node-скрипт (aws-sdk v2) → `https://minio.vds.kzntsv.site`, bucket `galleries` (stale-миррор там есть): listObjectsV2 + getObject. 200 = ок. Падение = бамп v2→v3 в фреймворке до кутовера. + +--- + +# Track A — апгрейд books-vds minio 2020→2025 + +> Все шаги — на books-vds (`root@89.253.255.133`, ключ `~/.ssh/id_ed25519_books_ops`). +> Transparent: НЕ меняем hostname/creds/имя-контейнера для apps. Только swap бэкенда под тем же именем. + +## A1. Shared `minio` 2020→2025 + +Принцип: новый 2025-контейнер `minio-new` (новый data-dir), `mc mirror` slovo-бакетов, swap traefik route, retire old. Контейнер-имя `minio` + internal DNS `http://minio:9000` сохранить для imgproxy/зависимостей (или imgproxy на books-vds выводится из эксплуатации после Track B DNS-flip — тогда можно переименовать). + +**Бакеты, что остаются на shared minio (slovo):** `books` (37442 obj, 1.2 GiB), `ozon` (0B), `manuals` (0B), `test`/`upload`/`imgproxytest`. + почистить bookva-дубли (см. A3). + +```bash +# 0. Стоп-условие: books apps живы на старом minio (baseline). Снимок логов. +ssh root@89.253.255.133 'docker logs --tail 50 books-api; docker exec minio sh -c "echo ok"' + +# 1. Новый 2025 контейнер, новый data-dir, тот же network=proxy, креды из env +ssh root@89.253.255.133 'mkdir -p /usr/docker/minio-new/data' +# compose: image minio/minio:RELEASE.2025-09-07..., command server /data --console-address :9001, +# env MINIO_ROOT_USER/PASSWORD (== AKIAJ2.../7o0Q...), volume /usr/docker/minio-new/data:/data, +# network proxy, traefik labels minio-console.kzntsv.site→:9001 (новая консоль) — написать compose-файл +# (template ниже; НЕ поднимать до готовности миррора) + +# 2. mc mirror slovo-бакетов old(2020) → new(2025) +docker run --rm --network host --entrypoint sh minio/mc:latest -c ' + mc alias set old http://127.0.0.1:9000 AKIAJ2YJP72W6ZHCRE6Q "7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S" >/dev/null + mc alias set new http://127.0.0.1:900X AKIAJ2YJP72W6ZHCRE6Q "7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S" >/dev/null + for b in books ozon manuals test upload imgproxytest; do mc mb --ignore-existing new/$b; mc mirror --overwrite old/$b new/$b; done +' +# 3. Verify per-bucket object count old==new +# for b in ...; mc ls --recursive old/$b | wc -l ; mc ls --recursive new/$b | wc -l + +# 4. Swap: traefik route minio.kzntsv.site → new:9000 (rule в dynamic config / compose labels new-контейнера) +# + minio-console.kzntsv.site → new:9001 (новая веб-консоль 2025; старый /minio/login на :9000 умрёт) +# 5. Smoke: books-api/web/task-runner пишут/читают S3. Picking-list report GET. +# 6. Retire old 2020 minio (docker stop minio; НЕ удалять data 48ч — rollback). +``` + +**Rollback:** traefik route → old minio:9000; old container ещё running 48ч. + +## A2. `bookva-minio` 2020→2025 + +Контейнер `bookva-minio`, volume `bookva-minio-data` (37074 obj в бакете `books`). НЕ пуст (мигрирован ~2026-05). Console наружу НЕ выставляем. + +```bash +# 1. Новый 2025 контейнер bookva-minio-new (новый volume bookva-minio-data-new), тот же creds +# 2. mc mirror bookva-minio(2020):9001 → bookva-minio-new(2025):900X, бакет books (37074 obj) +docker run --rm --network host --entrypoint sh minio/mc:latest -c ' + mc alias set bvold http://127.0.0.1:9001 AKIAJ2YJP72W6ZHCRE6Q "7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S" >/dev/null + mc alias set bvnew http://127.0.0.1:900X AKIAJ2YJP72W6ZHCRE6Q "7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S" >/dev/null + mc mb --ignore-existing bvnew/books; mc mirror --overwrite bvold/books bvnew/books +' +# 3. Verify count: mc ls --recursive bvold/books | wc -l == mc ls --recursive bvnew/books | wc -l (37074) +# 4. Swap: container name bookva-minio (чтобы bookva-api http://bookva-minio:9000 не менялся) → новый +# (down old, up new с именем bookva-minio). Или compose-имя сохранить. +# 5. Smoke: bookva-api report/picking-list GET. +# 6. Retire old (48ч rollback). +``` + +## A3. Почистить bookva-дубли из shared `books` (после A1, перед mirror-verify) + +В shared `books` остались 95+4 bookva-файлов от до-миграции (дубли того, что уже на bookva-minio). Bookva-unique ключи вычислить из bookva-db и удалить из shared `books`. + +```bash +# Выгрузить bookva-unique s3-пути (на books-vds): +docker exec -i bookva-db mariadb -ubooks -pFNud4Um47y books -N -e \ + "SELECT DISTINCT report_remote_path FROM fbs_picking_lists WHERE report_remote_path!=''" > /tmp/bv-paths.txt +# (плюс pdf_remote_path, и из других таблиц — receipts/reports/task-reports — полный набор S3-путей bookva) +# Пересечь с shared-minio books-бакетом; bookva-unique (не в slovo-db) → mc rm из shared books +``` +**Гейт:** до удаления убедиться, что КАЖДЫЙ bookva-путь жив на bookva-minio (после A2). Иначе не удалять. + +--- + +# Track B — сплит CMS-медиа на vds + +## B0. vds backup (precondition) + +`scripts/vds-backup-rsync-kreknin/run.sh` rsync sources — добавить `/opt/stacks/storage/minio-imgproxy/data` (и compose/nginx.conf для воспроизводимости): + +```diff + /opt/stacks/owncloud \ ++ /opt/stacks/storage/minio-imgproxy/data \ + /etc/ssh \ +``` ++ обновить EMAIL_BODY components. На vds: `sudo systemctl restart cron` не нужен (cron читает файл). **Verify:** дождаться реального прогона (05:00) ИЛИ manual `sudo bash /opt/stacks/backup/scripts/run.sh` → ntfy push на `vds-backup` topic (phone-confirm, был открыт). + +## B1. Миррор CMS-медиа books-vds → vds minio (свежий) + +vds minio уже 2025-09-07. Stale-копия от 2026-05 — перезаписать свежим миррором. + +```bash +# На books-vds (источник) → vds minio (назначение), через mc. Creds одинаковые. +docker run --rm --network host --entrypoint sh minio/mc:latest -c ' + mc alias set src http://127.0.0.1:9000 AKIAJ2YJP72W6ZHCRE6Q "7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S" >/dev/null + mc alias set dst https://minio.vds.kzntsv.site AKIAJ2YJP72W6ZHCRE6Q "7o0Q4NjE5GLkdC48r0oZFEnqddjPNLqtCk+ZEh+S" --insecure >/dev/null + for b in galleries artmone themes assets pilorama98 maxmind obsidian strapi; do + mc mb --ignore-existing dst/$b + mc mirror --overwrite --preserve src/$b dst/$b --insecure + done +' +# variant-cache НЕ миррорим (кэш imgproxy, перестроится на vds). +# modulair / modules — ВНЕ СПЛИТА (user: забыть). Остаются на books-vds shared minio, потребители не трогаются. +``` +**Verify (обязательно):** recursive-count src==dst по каждому бакету (mc ls --recursive | wc -l). Совпадение = ок. +- Ожидаемые объёмы: galleries 9984, artmone 16273, themes 10864, assets 4750, pilorama98 225, maxmind 2, obsidian 179, strapi 0. ~8.9 GiB. + +## B2. DNS-flip `imgproxy.kzntsv.site` → 89.253.255.94 (reg.ru) + +- В reg.ru `kzntsv.site` A-запись `imgproxy` → `89.253.255.94` (сейчас → books-vds 89.253.255.133). +- vds imgproxy-nginx traefik label: добавить `Host(imgproxy.kzntsv.site)` (сейчас serves `imgproxy.vds.kzntsv.site`). +- TTL дождаться (был 86400 — лучше заранее снизить). +- **Verify:** `curl -sI https://imgproxy.kzntsv.site/` → vds traefik; sample CMS image URL (signed) → 200. + +## B3. snolla deploy (6 сайтов) — по "го", после B1+B2 + +Образы собирает admin на VDS (snolla MCP read-only, нет build-канала; либо user даёт канал). Registry = `registry.kzntsv.site` (НЕ stostayer). + +```bash +# На vds, для каждого из 6 репо (on-snolla, kupimknigi, tandemmebel, labtools, labtools.pro, emspb): +cd /tmp && git clone git@git.kzntsv.site:victor/.git && cd +git pull # правка s3.endpoint уже в master (snolla запушил: 07db7e3, a83e2a9, 218c3ae, 4f3314f, 6c7ea88, f4ddce6) +docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN= \ + -t registry.kzntsv.site/-web: . +docker push registry.kzntsv.site/-web: +``` +Затем parity-gate (throwaway staging `:50XX`) + Portainer PUT env-preserving (по `snolla-live-prod-inplace-image-bump`). Smoke каждого сайта (crawl menu pages + viewModel, не только /). + +## B4. .NET catch-all админ (воркстейшн) — s3 endpoint + +`C:\sites\snolla\Web.config` → `` serviceURL: `https://minio.kzntsv.site` → `https://minio.vds.kzntsv.site` (galleries/assets/themes). imgproxy НЕ трогать (хардкод `imgproxy.kzntsv.site` → DNS-flip решает). IIS auto-recycle по Web.config. Smoke: `/admin/account/login` → Asset Manager превью. + +## B5. Obsidian Remotely Save — endpoint (ПОСЛЕ B1 миррора obsidian) + +Гейт: бакет `obsidian` смиррорен на vds (B1). Менять ТОЛЬКО тогда. +- Obsidian → Settings → Remotely Save → S3 endpoint: `https://minio.kzntsv.site/` → `https://minio.vds.kzntsv.site/` (бакет `obsidian`, креды те же). ИЛИ ре-обфусцировать `C:\Users\vitya\obsidian\main\.obsidian\plugins\remotely-save\data.json` (rev+base64) при ЗАКРЫТОМ Obsidian. +- Verify: sync run → no errors. + +--- + +# P3 — cleanup / verify (после cutover) + +- Retire books-vds 2020 minio containers (после 48ч rollback-окна): `docker stop minio` (old), `docker stop bookva-minio` (old). НЕ удалять data до подтверждения бэкапа. +- Retire books-vds imgproxy stack 29 (после DNS-flip — изображения идут через vds imgproxy). Проверить, что никто не ходит на books-vds imgproxy. +- Удалить с shared `books` bookva-дубли (A3). +- vds-backup: подтвердить что `/opt/stacks/storage/minio-imgproxy/data` попадает в nightly snapshot (B0 verify). +- Smoke: все 6 snolla-сайтов (crawl), books slovo reports, bookva reports, obsidian sync, modules-db. + +--- + +# Открытые/verify (до кутовера) + +1. **snolla compat-тест aws-sdk v2 vs vds minio** — snolla делает сейчас (без железа). +2. **.NET admin AWSSDK версия + region** — `local` работает с MinIO 2025? (stostayer нужен был `us-west-1`, но там minio настроен `us-west-1`; books-vds minio = `local`). Verify. +3. **modulair-rag lightrag:latest S3-клиент** — compat с MinIO 2025 (bucket пуст, низкий приоритет). +4. **artmone** — деплой неясен (контейнера на vds нет). Бакет миррорим (B1), но потребителя для endpoint-смены нет. Отдельный вопрос. +5. **bookva-minio расход 368 obj** (shared 37442 vs bookva-minio 37074) — проверить, не stranded ли bookva-файлы в shared (не попали в миррор 2026-05). До A3. \ No newline at end of file