Files
admin/scripts/minio-split-cutover/runbook.md
vitya 443713e8b3 feat(on-snolla): MinIO split S3-direct-path cutover — stack 22 bump 473923e494db->07db7e3
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 <noreply@anthropic.com>
2026-07-30 13:23:23 +03:00

205 lines
16 KiB
Markdown
Raw 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.
# 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/<pilorama98-image> → 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/<site>.git && cd <site>
git pull # правка s3.endpoint уже в master (snolla запушил: 07db7e3, a83e2a9, 218c3ae, 4f3314f, 6c7ea88, f4ddce6)
docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN=<from pass vds-kzntsv/verdaccio-ci> \
-t registry.kzntsv.site/<site>-web:<sha> .
docker push registry.kzntsv.site/<site>-web:<sha>
```
Затем 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``<fileStorageClients>` 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.