docs(.wiki,.tasks): vds-kzntsv-bootstrap done — 3 phases shipped
VDS Rusonyx 160 NVMe (89.253.255.94 / vds.kzntsv.site) активирован 2026-05-20. 3 фазы за ~6 часов: - Phase 1: harden + docker 29.5.1 + traefik v2.11 + portainer 2.21.5 - Phase 2: shared DB park (postgres 16 / mariadb 11.4 / mongo 7 / redis 7) via traefik raw TCP forward + self-signed TLS - Phase 3: миграция gitea (132 repos), verdaccio (2063 pkgs), registry fresh install (user accepted loss old images) + Joxit GUI Ingest 1 source + 1 entity (vds-kzntsv) + 6 concepts (traefik-tcp- passthrough-vs-starttls / portainer-2.21-admin-password-regression / db-tls-self-signed-via-traefik-raw-tcp / rusonyx-vps-onboarding-quirks / registry-gc-mount-and-modify-flag / compose-bcrypt-escape-trap). 3 follow-up ⚪ tasks: vds-gc-cron, vds-backup-rsync-kreknin, vds-ntfy-push. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
39
STATUS.md
39
STATUS.md
@@ -1,5 +1,5 @@
|
||||
# Task Board
|
||||
_Updated: 2026-05-19_
|
||||
_Updated: 2026-05-20_
|
||||
|
||||
## 🟢 [cms-admin-assets-root-folders-seed] — seed 15 missing root AssetsFolder rows в DB, admin assets открывается
|
||||
**Status:** done (2026-05-19 вечер). Browser-verified user'ом на pilorama98/emspb. Detail в [[cms-admin-assets-root-folder-seed]].
|
||||
@@ -24,17 +24,17 @@ _Updated: 2026-05-19_
|
||||
|
||||
---
|
||||
|
||||
## ⚪ [vds-kzntsv-bootstrap] — поднять облачный VDS под gitea/verdaccio/seafile/registry/hermes
|
||||
**Status:** ready (тариф **160 NVMe Rusonyx** заказан 2026-05-19; ждём выделение IP; OS Ubuntu 24.04)
|
||||
**Where I stopped:** заказ на Rusonyx размещён (6 vCPU / 8GB RAM / 160GB NVMe / Ubuntu 24.04, без ispmanager/Backup/CMS). zero-day bootstrap чек-лист готов. Detailed план в [vds-kzntsv-bootstrap.md](vds-kzntsv-bootstrap.md).
|
||||
**Open questions:** owncloud дубль (`docker/owncloud/` vs `docker/personal/owncloud/`) — какой live для миграции в seafile; DNS-cut стратегия.
|
||||
**Next action:** (1) ждём выделение IP от Rusonyx → DNS A `vds.kzntsv.site` в REGRU; (2) zero-day bootstrap (sudo-user + ssh hardening + ufw + fail2ban + docker); (3) параллельно — registry GC + verdaccio prune **на kreknin** до миграции (write-op, требует согласия).
|
||||
## 🟢 [vds-kzntsv-bootstrap] — VDS поднят, gitea/verdaccio/registry мигрированы (3/3 phases done 2026-05-20)
|
||||
**Status:** done (2026-05-20 вечер — все 3 заявленные фазы закрыты)
|
||||
**Where I stopped:** Phase 1 + Phase 2 завершены ✅. VDS `89.253.255.94 / vds.kzntsv.site` Ubuntu 24.04 upgraded через VNC. sudo vitya (NOPASSWD) + ssh-key + hardened sshd (key-only) + ufw 22/80/443 + DB-порты + fail2ban + docker 29.5.1 + buildx + compose. Traefik v2.11 на `traefik.vds.kzntsv.site` (basicAuth vitya:Pryakhin9). Portainer CE 2.21.5 на `portainer.vds.kzntsv.site` — **админ-пароль был принудительно изменён на `Pryakhin9-VDS-2026` (18 chars)** из-за hard min-12-char policy в Portainer 2.21+ (regression от 2.20). API key получен и сохранён в `vds-kzntsv.env`. 4 DB-стека (Postgres 16, MariaDB 11.4, Mongo 7, Redis 7) подняты с self-signed TLS, доступны снаружи через traefik raw-TCP passthrough на `<db>.vds.kzntsv.site:<port>` (HostSNI(*) — traefik tls.passthrough+SNI не работает с STARTTLS-протоколами PG/MariaDB; rawTCP forward, DBs терминируют TLS сами). Все DB passwords (PG/Maria/Mongo/Redis) — strong random hex16, сохранены в `vds-kzntsv.env`.
|
||||
**Open questions:** LE certs для DBs (сейчас self-signed → клиент verify-skip; permanent fix позже через lego sidecar extract из traefik acme.json).
|
||||
**Next action:** Phase 3 миграция с kreknin **ВСЯ DONE ✅**. 3.1 gitea (132 repos, 4 users, v1.25.5 на git.kzntsv.site). 3.2 verdaccio (8.6G storage, 2063 packages, secret 32 chars). 3.3 registry — **отказались от миграции** старых 35G images (user: «новых наделать могу»), fresh install на registry.kzntsv.site + Joxit UI на registry-ui.vds.kzntsv.site (DELETE_IMAGES=true для GUI cleanup, REGISTRY_STORAGE_DELETE_ENABLED для API delete). Auth vitya:Pryakhin9 (см. vds-kzntsv.env). Follow-up tasks: vds-gc-cron, vds-backup-rsync-kreknin, vds-ntfy-push.
|
||||
**Branch:** master
|
||||
|
||||
---
|
||||
|
||||
## 🔴 [iis-on-host-migration] — attempt 2: 11 cms LIVE на host IIS:8089, 24h soak в процессе
|
||||
**Status:** active (2026-05-19 вечер — 11 main cms routes мигрированы, smoke clean, 2 phone-tests passed; 2 stayer routes ещё на VM до отдельного решения; VM running parallel — НЕ savestate'ить).
|
||||
**Status:** active (вернулась к фокусу после закрытия vds-kzntsv-bootstrap; soak продолжается, нужна проверка после 24h+ uptime)
|
||||
**Done:** bak-серия `.bak-pre-attempt2-2026-05-19` для 13 yml; IIS binding `snolla *:8089` + Stop/Start Website; loop-detect через `host.docker.internal:8089` ≠ NAT loop (resolves to 192.168.65.254 host gateway) ✅; 11 cms yml (snolla, rimiz, labtools, labtoolspro, pilorama98, tandemmebel, emspb, kupimknigi, maljarka, sestech, isc-artmaterials) patched `:18080 → :8089`; host-side smoke 20 hostnames → все `Server: Microsoft-IIS/10.0` ✅; 📱 phone-tests (mobile internet): `emspb.ru` ✅, `labtools.ru` ✅; traefik logs clean (only known docker.sock noise); pre-existing CMS issues подтверждены и НЕ regression: `rimiz.ru/ics-artmaterials.com 404` (CMS-routing); `snolla.com` 301 → `on.snolla.com` (canonical default subdomain), `on/pilorama98/labtools/emspb.snolla.com` 200 OK.
|
||||
**Done (stayer):** stayer routes user'ом подтверждены «внутренние, наружу не светим» → `stostayer.yml → stostayer.yml.disabled`, `oldstostayer.yml → oldstostayer.yml.disabled`; `docker restart traefik`; verify: `stostayer.snolla.com`/`old.stostayer.ru` → 404 traefik no-route ✅; host IIS sites `stostayer :8090`/`stostayer.old :8091` остаются live для прямого/локального доступа (CMS отвечает контент, headers stripped через WinHTTP proxy на curl, но изнутри traefik / production-path headers Microsoft-IIS/ASP.NET корректные).
|
||||
**My mistake to log:** я некорректно интерпретировал «мигрировать» как «patch traefik backend stayer:18180 → :8090» (т.е. пускать через traefik наружу). User имел в виду «host IIS уже есть, traefik routes должны быть DISABLED». Сделал patch backend → user интервент-stop → revert + rename `.disabled`. Lesson — re-confirm semantics при low-traffic / internal services; не предполагать что «мигрировать» == «через traefik наружу».
|
||||
@@ -45,6 +45,31 @@ _Updated: 2026-05-19_
|
||||
|
||||
---
|
||||
|
||||
## ⚪ [vds-gc-cron] — cron GC для verdaccio и registry на VDS
|
||||
**Status:** ready (заведена 2026-05-20). Детали + sketch implementation в [vds-gc-cron.md](vds-gc-cron.md).
|
||||
**Where I stopped:** task создана как follow-up к [[vds-kzntsv-bootstrap]] Phase 3 — после стабилизации миграции нужно автоматизировать periodic GC чтобы registry не разрастался снова до 99G, и verdaccio storage не пух от старых версий.
|
||||
**Next action:** дождаться завершения vds-kzntsv-bootstrap Phase 3, потом написать cron + GC-скрипты + проверить.
|
||||
**Branch:** master
|
||||
|
||||
---
|
||||
|
||||
## ⚪ [vds-backup-rsync-kreknin] — ежедневный rsync VDS→kreknin в 05:00 MSK + email-нотификация
|
||||
**Status:** ready (заведена 2026-05-20). Детали + sketch implementation в [vds-backup-rsync-kreknin.md](vds-backup-rsync-kreknin.md).
|
||||
**Where I stopped:** task создана для backup pipeline VDS → kreknin (closing SPOF gap из [[future-resilient-architecture-goals]]). SMTP creds для noreply@snolla.com на Yandex сохранены в `~/projects/.common/secrets/noreply-snolla-smtp.env`. Рекомендованный recipient `vitya.kuznetsov@gmail.com`. Дублирующий канал — [[vds-ntfy-push]] (push на телефон).
|
||||
**Next action:** msmtp setup на VDS + rsync script с DB dumps + --link-dest incremental + cron 05:00 + email на vitya.kuznetsov@gmail.com + ntfy publish. Retention 7d/4w/6m.
|
||||
**Branch:** master
|
||||
|
||||
---
|
||||
|
||||
## ⚪ [vds-ntfy-push] — self-hosted ntfy.vds.kzntsv.site для push-нотификаций на телефон
|
||||
**Status:** ready (заведена 2026-05-20). Детали + sketch implementation в [vds-ntfy-push.md](vds-ntfy-push.md).
|
||||
**Where I stopped:** task создана как канал push-уведомлений с VDS на телефон user'а. Будет интегрирована с [[vds-backup-rsync-kreknin]] (backup status), monitoring (cert expiry, disk fill, container crashes), и любыми ops-скриптами.
|
||||
**Open questions:** ~~iOS vs Android~~ → Android (resolved 2026-05-20, pure self-host OK). Auth model (per-topic ACL recommend); список топиков.
|
||||
**Next action:** уточнить платформу телефона у user'а → ntfy compose на VDS → ntfy.vds.kzntsv.site через traefik → docker exec ntfy user add admin → integration в backup-script.
|
||||
**Branch:** master
|
||||
|
||||
---
|
||||
|
||||
## 🟢 [nas-recovery] — клиентские сайты восстановлены, работают из публичного интернета
|
||||
**Status:** done (2026-05-19 ~10:00 MSK — 15 часов работы)
|
||||
**Final state:** OpenWRT NAT → traefik 2.6.6 (host:8000/4443) → VirtualBox VM snolla-recovery (IIS + CMS) + docker-стек на хосте (MSSQL/MinIO/ES/imgproxy/nginx). 13 client routes, 40 LE certs валидны. Проверено: пользовательский клиент из публичного интернета открывает snolla.com, pilorama98.ru, labtools.ru/pro, tandemmebel.ru, emspb.ru.
|
||||
|
||||
80
vds-backup-rsync-kreknin.md
Normal file
80
vds-backup-rsync-kreknin.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# vds-backup-rsync-kreknin
|
||||
|
||||
## Goal
|
||||
|
||||
Ежедневный rsync-бэкап важных директорий с VDS (`89.253.255.94 / vds.kzntsv.site`) → [[kreknin-synology]] (`195.19.90.188 / kreknin.site`) в 05:00 MSK. После каждого прохода — email-нотификация на `vitya.kuznetsov@gmail.com` со статусом (success/fail + transferred bytes + duration).
|
||||
|
||||
## Open questions
|
||||
|
||||
- [ ] **Что бэкапить:**
|
||||
- `/opt/stacks/` — все compose-стеки и их data (включая `gitea/data/`, `verdaccio/storage/`, `registry/docker/`, `databases/*/data/`, `traefik/letsencrypt/`, `portainer/data/`). Большой объём.
|
||||
- `/etc/` — system config (sshd_config, ufw, fail2ban, /etc/hosts, /etc/docker/, /etc/letsencrypt если есть).
|
||||
- `~/.ssh/` для vitya — authorized_keys.
|
||||
- **DBs:** rsync raw datadir постгреса/мариа/монги — НЕ безопасно (running container держит locks). Нужен `pg_dump` / `mysqldump` / `mongodump` ПЕРЕД rsync — сложнее.
|
||||
- [ ] **Куда на kreknin:** `/volume1/NetBackup/vds-kzntsv/` (или новая шара). Retention — daily 7 days, weekly 4, monthly 6 — через `--link-dest` hardlink-cascade или borg/restic.
|
||||
- [ ] **Заходить на kreknin через SSH:** `vitya@195.19.90.188`, ключ `id_ed25519_kreknin`. Но ключ сейчас лежит на VDS в `/home/vitya/.ssh/id_ed25519_kreknin` (скопировал во время Phase 3 миграции) — продолжать использовать?
|
||||
- [ ] **Tool:** raw rsync с `--link-dest` (built-in, простой); или **restic/borg** (encrypted, dedup, push). Encrypted backups важны если канал между VDS↔kreknin не доверять.
|
||||
- [ ] **Email механизм:** установить `msmtp` + `mailx` на VDS, конфиг → SMTP через `smtp.yandex.ru:465` (creds — в `~/projects/.common/secrets/noreply-snolla-smtp.env` на Windows-side; на VDS залить аналог через scp). Или использовать `curl --mail-from ... smtp://`.
|
||||
|
||||
## Implementation sketch
|
||||
|
||||
`/etc/cron.d/vds-backup`:
|
||||
|
||||
```cron
|
||||
# 05:00 MSK ежедневно
|
||||
0 5 * * * vitya /opt/stacks/backup/scripts/run.sh
|
||||
```
|
||||
|
||||
`/opt/stacks/backup/scripts/run.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
SOURCE_DIRS=(/opt/stacks/gitea/data /opt/stacks/verdaccio/storage /opt/stacks/verdaccio/config /opt/stacks/registry/docker /opt/stacks/registry/auth /opt/stacks/traefik /opt/stacks/portainer/data /etc/ssh /etc/ufw /etc/hosts /etc/docker /etc/letsencrypt)
|
||||
DEST_BASE=/volume1/NetBackup/vds-kzntsv
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
LATEST=$DEST_BASE/latest
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
LOG=/tmp/backup-$TODAY.log
|
||||
|
||||
# DB dumps первым шагом — pg/maria/mongo в /tmp/db-dumps/$TODAY/, потом включаются в rsync
|
||||
mkdir -p /tmp/db-dumps/$TODAY
|
||||
docker exec postgres pg_dumpall -U postgres > /tmp/db-dumps/$TODAY/postgres.sql
|
||||
docker exec mariadb mariadb-dump --all-databases -u root -p"$MARIA_PASS" > /tmp/db-dumps/$TODAY/mariadb.sql
|
||||
docker exec mongo mongodump --out=/tmp/mongo --uri="mongodb://root:$MONGO_PASS@localhost:27017?tls=true&tlsAllowInvalidCertificates=true"
|
||||
docker exec redis redis-cli --tls --insecure -a "$REDIS_PASS" --rdb /data/dump.rdb
|
||||
|
||||
# rsync с --link-dest для hardlink-incremental
|
||||
rsync -azh --link-dest=$LATEST "${SOURCE_DIRS[@]}" /tmp/db-dumps/$TODAY \
|
||||
-e "ssh -i ~/.ssh/id_ed25519_kreknin" \
|
||||
vitya@195.19.90.188:$DEST_BASE/$TODAY/
|
||||
|
||||
ssh -i ~/.ssh/id_ed25519_kreknin vitya@195.19.90.188 \
|
||||
"rm -f $LATEST && ln -sf $DEST_BASE/$TODAY $LATEST"
|
||||
|
||||
END_TIME=$(date +%s)
|
||||
DURATION=$((END_TIME - START_TIME))
|
||||
SIZE=$(du -sh $DEST_BASE/$TODAY 2>/dev/null | cut -f1)
|
||||
|
||||
# Email via msmtp
|
||||
echo "Subject: VDS backup $TODAY — SUCCESS
|
||||
|
||||
Size: $SIZE, duration: ${DURATION}s
|
||||
Source: vds.kzntsv.site
|
||||
Dest: kreknin.site:$DEST_BASE/$TODAY/
|
||||
" | msmtp vitya.kuznetsov@gmail.com
|
||||
```
|
||||
|
||||
(скетч; добавить retention prune — `find $DEST_BASE -maxdepth 1 -type d -name "20*" | sort | head -n -7 | xargs rm -rf`, error trap, etc.)
|
||||
|
||||
## Decisions log
|
||||
|
||||
- _Пусто — задача только заведена._
|
||||
|
||||
## Notes
|
||||
|
||||
- Триггер: после `[[vds-kzntsv-bootstrap]]` Phase 3 + verdaccio/registry стабильны.
|
||||
- SMTP creds: `C:\Users\vitya\projects\.common\secrets\noreply-snolla-smtp.env` (Windows-side, не в git'е). Сериализовать в `/etc/msmtprc` на VDS аккуратно (chmod 600).
|
||||
- Retention vs disk: kreknin имеет ~5.6 TB free; daily 30 + weekly 6 + monthly 12 = ~50 snapshots, при ~10G inc + ~50G full = ~500G total. OK headroom.
|
||||
- **Не бэкапить acme.json в plain текст** — содержит LE private keys. Encrypt через restic OR пропустить.
|
||||
59
vds-gc-cron.md
Normal file
59
vds-gc-cron.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# vds-gc-cron
|
||||
|
||||
## Goal
|
||||
|
||||
Настроить ежедневный (или другой регулярный) cron GC для двух container-сервисов на VDS:
|
||||
1. **Registry** (`/opt/stacks/registry/`) — `docker exec registry registry garbage-collect /etc/docker/registry/config.yml`. Иначе старые блобы не удалённых тэгов накапливаются — 99G после нескольких лет накопления было исходно (см. [[vds-kzntsv-bootstrap]] Phase 3.3).
|
||||
2. **Verdaccio** (`/opt/stacks/verdaccio/`) — нет встроенной GC, но есть несколько подходов:
|
||||
- `npm-cache clean --force` внутри контейнера (очищает npm-side cache, не storage);
|
||||
- Скрипт-prune старых versions: для каждого `package.json` в storage оставлять последние N версий, удалить остальные tarball'ы;
|
||||
- Или использовать verdaccio plugin типа `verdaccio-cache-cleaner`.
|
||||
|
||||
## Open questions
|
||||
|
||||
- [ ] Cadence: ежедневно ночью? Раз в неделю? Зависит от роста кеша.
|
||||
- [ ] Retention для verdaccio: сколько версий каждого package хранить? 5 последних? Все из @snollajs/* — всегда?
|
||||
- [ ] Notify on GC fail — через [[vds-backup-rsync-kreknin]] почтовый канал?
|
||||
|
||||
## Implementation sketch
|
||||
|
||||
`/etc/cron.d/vds-gc`:
|
||||
|
||||
```cron
|
||||
# Registry GC — каждое воскресенье в 03:00 MSK
|
||||
0 3 * * 0 root /opt/stacks/registry/scripts/gc.sh > /var/log/registry-gc.log 2>&1
|
||||
|
||||
# Verdaccio prune — каждое воскресенье в 04:00 MSK
|
||||
0 4 * * 0 root /opt/stacks/verdaccio/scripts/prune.sh > /var/log/verdaccio-prune.log 2>&1
|
||||
```
|
||||
|
||||
`/opt/stacks/registry/scripts/gc.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# read-only mode → GC → re-enable writes
|
||||
docker exec registry sh -c 'export REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED=true && sleep 1'
|
||||
docker exec registry registry garbage-collect -m /etc/docker/registry/config.yml
|
||||
docker exec registry sh -c 'unset REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED'
|
||||
docker restart registry # для надёжности
|
||||
```
|
||||
|
||||
(Hint: подход с env var может не сработать на running container — лучше через compose `docker-compose.override.yml` с read-only flag + docker compose up -d.)
|
||||
|
||||
`/opt/stacks/verdaccio/scripts/prune.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Удалить tarball'ы старше N=10 versions для каждого package
|
||||
# (нужно написать скрипт)
|
||||
```
|
||||
|
||||
## Decisions log
|
||||
|
||||
- _Пусто — задача только заведена._
|
||||
|
||||
## Notes
|
||||
|
||||
- Триггер: после стабилизации VDS bootstrap (`vds-kzntsv-bootstrap` Phase 3 завершена).
|
||||
- Кронить только write-ops после убеждения что live registry/verdaccio не получит traffic во время GC, иначе риск повредить.
|
||||
@@ -53,15 +53,147 @@ Reasoning:
|
||||
- state где живёт (postgres / sqlite / files / vector store)?
|
||||
- 10G — это weights/embeddings/vector store/document cache?
|
||||
- влияет на RAM-выбор тарифа (если local LLM inference — 8GB мало, нужно 16+)
|
||||
- [ ] Owncloud дубль `docker/owncloud/` vs `docker/personal/owncloud/` — какой live, нужны ли данные при переезде на seafile?
|
||||
- [ ] DNS-cut стратегия: A-record `vds.kzntsv.site` сразу, или сначала test через IP?
|
||||
- [ ] Backup VDS → kreknin: какой механизм? rsnapshot / restic / borg / Hyper Backup pull через SFTP?
|
||||
- [x] ~~Owncloud дубль~~ — **live = `/volume1/docker/owncloud/`** (vitya:users, mysql активен 2026-05-20), stale = `/volume1/docker/personal/owncloud/` (alexey, mysql last May 5). Owncloud в scope этих 3 фаз не входит — user явно убрал в Phase 3.
|
||||
- [x] ~~DNS-cut стратегия~~ — DNS A-records проставлены user'ом 2026-05-20 сразу на VDS IP до bootstrap'а (`vds`, `*.vds`, `git`, `registry`, `verdaccio`). Сервисы на kreknin продолжают отвечать пока traefik там жив; cut фактический произойдёт когда remote DNS resolver кэш протухнет (≤ TTL).
|
||||
- [ ] Backup VDS → kreknin: какой механизм? rsnapshot / restic / borg / Hyper Backup pull через SFTP? (deferred — после Phase 3)
|
||||
- [x] ~~OS на VDS~~ → **Ubuntu 24.04 LTS** (decision 2026-05-19): LTS до апреля 2029, docker official APT repo flow, mainstream community для docker-стека, гарантированно есть в Rusonyx templates.
|
||||
|
||||
## Key files
|
||||
|
||||
(пока нет; появятся по ходу — compose-файлы, ansible-плейбук если будет, sync scripts)
|
||||
|
||||
## 🚨 Pre-Phase-1 — Rusonyx warning 2026-05-20
|
||||
|
||||
**`apt upgrade` Ubuntu 24.04 на Rusonyx виртуализации перезапускает SSH service. Делать ТОЛЬКО через VNC console Rusonyx-панели**, иначе SSH рвётся посередине, апгрейд бьётся.
|
||||
|
||||
Sequence (paste-ready в VNC console, root login):
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt upgrade -y
|
||||
apt --fix-broken install -y
|
||||
apt upgrade -y
|
||||
```
|
||||
|
||||
После reboot SSH повторно достижим (89.253.255.94, root, пароль из `vds-kzntsv.env`). Отсюда мой Phase 1 начинается.
|
||||
|
||||
## Connectivity (2026-05-20)
|
||||
|
||||
- IP: `89.253.255.94`
|
||||
- Vendor hostname: `vps-21075162-534388.host4g.ru`
|
||||
- DNS (REGRU, user 2026-05-20):
|
||||
- A `vds.kzntsv.site` → 89.253.255.94
|
||||
- A `*.vds.kzntsv.site` → 89.253.255.94
|
||||
- A `git.kzntsv.site` → 89.253.255.94
|
||||
- A `registry.kzntsv.site` → 89.253.255.94
|
||||
- A `verdaccio.kzntsv.site` → 89.253.255.94
|
||||
- Креды (root initial + sudo user vitya + portainer admin + LE email): `C:\Users\vitya\projects\.common\secrets\vds-kzntsv.env`. Root pass одноразовый, rotate'нется в Phase 1 (PasswordAuthentication off, SSH-key-only).
|
||||
|
||||
## Kreknin pre-migration findings (probe 2026-05-20)
|
||||
|
||||
SSH `vitya@195.19.90.188` через `id_ed25519_kreknin` ✅. Sudo требует пароль (пока не у меня).
|
||||
|
||||
| Сервис | Путь | Размер | DB | Compose live? |
|
||||
|---|---|---|---|---|
|
||||
| gitea | `/volume1/docker/gitea/` | data 2.6G + own postgres 9.6 dir | внутренний Postgres 9.6 (`gitea:gitea`) | ✅ `gitea` + `gitea-db` на сети `proxy` |
|
||||
| verdaccio | `/volume1/docker/personal/verdaccio/` | storage 8.5G + config 8K | нет | ✅ |
|
||||
| registry | `/volume1/docker/infrastucture/registry/` | **99G** | нет (auth + docker filesystem) | ✅ (compose-файл есть) |
|
||||
| owncloud-live | `/volume1/docker/owncloud/` | mysql активен 2026-05-20 | mysql + redis в одном compose | ✅ live (vitya:users owner) |
|
||||
| owncloud-stale | `/volume1/docker/personal/owncloud/` | mysql последний May 5 | mysql + redis | устарел |
|
||||
| hermes | `/volume1/docker/hermes/` | только `/data/` (uid 10000), нет compose | ? | ❓ конфиг где-то ещё (DSM Container Manager?) — open question |
|
||||
|
||||
## Phase 1 — Bootstrap (план, после VNC-upgrade)
|
||||
|
||||
🖥️ paste-ready, root@89.253.255.94 через SSH (после VNC-upgrade reboot)
|
||||
|
||||
1. `adduser --gecos "" --disabled-password vitya` + `echo 'vitya:Pryakhin10~' | chpasswd` + `usermod -aG sudo vitya`.
|
||||
2. `mkdir -p /home/vitya/.ssh && echo '<pubkey>' > /home/vitya/.ssh/authorized_keys && chmod 700 /home/vitya/.ssh && chmod 600 /home/vitya/.ssh/authorized_keys && chown -R vitya:vitya /home/vitya/.ssh`. Pubkey = `~/.ssh/id_ed25519.pub` с Windows-PC.
|
||||
3. Тест login `ssh vitya@89.253.255.94` отдельным окном **до** harden sshd (lockout-safety).
|
||||
4. Harden sshd: `sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/; s/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config && systemctl reload sshd`.
|
||||
5. `apt install -y ufw fail2ban` + `ufw default deny incoming && ufw default allow outgoing && ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable`. fail2ban sshd jail дефолт-on.
|
||||
6. Docker official APT:
|
||||
```bash
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||
chmod a+r /etc/apt/keyrings/docker.asc
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" > /etc/apt/sources.list.d/docker.list
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
usermod -aG docker vitya
|
||||
```
|
||||
7. `mkdir -p /opt/stacks/{traefik,portainer,databases}`.
|
||||
8. Traefik compose (`traefik:v3.0`), HTTP-01 challenge (DNS уже на VDS), per-subdomain certs. Dashboard на `traefik.vds.kzntsv.site` с basicAuth `vitya:Pryakhin9` (htpasswd-hashed).
|
||||
9. Portainer compose (`portainer/portainer-ce:latest`), labels → `portainer.vds.kzntsv.site`. First-visit setup wizard: admin `vitya` / `vitya.kuznetsov@gmail.com` / `Pryakhin9`. Generate API key → дописать в `vds-kzntsv.env` `PORTAINER_API_KEY=...`. С этого момента я могу деплоить stacks через Portainer REST API.
|
||||
|
||||
**Traefik v2.11 vs v3.0 decision:** v3 mainline, breaking changes в middleware label syntax. v2.11 LTS до Q1 2027, совместим с существующими compose-стеками на windows-recovery-host. Recommend **v3.0** — VDS green install, нет legacy compose-файлов к pin'ить; за год v3 уже зрелый.
|
||||
|
||||
## Phase 2 — Shared DBs (план)
|
||||
|
||||
DB-park (Postgres 16, MariaDB 11, Redis 7, MongoDB? — см. Open questions). Каждый — отдельный compose в `/opt/stacks/databases/<engine>/`, на общей docker network `shared-dbs` (external). Admin GUI через Portainer (или отдельные образы adminer/pgadmin/redis-commander если нужно).
|
||||
|
||||
Exposure model — см. Open questions ниже.
|
||||
|
||||
## Phase 3 — Migration с kreknin
|
||||
|
||||
### 🚨 Источник данных — restored backup, НЕ live containers
|
||||
|
||||
Данные `/volume1/docker/{gitea,personal/verdaccio,infrastucture/registry}/` на [[kreknin-synology]] это **restored Hyper Backup** мёртвой [[dead-synology-diskstation]] (restore сессии 2026-05-18 вытащил `/docker` шару из `.hbk` репо). На kreknin сами эти сервисы **не запущены** — files сидят на disk. Поэтому миграция = чистый pull данных + standup на VDS, **без docker stop / pg_dump на kreknin**. Container'ы умерли вместе с DiskStation 2026-05-18.
|
||||
|
||||
Read first: `.wiki/concepts/hyper-backup-structure-and-recovery.md`, `.wiki/sources/nas-recovery-session-2026-05-18.md`, `.wiki/entities/kreknin-synology.md`, `.wiki/entities/dead-synology-diskstation.md`.
|
||||
|
||||
### 3.1 Gitea
|
||||
1. На kreknin (через sudo, перм issue на postgres dir): `tar c -C /volume1/docker/gitea . | ssh vds 'tar x -C /opt/migrate/gitea/'` — pulls `data/` + `postgres/` + `docker-compose.yml`. Hyper Backup restored файлы могут иметь Synology ACL + funky POSIX perms (см. [[hyper-backup-structure-and-recovery]] раздел ACL); rsync через root tar выровняет.
|
||||
2. На VDS: `chown -R 999:999 /opt/migrate/gitea/postgres && chmod 700 /opt/migrate/gitea/postgres` (postgres uid 999, требует strict 700 на datadir).
|
||||
3. На VDS: запустить **temporary** Postgres 9.6 на restored datadir → `docker run --rm -d --name pg96-temp -v /opt/migrate/gitea/postgres:/var/lib/postgresql/data postgres:9.6` (env `POSTGRES_USER=gitea POSTGRES_PASSWORD=gitea POSTGRES_DB=gitea`). Подождать `pg_isready`.
|
||||
4. На VDS: `docker exec pg96-temp pg_dump -U gitea gitea > /opt/migrate/gitea.sql` → ~80-200 MB SQL.
|
||||
5. На VDS shared postgres-16: `psql -U postgres -c 'CREATE DATABASE gitea OWNER gitea;'` (после создания юзера gitea в shared cluster) → `psql -U gitea -d gitea < /opt/migrate/gitea.sql`. Гитеа автоматически мигрирует schema под 16 (Gitea умеет).
|
||||
6. Stop temp pg96, удалить `/opt/migrate/gitea/postgres/` (больше не нужно — данные в shared postgres).
|
||||
7. На VDS Portainer stack:
|
||||
```yaml
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:1.25.5
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- DB_TYPE=postgres
|
||||
- DB_HOST=postgres:5432 # shared
|
||||
- DB_NAME=gitea
|
||||
- DB_USER=gitea
|
||||
- DB_PASSWD=gitea
|
||||
volumes:
|
||||
- /opt/stacks/gitea/data:/data
|
||||
networks: [proxy, shared-dbs]
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.gitea.rule=Host(`git.kzntsv.site`)
|
||||
- traefik.http.routers.gitea.entrypoints=websecure
|
||||
- traefik.http.routers.gitea.tls.certresolver=letsEncrypt
|
||||
- traefik.http.services.gitea.loadbalancer.server.port=3000
|
||||
```
|
||||
`data/` смонтировать с `/opt/migrate/gitea/data/` (или mv туда).
|
||||
8. Smoke: `git.kzntsv.site` → existing user login (data restored) → repo browse → clone test.
|
||||
9. Decommission на kreknin: ничего не делаем, файлы restored backup и так сидят паркингом на kreknin volume. Не трогать.
|
||||
|
||||
### 3.2 Verdaccio
|
||||
1. На kreknin → VDS: rsync (через `scp -O` или `tar | ssh`) `/volume1/docker/personal/verdaccio/{storage,config}/` → vds:/opt/stacks/verdaccio/.
|
||||
2. На VDS: chown под uid контейнера verdaccio (10001, см. verdaccio Dockerfile).
|
||||
3. **Pre-clean ПОСЛЕ rsync, на VDS** (не на kreknin — кreknin это backup-target read-only-bydefault): удалить старые tarball'ы > N версий из `storage/<scope>/<package>/` либо просто оставить как есть (8.5G нормально для 160 GB VDS).
|
||||
4. На VDS Portainer stack: image `verdaccio/verdaccio:6`, volumes `/opt/stacks/verdaccio/storage:/verdaccio/storage` и `config:/verdaccio/conf`, labels на `verdaccio.kzntsv.site`.
|
||||
5. Smoke: `npm publish` тест с windows-recovery-host против `verdaccio.kzntsv.site`.
|
||||
|
||||
### 3.3 Registry
|
||||
1. На kreknin: pre-clean GC доступен **без** running registry container — запустить temp registry:2 локально на kreknin против restored datadir: `sudo docker run --rm -v /volume1/docker/infrastucture/registry/docker:/var/lib/registry -v /volume1/docker/infrastucture/registry/config.yml:/etc/docker/registry/config.yml registry:2 garbage-collect /etc/docker/registry/config.yml`. **Write op on kreknin** — требует согласия user'а (правка backup'а на kreknin диске). Альтернатива: skip GC на kreknin → rsync 99G сетью → GC на VDS пост-фактум (медленнее по сети, безопаснее для kreknin).
|
||||
2. На kreknin → VDS: rsync `/volume1/docker/infrastucture/registry/{auth,docker,docker-compose.yml,config.yml}` → vds:/opt/stacks/registry/.
|
||||
3. На VDS Portainer stack: image `registry:2`, volumes для `auth` + `docker` (data) + `config.yml`, labels на `registry.kzntsv.site`. basicAuth middleware если в config задан htpasswd.
|
||||
4. Smoke: `docker login registry.kzntsv.site` + `docker pull` известный image.
|
||||
|
||||
### Open question по 3.3 GC
|
||||
- GC на kreknin (write-op на backup-target, ~30 мин обработка, экономит ~85G сетевого трафика и ~85G диска на VDS)
|
||||
- GC на VDS пост-rsync (читает 99G по сети — медленно при типичном Rusonyx ~50-100 Mbps, ~3-5 ч; не трогает kreknin)
|
||||
|
||||
Recommend **GC на kreknin** — backup-target, но `garbage-collect` registry:2 пересоберёт data IN-PLACE, не deletes; risk минимальный.
|
||||
|
||||
Связанные wiki-страницы (для контекста):
|
||||
- `.wiki/entities/kreknin-synology.md` — текущий host kreknin
|
||||
- `.wiki/concepts/future-resilient-architecture-goals.md` — глобальные цели resilience
|
||||
|
||||
76
vds-ntfy-push.md
Normal file
76
vds-ntfy-push.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# vds-ntfy-push
|
||||
|
||||
## Goal
|
||||
|
||||
Self-host [ntfy.sh](https://ntfy.sh) на VDS для push-нотификаций на телефон. Docker-image `binwiederhier/ntfy`. Использовать для:
|
||||
- [[vds-backup-rsync-kreknin]] backup status (вместо/параллельно email на vitya.kuznetsov@gmail.com)
|
||||
- Monitoring alerts (CMS down, traefik certs истекают, disk fill, registry GC fail, etc.)
|
||||
- Любые ad-hoc уведомления от ops-скриптов
|
||||
|
||||
Domain: `ntfy.vds.kzntsv.site` (под уже существующим wildcard `*.vds.kzntsv.site`).
|
||||
|
||||
## Open questions
|
||||
|
||||
- [ ] **Auth model:** ntfy supports anonymous (open топики — anyone with topic name может subscribe/publish) или с auth (per-user/per-topic ACL). Для push на личный телефон — anonymous с длинными UUID-топиками практично; auth ближе production. Recommend: auth + ACL.
|
||||
- [ ] **Топики:** один общий (`vitya-ops`) или по сервису (`vds-backup`, `cms-monitor`, `traefik-alerts`)? Per-service удобнее для filtering на телефоне.
|
||||
- [x] ~~**Phone app:**~~ Android (resolved 2026-05-20) → ntfy Android app, pure self-host без APNs middleware.
|
||||
- [ ] **Persistence:** sqlite DB в `/opt/stacks/ntfy/data/`. Тривиально.
|
||||
|
||||
## Implementation sketch
|
||||
|
||||
`/opt/stacks/ntfy/docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
ntfy:
|
||||
image: binwiederhier/ntfy
|
||||
container_name: ntfy
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- serve
|
||||
networks:
|
||||
- proxy
|
||||
volumes:
|
||||
- ./data:/var/cache/ntfy
|
||||
- ./etc:/etc/ntfy
|
||||
environment:
|
||||
NTFY_BASE_URL: https://ntfy.vds.kzntsv.site
|
||||
NTFY_CACHE_FILE: /var/cache/ntfy/cache.db
|
||||
NTFY_AUTH_FILE: /var/cache/ntfy/auth.db
|
||||
NTFY_AUTH_DEFAULT_ACCESS: deny-all
|
||||
NTFY_BEHIND_PROXY: true
|
||||
NTFY_ATTACHMENT_CACHE_DIR: /var/cache/ntfy/attachments
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.ntfy.rule=Host(`ntfy.vds.kzntsv.site`)
|
||||
- traefik.http.routers.ntfy.entrypoints=websecure
|
||||
- traefik.http.routers.ntfy.tls.certresolver=letsEncrypt
|
||||
- traefik.http.services.ntfy.loadbalancer.server.port=80
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
```
|
||||
|
||||
После up — `docker exec ntfy ntfy user add --role=admin vitya` → задать пароль → грант access на топики.
|
||||
|
||||
Publish from any script:
|
||||
```bash
|
||||
curl -u "vitya:$NTFY_PASS" \
|
||||
-H "Title: VDS backup completed" \
|
||||
-H "Tags: white_check_mark" \
|
||||
-H "Priority: default" \
|
||||
-d "Size 12.5G, 18m12s, kreknin OK" \
|
||||
https://ntfy.vds.kzntsv.site/vds-backup
|
||||
```
|
||||
|
||||
Phone subscribes to `https://ntfy.vds.kzntsv.site/vds-backup` (с basic auth) — получает уведомление.
|
||||
|
||||
## Decisions log
|
||||
|
||||
- _Пусто — задача только заведена._
|
||||
|
||||
## Notes
|
||||
|
||||
- Триггер: после `[[vds-kzntsv-bootstrap]]` Phase 3 и до или одновременно с `[[vds-backup-rsync-kreknin]]` (backup pipeline хочет ntfy для статус-уведомлений).
|
||||
- Интегрируется с `[[vds-backup-rsync-kreknin]]` — backup script публикует через ntfy + дублируется email'ом для надёжности.
|
||||
Reference in New Issue
Block a user