Запрос snolla (raw-stream content-api) потребовал MinIO endpoint+креды; ответ был выводим из вики, но не собран одним куском. Добавлен раздел «S3 access для клиентских приложений» в minio-imgproxy-on-vds.md: endpoint (https://minio.kzntsv.site / сырой :9000 / inter minio:9000), root accessKey + pass-ссылка на secret, форма ключа, ssl/region/pathStyle. Следующий такой вопрос — grep по вики, без SSH на сервер. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
146 lines
9.9 KiB
Markdown
146 lines
9.9 KiB
Markdown
---
|
||
title: MinIO + imgproxy stack on vds-kzntsv
|
||
status: STANDBY/unused for CMS — живой CMS image pipeline переехал на books-vds 2026-06-08 (см. ниже)
|
||
tags: [vds, minio, imgproxy, migration, ops]
|
||
related: [[vds-kzntsv]], [[recovery-architecture-snapshot]], [[mssql-on-vds]], [[../entities/books-vds]], [[galleries-storage-class-local-not-s3]]
|
||
---
|
||
|
||
# MinIO + imgproxy on vds-kzntsv
|
||
|
||
> ⚠ **Stale для живого pipeline (2026-06-08).** `imgproxy.kzntsv.site` (hardcoded в `MoreThenCms.Modules.Imgproxy.dll`) теперь смотрит на **[[../entities/books-vds]]** (стек 29 imgproxy + стек 30 minio, bucket'ы `pilorama98`, `galleries`, …), а windows-recovery-host декоммишнен. Этот VDS-kzntsv стек — standby/неиспользуемый для CMS. Option C ниже («остаётся на windows-host») историчен. Миграция галерей и рецепт smoke — [[galleries-storage-class-local-not-s3]].
|
||
|
||
## S3 access для клиентских приложений (content-api, raw-stream, прямой getObject)
|
||
|
||
**Это раздел-lookup — отвечать на «дай MinIO endpoint/креды» отсюда, без SSH на сервер.**
|
||
|
||
Живой MinIO с бакетами CMS — на **[[../entities/books-vds]]** (`89.253.255.133`), legacy-инстанс `minio/minio:RELEASE.2020-07-13` (миграция as-is, без ротации — поэтому ключ совпадает с историческим windows-host).
|
||
|
||
| Параметр | Значение |
|
||
|---|---|
|
||
| Endpoint (внешний, TLS) | `https://minio.kzntsv.site` (traefik → `minio:9000`, LE cert, port 443) |
|
||
| Endpoint (сырой host-порт) | `http://89.253.255.133:9000` (тот же бэкенд, ssl:false) |
|
||
| Endpoint (inter-container на books-vds) | `http://minio:9000` (сеть `proxy`) — так ходит imgproxy |
|
||
| accessKeyId | `AKIAJ2YJP72W6ZHCRE6Q` (= `MINIO_ACCESS_KEY`, root, read+write all) |
|
||
| secretAccessKey | в `pass minio-vds/full-env` (`MINIO_ROOT_PASSWORD`) |
|
||
| region / pathStyle / ssl | `local` / `true` / **`true` для https-хостнейма** (false для сырого `:9000`) |
|
||
|
||
**Форма ключа (подтверждена тем, что imgproxy отдаёт 200):** `bucket=<storageClient>`, `key=<ownerId-lowercase>/<storageFilename>`, backslash→slash. owner из MSSQL приходит UPPERCASE → лоуэркейсить. Бакеты: `assets` (`<ownerId>/<file>`), `galleries` (`<siteId-hex-no-dashes>/<guid>.<ext>`), `pilorama98` (`products/<hex-shard>/...`).
|
||
|
||
⚠️ В snolla dev `default.json` endpoint `212.24.37.82:9000` + ключ `AV1QWDHDUQIPO6ABJOM0` — **устарели** (windows-host, декоммишен). Менять и endpoint, и креды на books-vds-значения выше.
|
||
|
||
> Прецедент: inbox-запрос snolla 2026-06-14 (raw-stream content-api) закрыт этим блоком.
|
||
|
||
Запущен 2026-05-22 как часть `minio-imgproxy-vds-migration`. Заодно **upgrade MinIO** `RELEASE.2020-07-13` (5 лет, CVE-куча) → `RELEASE.2025-09-07`.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
CMS templates (URL pattern: imgproxy.kzntsv.site/<sig>/.../plain/s3://<bucket>/<path>)
|
||
→ traefik (windows-host) [пока, до DNS swap]
|
||
→ imgproxy-nginx (windows-host) [пока]
|
||
→ imgproxy (windows-host) [пока]
|
||
→ MinIO localhost:9000 (windows-host) [пока]
|
||
|
||
После DNS swap imgproxy.kzntsv.site → 89.253.255.94 (VDS):
|
||
→ VDS traefik (HTTPS, LE cert)
|
||
→ imgproxy-nginx (VDS, nginx:alpine, 10 GB cache)
|
||
→ imgproxy (VDS, darthsim/imgproxy:latest)
|
||
→ minio (VDS, RELEASE.2025-09-07, internal docker DNS через proxy network)
|
||
```
|
||
|
||
Stack: `/opt/stacks/storage/minio-imgproxy/{docker-compose.yml,.env,data,nginx.conf,nginx-cache}`. Все 3 контейнера в `proxy` network — service-name DNS работает.
|
||
|
||
**Traefik routes (live):**
|
||
- `minio.vds.kzntsv.site` → minio:9000 (S3 API)
|
||
- `minio-console.vds.kzntsv.site` → minio:9001 (web UI)
|
||
- `imgproxy.vds.kzntsv.site` → imgproxy-nginx:80 (test endpoint pre-cutover)
|
||
|
||
После DNS swap имя `imgproxy.kzntsv.site` (без `.vds.`) добавить в traefik label imgproxy-nginx сервиса.
|
||
|
||
## Migration recipe
|
||
|
||
```bash
|
||
# 1. pass insert (local) — preserve original IMGPROXY_KEY/SALT, reuse MinIO creds
|
||
pass insert -m minio-vds/full-env <<EOF
|
||
MINIO_ROOT_USER=<same-as-source>
|
||
MINIO_ROOT_PASSWORD=<same-as-source>
|
||
IMGPROXY_KEY=<same-as-source-128hex>
|
||
IMGPROXY_SALT=<same-as-source-128hex>
|
||
EOF
|
||
|
||
# 2. VDS: create dir + scp nginx.conf + write .env from pass + write compose
|
||
ssh vitya@vds 'sudo mkdir -p /opt/stacks/storage/minio-imgproxy/{data,nginx-cache}
|
||
sudo chown -R vitya:vitya /opt/stacks/storage'
|
||
scp <source>/nginx.conf vitya@vds:/opt/stacks/storage/minio-imgproxy/
|
||
pass show minio-vds/full-env | ssh vitya@vds 'cat > /opt/stacks/storage/minio-imgproxy/.env
|
||
chmod 600 .env'
|
||
|
||
# 3. docker compose up -d (image pull + start all 3)
|
||
|
||
# 4. Local mc setup + bucket prep
|
||
mc alias set old http://localhost:9000 <creds>
|
||
mc alias set new https://minio.vds.kzntsv.site <creds> --insecure
|
||
for b in $(mc ls old | awk '{print $NF}' | tr -d /); do
|
||
mc mb --ignore-existing new/$b --insecure # mc 2025 не auto-creates на mirror
|
||
done
|
||
|
||
# 5. Mirror (background, ~1 час на 3 GB при 600 KiB/s)
|
||
mc mirror --preserve --quiet --overwrite old new --insecure
|
||
|
||
# 6. Verify per-bucket (size + object count)
|
||
for b in $(mc ls old | awk '{print $NF}' | tr -d /); do
|
||
echo "[$b]"; mc du old/$b; mc du new/$b --insecure
|
||
done
|
||
```
|
||
|
||
## Gotchas
|
||
|
||
1. **MinIO upgrade 5-летний gap WORKS via mc mirror** — старый FS backend (2020) → новый erasure-coded single-drive (2025) совместим через S3 API copy. `mc mirror` reads через S3 API, не сырое filesystem. Бинарный bind-mount свопнуть НЕЛЬЗЯ — формат `xl.meta` разный, новый MinIO не поднимется на старых dirs.
|
||
2. **mc 2025 НЕ auto-creates target buckets** — old mc (<2023?) делало неявно. Симптом: `Failed to perform mirroring The specified bucket does not exist`. Fix: `mc mb --ignore-existing new/<b>` для каждого bucket заранее.
|
||
3. **Non-ASCII filenames + slow uplink → partial mirror failure**: один файл `Albrecht Dürer ...tif` (30 MiB) в `imgproxytest` bucket не доехал с первого `mc mirror` exit=0 — silent skip без error. Retry того же `mc mirror` подтащил. Recommendation: всегда verify по object count + size после mirror, не doверять exit code.
|
||
4. **IMGPROXY_KEY + IMGPROXY_SALT preserve обязательно** — это HMAC signing для URLs. Если поменять, все pre-existing CMS image URLs (signed ссылки в шаблонах + кешированные в admin) ломаются. Reuse same values из source.
|
||
5. **MinIO root creds vs AWS-style**: source использует MINIO_ACCESS_KEY/MINIO_SECRET_KEY (deprecated env vars), новый MinIO — MINIO_ROOT_USER/MINIO_ROOT_PASSWORD. Reuse same values (no rotation в этой миграции) → CMS+imgproxy credentials не меняются. Rotation — отдельная hardening-таска.
|
||
|
||
## CMS image pipeline — DLL hardcoded constraint (2026-05-22 finding)
|
||
|
||
Browser-side URL pattern: `https://www.<site>/imgproxy/<sig>/fill/W/H/no/1/<base64(s3://<bucket>/<path>)>.<ext>`. Sample (working URL provided by user):
|
||
```
|
||
https://www.pilorama98.ru/imgproxy/3rGOrTLEtTSjsrwvbs2fe75A1jDHFGPbaSe9ea7vtPQ/fill/800/800/no/1/czM6Ly9waWxvcmFtYTk4L3Byb2R1Y3RzL2wvOWQ0MjgwODktMGFjYi00ZGM2LWE3YjAtMGQxYzIxN2E1ZTFjLmpwZw.webp
|
||
```
|
||
Base64 source decode: `s3://pilorama98/products/l/9d428089-0acb-4dc6-a7b0-0d1c217a5e1c.jpg`. HMAC signature `3rGOr...` computed from `IMGPROXY_KEY + SALT` (preserved в нашей миграции).
|
||
|
||
**Server-side architecture:**
|
||
```
|
||
Browser → https://www.<site>/imgproxy/<sig>/...
|
||
→ traefik (windows-host) → IIS snolla site :8089
|
||
→ ImgproxyHandler (MoreThenCms.Modules.Imgproxy.dll, closed-source)
|
||
→ HTTP GET https://imgproxy.kzntsv.site/<sig>/... (HARDCODED in DLL)
|
||
→ traefik (windows-host) imgproxy.yml route
|
||
→ imgproxy-nginx:80 → imgproxy → MinIO localhost:9000
|
||
```
|
||
|
||
Decoded DLL strings (`cat MoreThenCms.Modules.Imgproxy.dll | tr -cd '[:print:]\n' | grep -oE ...`):
|
||
```
|
||
/imgproxy;https://imgproxy.kzntsv.site/-ImgproxyHandler_Invoke
|
||
```
|
||
|
||
`MoreThenCms.Modules.Imgproxy.dll` closed-source (исходника нет в `~/projects/MoreThenCms`; ни одного `*.cs` файла с `imgproxy` literal). Options для cutover:
|
||
- A) Hosts file + DNS-01 cert trick — средняя сложность
|
||
- B) Local nginx-relay на windows + self-signed cert + CallTrust поведение DLL unknown — средне-высокая
|
||
- C) Keep as-is — windows-host imgproxy/nginx/MinIO живут indefinitely, VDS stack = standby
|
||
- D) Decompile + recompile DLL — высокая, fragile (binding+strong-name)
|
||
|
||
**User decision 2026-05-22: Option C.** Image pipeline остаётся на windows-host; VDS stack — standby/future use. Decommission windows-host MinIO/imgproxy/imgproxy-nginx НЕ выполняется.
|
||
|
||
Если в будущем понадобится cutover — рассматривать Option A (DNS-01 challenge через REGRU API) как наиболее clean путь, при условии что `imgproxy.kzntsv.site` снова станет нашим (сейчас занят другим сервисом на другом сервере).
|
||
|
||
## Rollback
|
||
|
||
```bash
|
||
# On VDS
|
||
cd /opt/stacks/storage/minio-imgproxy && sudo docker compose down -v
|
||
# Source windows-host MinIO + imgproxy + imgproxy-nginx должны быть still running (48ч uptime acceptance).
|
||
# Если уже decommissioned — docker start minio imgproxy imgproxy-nginx.
|
||
# Snapshot insurance: tar archive bind-mount source data preserved отдельно (acceptance step 3).
|
||
```
|