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>
16 KiB
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).
# 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 наружу НЕ выставляем.
# 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.
# Выгрузить 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 для воспроизводимости):
/opt/stacks/owncloud \
+ /opt/stacks/storage/minio-imgproxy/data \
/etc/ssh \
- обновить EMAIL_BODY components. На vds:
sudo systemctl restart cronне нужен (cron читает файл). Verify: дождаться реального прогона (05:00) ИЛИ manualsudo bash /opt/stacks/backup/scripts/run.sh→ ntfy push наvds-backuptopic (phone-confirm, был открыт).
B1. Миррор CMS-медиа books-vds → vds minio (свежий)
vds minio уже 2025-09-07. Stale-копия от 2026-05 — перезаписать свежим миррором.
# На 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.siteA-записьimgproxy→89.253.255.94(сейчас → books-vds 89.253.255.133). - vds imgproxy-nginx traefik label: добавить
Host(imgproxy.kzntsv.site)(сейчас servesimgproxy.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).
# На 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
booksbookva-дубли (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 (до кутовера)
- snolla compat-тест aws-sdk v2 vs vds minio — snolla делает сейчас (без железа).
- .NET admin AWSSDK версия + region —
localработает с MinIO 2025? (stostayer нужен былus-west-1, но там minio настроенus-west-1; books-vds minio =local). Verify. - modulair-rag lightrag:latest S3-клиент — compat с MinIO 2025 (bucket пуст, низкий приоритет).
- artmone — деплой неясен (контейнера на vds нет). Бакет миррорим (B1), но потребителя для endpoint-смены нет. Отдельный вопрос.
- bookva-minio расход 368 obj (shared 37442 vs bookva-minio 37074) — проверить, не stranded ли bookva-файлы в shared (не попали в миррор 2026-05). До A3.