Files
admin/.tasks/vds-kzntsv-bootstrap.md
vitya c55cb11948 tasks: merge imported MoreThenCms task history with admin live agenda
- 7 admin per-task files moved from .tasks-imported/ to .tasks/ (renames)
- NEXT-SESSION-PROMPT.md (iis-migration session handoff) kept as sibling
- .tasks-imported/STATUS.md removed (content merged into admin STATUS.md)
- admin STATUS.md gains 'Imported from MoreThenCms' section with all 7 admin
  done tasks verbatim. 4 CMS-domain task blocks excluded (stay in MoreThenCms):
  cms-admin-assets-root-folders-seed, cms-port-leak-fix,
  traefik-maljarka-502-bug, cms-maljarka-https-mode-bug-fix
- morecms-subtree-split marked done (acceptance: 4 split branches exist with
  per-file history)
- admin-subtree-import-and-cleanup marked active (in-progress this session)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:51:48 +03:00

225 lines
20 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.
# vds-kzntsv-bootstrap
## Goal
Поднять облачный VDS `vds.kzntsv.site` (Rusonyx) и вынести туда ключевые инфраструктурные сервисы пользователя, чтобы не зависеть от собственного железа (мёртвая NAS показала single-point-of-failure). Цель — устранить риск повторения 2026-05-18 для **инфраструктурного** слоя (gitea/verdaccio/seafile/registry/hermes); production CMS (MoreThenCms) остаётся на [[windows-recovery-host]] и обсуждается отдельно в [[future-resilient-architecture-goals]].
После миграции [[kreknin-synology]] остаётся как backup target, не как live host инфры.
## Сервисы под миграцию (snapshot 2026-05-19)
Источник размеров — `du -sh /volume1/docker/*/` на kreknin (см. Decisions log 2026-05-19).
| Сервис | Сейчас на kreknin | После cleanup / на VDS | План |
|---|---|---|---|
| gitea | `/volume1/docker/gitea/` 2.6G | ~2.6G | lift-and-shift (compose + data tar) |
| verdaccio | `/volume1/docker/personal/verdaccio/` 8.5G | 23G | prune old tarballs до миграции |
| owncloud | `/volume1/docker/owncloud/` 187M + дубль 195M | — | **отказ**, заменяется seafile |
| seafile | (новый) | 3040G | green install на VDS |
| registry | `/volume1/docker/infrastucture/registry/` 99G | 515G | **`registry garbage-collect` ДО миграции** (write op, требует read-only/stopped registry) |
| hermes | `/volume1/docker/hermes/` 0 (пусто на kreknin) | ~10G (user estimate) | TBD — что это, где state |
Раскладка после cleanup: ~5575G data + ~5G OS/docker → ~80100G с headroom.
## Тариф VDS (Rusonyx, https://www.rusonyx.ru/hosting/vps/#ssd)
**Заказан (финал, user 2026-05-19): `160 NVMe`** (Rusonyx переименовал прежний `160 SSD` тариф в NVMe — апгрейд storage по той же цене, IOPS выше).
Конфигурация заказа:
- 6 vCPU 2.6GHz
- 8192 MiB RAM (8 GiB)
- 163840 MiB disk (160 GiB) NVMe
- Ubuntu Server 24.04
- 1 IPv4 (free)
- Лицензия / CMS / ispmanager — нет (docker-only, control-panel мусор не нужен)
- VPS Backup от Rusonyx — 0 шт (свой backup через rsync/borg → kreknin или off-site cloud)
- SSH root access — Вкл на старте (после bootstrap — отключить, sudo-user)
Reasoning:
- 160GB headroom ~45% на старте после миграции (~60-70G data + 5G OS) — годен на 2-3 года без add-on operations.
- 8GB RAM overkill для baseline (~2.1G) но даёт реальный запас под burst и любые будущие сервисы.
- User выбрал «купи-и-забудь» вариант over 80 SSD+add-on; +1000 ₽/мес vs 80 SSD = +12k₽/год за operational simplicity.
**Не выбраны:**
- 40 SSD — 4GB RAM впритык, OOM risk под seafile+registry burst.
- 80 SSD — RAM ок, но 80GB диск требует add-on (цены непрозрачны без звонка в managers).
- 220+ SSD — overkill без local LLM-inference.
## Open questions
- [ ] **Hermes** — Nous Research agent runtime (https://hermes-agent.nousresearch.com/). Открытые подвопросы:
- self-host их open-source / docker image, или client-wrapper к managed API?
- если self-host — какой репо/образ, какие порты, какие external deps (vector DB, LLM endpoint)?
- state где живёт (postgres / sqlite / files / vector store)?
- 10G — это weights/embeddings/vector store/document cache?
- влияет на RAM-выбор тарифа (если local LLM inference — 8GB мало, нужно 16+)
- [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
- `.wiki/concepts/recovery-architecture-snapshot.md` — текущая prod-инфра
## Decisions log
- **2026-05-19:** OS на VDS = **Ubuntu 24.04 LTS** (Noble). Reasoning: LTS support до апреля 2029, docker official APT flow, mainstream community для docker-workload. Debian 12 отвергнут — не даёт material выгоды при 8GB RAM (idle разница ~100M), а docker-docs primary-target — Ubuntu LTS.
- **2026-05-19 (заказан):** Тариф `160 NVMe` (Rusonyx переименовал 160 SSD → 160 NVMe, та же цена, апгрейд по IOPS). VPS Backup от Rusonyx — 0 шт (свой backup pipeline). SSH root — Вкл на bootstrap, потом отключить.
- **2026-05-19 (final tier):** User выбрал **160 SSD (2500 ₽/мес)**. Reasoning user'а — operational simplicity, не звонить в support за add-on ценой, есть запас на 2-3 года. +1000 ₽/мес vs 80 SSD приемлемо.
- **2026-05-19 (промежуточный заход, откатан):** Рекомендовалось 80 SSD + disk add-on исходя из того что 8GB RAM избыточно после уточнения hermes-профиля. User решил что 500 ₽/мес экономии не стоят звонков в support и риска что add-on окажется дорогой.
- **2026-05-19 (первый заход):** Изначально 160 SSD по budget-расчёту headroom; затем переоценено вниз после уточнения hermes; затем user вернул обратно к 160 SSD по user-preference.
- **2026-05-19:** Owncloud → seafile. Reasoning: пользователь решил заменить (rationale пока не записан).
- **2026-05-19:** Registry **garbage-collect ДО миграции** (на kreknin), чтобы не тащить 99G чтобы потом всё равно чистить.
- **2026-05-19:** Verdaccio тоже prune ДО миграции, по аналогичной логике.
## Next actions (по порядку)
1. **Уточнить hermes** — что это, где его state.
2. **Решить owncloud дубль** — нужны ли данные для миграции в seafile или green install.
3. **Registry GC на kreknin** — stop писателей, `docker run --rm -v ...:/var/lib/registry registry:2 garbage-collect /etc/docker/registry/config.yml`, measure delta. ⚠️ prod-changing, согласие от user.
4. **Verdaccio prune** — `npm cache clean` + удалить old tarballs из storage. (low risk, restorable из upstream npm)
5. ~~**Закупить тариф у Rusonyx**~~ → **заказан 160 NVMe, Ubuntu 24.04, 8GB/6vCPU/160GB, 1 IPv4** (2026-05-19, ожидание выделения IP).
6. **DNS** — A `vds.kzntsv.site → <IP>` в REGRU.
7. **Bootstrap VDS** (zero-day чек-лист): apt update/upgrade → создать sudo-user `vitya` + копировать SSH key → `PermitRootLogin no` + `PasswordAuthentication no` → ufw (22/80/443 only) → fail2ban → docker official APT repo (docker-ce + buildx + compose-plugin). Полная paste-ready команда в чате сессии.
8. **Per-service migration** (gitea → tarball+restore, verdaccio → tar storage, seafile → green install, registry → rsync, hermes → repo+state).
9. **Backup pipeline** VDS → kreknin (или cloud — Backblaze B2 для off-site, см. [[future-resilient-architecture-goals]]).
10. **DNS cut** к production, удаление сервисов с kreknin (с paranoid keep-on-kreknin-7days).