- 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>
225 lines
20 KiB
Markdown
225 lines
20 KiB
Markdown
# 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 | 2–3G | prune old tarballs до миграции |
|
||
| owncloud | `/volume1/docker/owncloud/` 187M + дубль 195M | — | **отказ**, заменяется seafile |
|
||
| seafile | (новый) | 30–40G | green install на VDS |
|
||
| registry | `/volume1/docker/infrastucture/registry/` 99G | 5–15G | **`registry garbage-collect` ДО миграции** (write op, требует read-only/stopped registry) |
|
||
| hermes | `/volume1/docker/hermes/` 0 (пусто на kreknin) | ~10G (user estimate) | TBD — что это, где state |
|
||
|
||
Раскладка после cleanup: ~55–75G data + ~5G OS/docker → ~80–100G с 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).
|