backup-master: сервер бэкапов, агент и восстановление VPS

Один бинарник backupctl:
- server: API для агентов и restore, веб-морда (один админ), SQLite,
  ретеншн GFS, heavy-части, уведомления ntfy, раздача agent.sh/restore.sh;
- backup/plan: сбор по каталогу категорий (files/systemd/xui/compose),
  горячие дампы Postgres/Mongo/MySQL, снимок x-ui.db, docker-тома,
  digest образов, проверка crontab, потоковая отправка tar.zst;
- restore: провижининг на основе vps_installer с теми же версиями пакетов,
  recipes 3x-ui/nebula/sing-box нужных версий, восстановление compose
  с заливкой дампов и закреплением образов по digest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-06 21:52:40 +03:00
commit 1d44d067e9
53 files changed
+7278

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
# Устройство backup-master
## Компоненты
```
Старый сервер Сервер бэкапов Новый сервер
backupctl backup ──PUT части──► backupctl server ◄── backupctl restore
(systemd timer) SQLite + файлы, веб-морда provision.sh → recipes → распаковка → сервисы
ретеншн, ntfy
```
| Пакет | Назначение |
|---|---|
| `internal/manifest` | формат манифеста бэкапа (общий для всех) |
| `internal/catalog` | категории бэкапа: встроенный `default.yaml` + переопределения клиента |
| `internal/archive` | tar (PAX) + zstd: запись, распаковка, исключения, владельцы по имени |
| `internal/api` | протокол и HTTP-клиент |
| `internal/agent` | сбор: files / systemd / xui / compose, дампы, проверка crontab, отправка |
| `internal/restore` | восстановление: provision.sh, recipes бинарников, compose, порядок категорий |
| `internal/server` | API, веб-морда, ретеншн, уведомления |
## Формат архива
Каждая часть — `tar.zst`. В main первой записью идёт `manifest.json`. Пути внутри архива:
- `<cat>/files/<абсолютный путь>` — восстанавливается в `/`;
- `<cat>/dumps/<файл>` — дампы БД и снимок x-ui.db;
- `<cat>/volumes/<том>/…` — именованные docker-тома;
- `<cat>/bin/<абсолютный путь>` — fallback-копии бинарников.
Heavy-часть называется именем категории и устроена так же. Main без неё самодостаточен.
## Протокол
| Метод | Токен | Назначение |
|---|---|---|
| `GET /api/v1/agent/config` | агент | домен, переопределения каталога, пора ли слать heavy |
| `POST /api/v1/backups` | агент | манифест → id |
| `PUT /api/v1/backups/{id}/parts/{name}` | агент | потоковая загрузка части |
| `POST /api/v1/backups/{id}/complete` | агент | sha256/размеры частей + предупреждения архивации |
| `POST /api/v1/backups/{id}/fail` | агент | ошибка |
| `GET /api/v1/restore/manifest[?backup=]` | restore | манифест и доступные части |
| `GET /api/v1/restore/{id}/parts/{name}` | restore | скачивание (Range — докачка) |
Агентский токен даёт только запись: если клиента взломают, злоумышленник не сможет прочитать или удалить его историю. Restore-токен выпускается в морде на 24 часа и даёт только чтение бэкапов одного клиента. В БД хранятся только sha256 токенов.
Агент архивирует часть прямо в тело PUT (`io.Pipe`), временный архив на диске не создаётся. Дампы БД снимаются **до** архивации. Поэтому в Stoat порядок «сначала mongodump, потом файлы MinIO» соблюдается сам собой: файлы, загруженные во время бэкапа, станут лишними объектами без ссылок, но ссылок на отсутствующие файлы не появится.
## Compose
- Проекты берутся из `docker compose ls -a`, к ним добавляются явные `dir` из каталога. Сервисы, bind-mounts и тома — из `docker compose config --format json`.
- БД определяются по образу. Если контейнер запущен, делается дамп через `docker exec`: учётные данные берутся из окружения контейнера, а каталог данных исключается. Если остановлен — каталог данных копируется как есть (он консистентен).
- Данные `rabbitmq` и сервисов из `skip_services` не бэкапятся.
- Записываются digest запущенных образов. При восстановлении создаётся `compose.restore.yml` с `image: repo@sha256:…`, потому что `latest` на новом сервере дал бы другую Mongo/MinIO.
- Отсутствующие external-сети создаются пустыми. Так stoat-mbot поднимается без psiphon.
- Порядок восстановления: `pull` → `build` → `create` → для каждой БД `up -d <db>` → ожидание TCP-готовности → заливка дампа → `up -d`.
## Версии
| Что | Как записывается | Как восстанавливается |
|---|---|---|
| apt-пакеты (caddy, telegraf, docker-ce, docker-compose-plugin) | `dpkg-query` | `provision.sh` ставит `pkg=version`, если версия есть в репозитории |
| 3x-ui, nebula, sing-box-extended | `version_cmd` | recipes в `internal/restore/recipes.go`: release-архивы нужного тега; при неудаче — копия из `bin/` |
| образы compose | digest | `compose.restore.yml` |
## Расширение
- Новая категория — запись в `default.yaml` или в YAML клиента в морде.
- Новый способ установки бинарника — функция в `recipes`, имя указывается в поле `install` категории.
- Новый тип БД в compose — `dbKind`/`dumpCmds` в `internal/agent/compose.go` и `dbReady`/`dbRestore` в `internal/restore/compose.go`.