deploy/backup/README.md — подробная пошаговая инструкция по всему, что делается вручную: пароль шифрования, SSH-ключ, VPS (пользователь fsbackup только для SFTP, проверки sshd), сборка образов, включение на Pi (с разбором каждой переменной и контрольными проверками), доступ с ПК и выгрузка, учебное восстановление на тест-клоне, восстановление прода и из архива, катастрофа «Pi умер», повседневные действия, таблица неполадок, справочник, чек-лист. Команды разделов VPS и Pi прогнаны на локальном стенде. deploy/pi, deploy/vps §8, deploy/README — ссылки на новую схему. Удалены scripts/backup.sh и scripts/restore.sh (restore.sh ещё и оставлял БД root-овой: docker cp пишет файлы с uid 0); их архивы fs_*.tar.gz восстанавливаются через fs-backup import. #64 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jBxs9nBCk5nBdTLzcGz91
981 lines
56 KiB
Markdown
981 lines
56 KiB
Markdown
# Бэкапы Forbidden Stars
|
||
|
||
Пошаговая инструкция: как включить бэкапы, проверить, что они работают, скачать их на ПК
|
||
и восстановить данные — в том числе на новом Pi, если старый умер.
|
||
|
||
Команды даны целиком — копируйте их как есть. Где нужно подставить своё значение, это
|
||
написано в угловых скобках: `<IP-адрес-Pi>`. Каждый шаг заканчивается блоком
|
||
**«Что должно получиться»** — не переходите к следующему шагу, пока не получили то же самое.
|
||
|
||
## Содержание
|
||
|
||
0. [Как это устроено](#0-как-это-устроено)
|
||
1. [Пароль шифрования](#шаг-1-пароль-шифрования) — ПК
|
||
2. [SSH-ключ для VPS](#шаг-2-ssh-ключ-для-vps) — ПК
|
||
3. [VPS: пользователь только для SFTP](#шаг-3-vps-пользователь-только-для-sftp) — VPS
|
||
4. [Сборка и публикация образов](#шаг-4-сборка-и-публикация-образов) — ПК
|
||
5. [Pi: включить бэкапы](#шаг-5-pi-включить-бэкапы) — Pi
|
||
6. [ПК: доступ к Pi и выгрузка бэкапов](#шаг-6-пк-доступ-к-pi-и-выгрузка-бэкапов) — ПК
|
||
7. [Учебное восстановление на тест-клоне](#шаг-7-учебное-восстановление-на-тест-клоне) — ПК
|
||
8. [Восстановление прода](#8-восстановление-прода) — Pi
|
||
9. [Катастрофа: Pi умер](#9-катастрофа-pi-умер) — новый Pi
|
||
10. [Повседневные действия](#10-повседневные-действия)
|
||
11. [Неполадки](#11-неполадки)
|
||
12. [Справочник: команды и переменные](#12-справочник-команды-и-переменные)
|
||
13. [Итоговый чек-лист](#13-итоговый-чек-лист)
|
||
|
||
---
|
||
|
||
## 0. Как это устроено
|
||
|
||
```
|
||
Raspberry Pi (прод) VPS 186.246.51.17
|
||
┌──────────────────────────────────────────┐ ┌─────────────────────────────┐
|
||
│ app ──► тома: БД, uploads, achievements │ │ /srv/fs-backups/restic │
|
||
│ ▲ читает (консистентно) │ SFTP │ (копия №2, зашифрована) │
|
||
│ backup ──────┘ │ ────────────► │ пользователь fsbackup: │
|
||
│ │ каждую ночь в 04:00 │ (Pi сам │ только SFTP, без shell │
|
||
│ ▼ │ ходит └─────────────────────────────┘
|
||
│ том backup-data (копия №1, зашифрована) │ наружу)
|
||
└──────────────────────────────────────────┘
|
||
▲ ssh + scp по команде «pull»
|
||
┌─────────┴────────────────────────────────┐
|
||
│ ПК: backups\fs_<дата>_<id>.tar │ копия №3, по запросу, НЕ зашифрована
|
||
└──────────────────────────────────────────┘
|
||
```
|
||
|
||
На Pi работает третий контейнер — `backup`. В нём [restic](https://restic.net) — известная
|
||
программа для бэкапов. Каждую ночь контейнер:
|
||
|
||
1. снимает **консистентную** копию БД. Сайт при этом работает, пользователи ничего не замечают;
|
||
2. проверяет копию (`PRAGMA integrity_check`). Битая копия не сохраняется, старые снимки
|
||
тоже не трогаются;
|
||
3. сохраняет **снимок** — БД + загруженные фото (`uploads`) + титулы (`achievements`) — в
|
||
два места: на сам Pi и на VPS;
|
||
4. удаляет старые снимки по правилам хранения;
|
||
5. проверяет, что репозитории целы.
|
||
|
||
Раз в неделю (воскресенье, 05:30) дополнительно читает часть сохранённых данных и убеждается,
|
||
что последний снимок действительно восстанавливается.
|
||
|
||
**Словарь**
|
||
|
||
| Слово | Что значит |
|
||
|---|---|
|
||
| **Снимок** (snapshot) | Состояние данных на момент бэкапа. У каждого есть короткий ID, например `3f2a9c1d`. |
|
||
| **Репозиторий** | Хранилище снимков. У нас их два: `local` (на Pi) и `vps` (на VPS). |
|
||
| **Пароль шифрования** | Им зашифрованы оба репозитория. Без него снимки прочитать нельзя — **никак**. |
|
||
| **Хранение** | Сколько снимков остаётся: последний за каждый из 14 дней, за каждую из 8 недель, за каждый из 12 месяцев, плюс 3 самых свежих. Одинаковые данные хранятся один раз (дедупликация), поэтому 30+ снимков занимают немногим больше одного. |
|
||
| **Именованный снимок** | Снимок, сделанный вручную с меткой (`--tag before-update`). Автоматически не удаляется. |
|
||
| **pre-restore** | Страховочный снимок, который автоматически делается перед каждым восстановлением: «как было до». Автоматически не удаляется. |
|
||
|
||
**Что попадает в снимок:** БД, `uploads`, `achievements`.
|
||
**Что НЕ попадает:** файл `.env` с секретами (пароли, токен бота, ключи). Его копию храните
|
||
отдельно — см. [шаг 1](#шаг-1-пароль-шифрования).
|
||
|
||
**Ограничения, о которых стоит знать**
|
||
|
||
- Копии на ПК (`backups\*.tar`) **не зашифрованы**: там данные игроков. Не выкладывайте их
|
||
никуда и удаляйте ненужные.
|
||
- Ключ, которым Pi заходит на VPS, умеет и удалять файлы в `/srv/fs-backups`. Если Pi будет
|
||
взломан, злоумышленник сможет удалить копию на VPS. Поэтому раз в месяц полезно скачивать
|
||
снимок на ПК ([раздел 10](#10-повседневные-действия)).
|
||
|
||
---
|
||
|
||
## Шаг 1. Пароль шифрования
|
||
|
||
**Где:** ПК. **Сколько времени:** 5 минут.
|
||
|
||
> ### ⚠️ Самое важное во всей инструкции
|
||
> Потеряете пароль — **ни один бэкап не восстановить**. Ни на Pi, ни на VPS. Никакого
|
||
> «сброса пароля» у restic нет, и это не баг, а суть шифрования.
|
||
|
||
1. Откройте PowerShell и сгенерируйте пароль:
|
||
|
||
```powershell
|
||
python -c "import secrets; print(secrets.token_urlsafe(32))"
|
||
```
|
||
|
||
Если `python` не найден, подойдёт такая команда:
|
||
|
||
```powershell
|
||
$b = New-Object byte[] 32; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); [Convert]::ToBase64String($b).TrimEnd('=').Replace('+','-').Replace('/','_')
|
||
```
|
||
|
||
2. Сохраните результат в менеджер паролей (Bitwarden, KeePass, 1Password…), запись
|
||
назовите, например, **«Forbidden Stars — пароль бэкапов (restic)»**.
|
||
3. Сделайте вторую копию вне компьютера: распечатайте или запишите на бумагу и уберите
|
||
в надёжное место.
|
||
4. **Рекомендуется:** в той же записи менеджера паролей храните копию файла `.env` с Pi
|
||
(после шага 5). При гибели Pi это сэкономит час восстановления секретов.
|
||
|
||
**Что должно получиться:** строка из ~43 символов (латиница, цифры, `-`, `_`), сохранённая
|
||
в менеджере паролей и на бумаге.
|
||
|
||
> В пароле не должно быть символа `$` — docker compose воспринимает его как переменную.
|
||
> Обе команды выше `$` не генерируют.
|
||
|
||
---
|
||
|
||
## Шаг 2. SSH-ключ для VPS
|
||
|
||
**Где:** ПК, PowerShell, папка репозитория. **Зачем:** этим ключом контейнер `backup`
|
||
на Pi будет заходить на VPS. Пароль к ключу не ставим: контейнер работает без человека.
|
||
|
||
1. Перейдите в папку репозитория (подставьте свой путь):
|
||
|
||
```powershell
|
||
cd C:\Users\<вы>\ForbidenStarsApp
|
||
```
|
||
|
||
2. Создайте ключ:
|
||
|
||
```powershell
|
||
ssh-keygen -t ed25519 -C "fs-backup" -f deploy\backup\id_backup
|
||
```
|
||
|
||
На вопросы `Enter passphrase` и `Enter same passphrase again` просто **дважды нажмите
|
||
Enter** (пароль к ключу пустой).
|
||
|
||
3. Проверьте, что появились два файла:
|
||
|
||
```powershell
|
||
Get-ChildItem deploy\backup\id_backup*
|
||
Get-Content deploy\backup\id_backup.pub
|
||
```
|
||
|
||
4. Проверьте, что ключ не попадёт в git (команда должна вывести **пустоту**):
|
||
|
||
```powershell
|
||
git status --short deploy\backup
|
||
```
|
||
|
||
**Что должно получиться:**
|
||
- файлы `id_backup` (приватный, секрет) и `id_backup.pub` (публичный);
|
||
- содержимое `id_backup.pub` — одна строка вида `ssh-ed25519 AAAAC3Nza…много символов… fs-backup`;
|
||
- `git status` по этой папке ничего не показывает.
|
||
|
||
---
|
||
|
||
## Шаг 3. VPS: пользователь только для SFTP
|
||
|
||
**Где:** VPS. **Зачем:** отдельный пользователь `fsbackup` сможет только класть и читать
|
||
файлы бэкапов по SFTP: без shell, без туннелей, без входа по паролю.
|
||
|
||
> Почему `fsbackup`, а не `backup`: в Debian/Ubuntu системный пользователь `backup` уже
|
||
> существует (он служебный, домашняя папка `/var/backups`) — его трогать нельзя.
|
||
|
||
1. Зайдите на VPS (с ПК):
|
||
|
||
```powershell
|
||
ssh root@186.246.51.17
|
||
```
|
||
|
||
Все команды ниже выполняются **на VPS** от root. Заходите не под root — добавляйте
|
||
`sudo` перед каждой командой.
|
||
|
||
2. Убедитесь, что такого пользователя ещё нет:
|
||
|
||
```bash
|
||
id fsbackup
|
||
```
|
||
|
||
Ожидается `id: 'fsbackup': no such user`. Если пользователь уже есть — пропустите пункт 3.
|
||
|
||
3. Создайте пользователя и папки:
|
||
|
||
```bash
|
||
useradd --create-home --shell /usr/sbin/nologin fsbackup
|
||
install -d -m 700 -o fsbackup -g fsbackup /home/fsbackup/.ssh
|
||
install -m 600 -o fsbackup -g fsbackup /dev/null /home/fsbackup/.ssh/authorized_keys
|
||
install -d -m 700 -o fsbackup -g fsbackup /srv/fs-backups
|
||
```
|
||
|
||
4. Добавьте публичный ключ из шага 2.
|
||
- **На ПК**, во втором окне PowerShell в папке репозитория, скопируйте ключ в буфер обмена:
|
||
|
||
```powershell
|
||
Get-Content deploy\backup\id_backup.pub | Set-Clipboard
|
||
```
|
||
|
||
- **На VPS** наберите команду ниже, вставив ключ вместо `ВСТАВЬТЕ_КЛЮЧ`. Вставка в
|
||
терминале — правая кнопка мыши или `Ctrl+Shift+V`. Одинарные кавычки оставьте:
|
||
|
||
```bash
|
||
echo 'ВСТАВЬТЕ_КЛЮЧ' >> /home/fsbackup/.ssh/authorized_keys
|
||
cat /home/fsbackup/.ssh/authorized_keys
|
||
```
|
||
|
||
`cat` должен показать одну строку, которая начинается с `ssh-ed25519` и заканчивается на `fs-backup`.
|
||
|
||
5. Запретите этому пользователю всё, кроме SFTP. Скопируйте блок **целиком**, от `cat` до
|
||
последнего `EOF` включительно:
|
||
|
||
```bash
|
||
cat > /etc/ssh/sshd_config.d/60-fs-backup.conf <<'EOF'
|
||
Match User fsbackup
|
||
ForceCommand internal-sftp -d /srv/fs-backups
|
||
PasswordAuthentication no
|
||
AllowTcpForwarding no
|
||
AllowAgentForwarding no
|
||
PermitTunnel no
|
||
X11Forwarding no
|
||
PermitTTY no
|
||
EOF
|
||
```
|
||
|
||
6. Убедитесь, что основной конфиг подключает папку `sshd_config.d`:
|
||
|
||
```bash
|
||
grep -n '^Include' /etc/ssh/sshd_config
|
||
```
|
||
|
||
Ожидается строка `Include /etc/ssh/sshd_config.d/*.conf`. **Если вывода нет** (старая
|
||
система), допишите блок в конец основного конфига:
|
||
`cat /etc/ssh/sshd_config.d/60-fs-backup.conf >> /etc/ssh/sshd_config`.
|
||
|
||
7. Проверьте конфиг и примените его. Текущая SSH-сессия при этом не оборвётся:
|
||
|
||
```bash
|
||
sshd -t && echo "конфиг OK"
|
||
systemctl reload ssh || systemctl reload sshd
|
||
```
|
||
|
||
Если `sshd -t` вывел ошибку, **не выходите из сессии** и исправьте файл
|
||
(`nano /etc/ssh/sshd_config.d/60-fs-backup.conf`). Ошибка в конфиге sshd может закрыть
|
||
вход на сервер.
|
||
|
||
8. Проверьте, что правила действуют только на `fsbackup`:
|
||
|
||
```bash
|
||
sshd -T -C user=fsbackup,host=x,addr=1.2.3.4 | grep -i forcecommand
|
||
sshd -T -C user=root,host=x,addr=1.2.3.4 | grep -i forcecommand
|
||
```
|
||
|
||
Первая команда должна показать `forcecommand internal-sftp -d /srv/fs-backups`,
|
||
вторая — `forcecommand none`.
|
||
|
||
9. **С ПК** (новое окно PowerShell в папке репозитория) проверьте вход по SFTP:
|
||
|
||
```powershell
|
||
sftp -i deploy\backup\id_backup fsbackup@186.246.51.17
|
||
```
|
||
|
||
При первом подключении ответьте `yes` на вопрос `Are you sure you want to continue
|
||
connecting`. Появится приглашение `sftp>`. Наберите `pwd`, затем `bye`.
|
||
|
||
10. **С ПК** проверьте, что shell закрыт:
|
||
|
||
```powershell
|
||
ssh -i deploy\backup\id_backup fsbackup@186.246.51.17
|
||
```
|
||
|
||
**Что должно получиться:**
|
||
- `sshd -t` → `конфиг OK`;
|
||
- в `sftp` команда `pwd` отвечает `Remote working directory: /srv/fs-backups`;
|
||
- `ssh` из пункта 10 отвечает `This service allows sftp connections only.` и сразу отключается;
|
||
- вход root на VPS работает как раньше (проверьте новым окном: `ssh root@186.246.51.17`).
|
||
|
||
---
|
||
|
||
## Шаг 4. Сборка и публикация образов
|
||
|
||
**Где:** ПК с Docker Desktop, папка репозитория. **Зачем:** на Pi нет сборки — он скачивает
|
||
готовые образы из реестра Gitea. Новый образ `forbidden-stars-backup` нужно туда положить.
|
||
|
||
> `build-push` собирает **все три** образа (app, tunnel, backup) из текущей ветки ПК и
|
||
> публикует их с тегом из `IMAGE_TAG`. Убедитесь, что вы на ветке, которая должна быть на
|
||
> проде (обычно `main` после релиза): `git branch --show-current`.
|
||
|
||
1. Один раз войдите в реестр (если уже входили — пропустите):
|
||
|
||
```powershell
|
||
docker login gitea.arseniev.info
|
||
```
|
||
|
||
2. Соберите и опубликуйте образы (первый раз — 5–15 минут):
|
||
|
||
```powershell
|
||
.\scripts\build-push.ps1
|
||
```
|
||
|
||
3. Проверьте, что образ бэкапа есть в реестре и собран под arm64:
|
||
|
||
```powershell
|
||
docker buildx imagetools inspect gitea.arseniev.info/notbigghost/forbidden-stars-backup:latest
|
||
```
|
||
|
||
**Что должно получиться:**
|
||
- `build-push.ps1` заканчивается зелёной строкой `Done. On the Pi: ...`;
|
||
- `imagetools inspect` показывает `Platform: linux/arm64`.
|
||
|
||
---
|
||
|
||
## Шаг 5. Pi: включить бэкапы
|
||
|
||
**Где:** Pi. **Сколько времени:** 15 минут.
|
||
|
||
1. Зайдите на Pi и перейдите в папку прода:
|
||
|
||
```powershell
|
||
ssh pi@<IP-адрес-Pi>
|
||
```
|
||
|
||
```bash
|
||
cd ~/forbidden-stars
|
||
ls
|
||
```
|
||
|
||
Должны быть видны `docker-compose.yml` и `.env`. Если папка называется иначе, дальше
|
||
везде используйте своё название.
|
||
|
||
2. Сохраните копию текущего compose-файла — на случай отката:
|
||
|
||
```bash
|
||
cp docker-compose.yml docker-compose.yml.bak-$(date +%F)
|
||
```
|
||
|
||
3. Скачайте новый `docker-compose.yml`. Замените `main` на `dev`, если функционал ещё не
|
||
попал в релиз:
|
||
|
||
```bash
|
||
BRANCH=main
|
||
curl -fsSLO https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/$BRANCH/docker-compose.yml
|
||
grep -n '^ backup:' docker-compose.yml
|
||
```
|
||
|
||
`grep` должен найти строку ` backup:`. Если не нашёл, в этой ветке функционала ещё нет.
|
||
|
||
4. Посмотрите, какие `BACKUP_*` уже есть в `.env`:
|
||
|
||
```bash
|
||
grep -n '^BACKUP_' .env
|
||
```
|
||
|
||
- Есть **старые** строки (`BACKUP_VPS_USER=backup`, `BACKUP_VPS_DIR=/srv/fs-backups`,
|
||
`BACKUP_VPS_KEY`, `BACKUP_KEEP_LOCAL`, `BACKUP_KEEP_REMOTE`)? **Удалите их все** в
|
||
пункте 5: они указывают на неправильного пользователя и папку.
|
||
- Уже есть **новые** строки (`BACKUP_PASSWORD`, `BACKUP_SSH_KEY_B64` и т.д. — если `.env`
|
||
делался из свежего `.env.example`)? Блок ниже **не дописывайте**, а заполните
|
||
существующие строки теми же значениями. Иначе переменные задвоятся.
|
||
|
||
5. Откройте `.env` и добавьте в конец блок ниже:
|
||
|
||
```bash
|
||
nano .env
|
||
```
|
||
|
||
В nano: стрелками вниз до конца файла, вставка — правая кнопка мыши, сохранить —
|
||
`Ctrl+O`, затем `Enter`, выйти — `Ctrl+X`.
|
||
|
||
```ini
|
||
# ─── БЭКАПЫ ───
|
||
BACKUP_PASSWORD=<пароль из шага 1>
|
||
BACKUP_SCHEDULE="0 4 * * *"
|
||
BACKUP_VERIFY_SCHEDULE="30 5 * * 0"
|
||
BACKUP_TZ=Europe/Moscow
|
||
BACKUP_KEEP_DAILY=14
|
||
BACKUP_KEEP_WEEKLY=8
|
||
BACKUP_KEEP_MONTHLY=12
|
||
BACKUP_COMPRESSION=max
|
||
BACKUP_MAX_AGE_HOURS=30
|
||
BACKUP_VPS_HOST=186.246.51.17
|
||
BACKUP_VPS_USER=fsbackup
|
||
BACKUP_VPS_PORT=22
|
||
BACKUP_VPS_DIR=/srv/fs-backups/restic
|
||
BACKUP_SSH_KEY_B64=<длинная строка base64, см. ниже>
|
||
```
|
||
|
||
**Как получить `BACKUP_SSH_KEY_B64`.** На ПК, в PowerShell в папке репозитория:
|
||
|
||
```powershell
|
||
[Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path "deploy\backup\id_backup"))) | Set-Clipboard
|
||
[Text.Encoding]::ASCII.GetString([Convert]::FromBase64String((Get-Clipboard))).Split("`n")[0]
|
||
```
|
||
|
||
Вторая команда проверяет содержимое буфера. Она должна вывести
|
||
`-----BEGIN OPENSSH PRIVATE KEY-----`. Теперь вставьте строку из буфера в `.env` после
|
||
`BACKUP_SSH_KEY_B64=`: **одной строкой, без пробелов и кавычек**.
|
||
|
||
**Что означает каждая строка**
|
||
|
||
| Переменная | Значение | Что делает |
|
||
|---|---|---|
|
||
| `BACKUP_PASSWORD` | пароль из шага 1 | шифрует оба репозитория; пусто = бэкапы выключены |
|
||
| `BACKUP_SCHEDULE` | `"0 4 * * *"` | когда делать бэкап: минута, час, день, месяц, день недели → каждый день в 04:00 |
|
||
| `BACKUP_VERIFY_SCHEDULE` | `"30 5 * * 0"` | когда проверять данные: воскресенье 05:30 |
|
||
| `BACKUP_TZ` | `Europe/Moscow` | часовой пояс расписания и времени снимков |
|
||
| `BACKUP_KEEP_DAILY/WEEKLY/MONTHLY` | `14` / `8` / `12` | сколько дней / недель / месяцев хранить снимки |
|
||
| `BACKUP_COMPRESSION` | `max` | сжатие restic: `auto` — быстрее, `max` — плотнее, `off` — без сжатия |
|
||
| `BACKUP_MAX_AGE_HOURS` | `30` | если успешного бэкапа не было дольше — контейнер помечается `unhealthy` |
|
||
| `BACKUP_VPS_HOST` | `186.246.51.17` | адрес VPS; пусто = копия только на Pi |
|
||
| `BACKUP_VPS_USER` | `fsbackup` | пользователь из шага 3 |
|
||
| `BACKUP_VPS_PORT` | `22` | SSH-порт VPS |
|
||
| `BACKUP_VPS_DIR` | `/srv/fs-backups/restic` | папка репозитория на VPS (создастся сама) |
|
||
| `BACKUP_SSH_KEY_B64` | base64 ключа | приватный ключ из шага 2 одной строкой |
|
||
|
||
6. Проверьте `.env`, **не показывая секреты на экране**:
|
||
|
||
```bash
|
||
grep -c '^BACKUP_' .env
|
||
grep '^BACKUP_SSH_KEY_B64=' .env | wc -c
|
||
grep '^BACKUP_PASSWORD=' .env | grep -c '\$'
|
||
docker compose config --quiet && echo "compose OK"
|
||
```
|
||
|
||
Ожидается:
|
||
- `14` (или `16`, если в `.env` есть ещё `BACKUP_PI_SSH` и `BACKUP_PI_DIR` из нового шаблона —
|
||
на Pi они не используются и не мешают);
|
||
- число больше `400`;
|
||
- `0`;
|
||
- `compose OK`.
|
||
|
||
Проверить, что ни одна переменная не задвоилась (вывод должен быть **пустым**):
|
||
|
||
```bash
|
||
grep -o '^BACKUP_[A-Z_0-9]*' .env | sort | uniq -d
|
||
```
|
||
|
||
7. Запустите **только** контейнер бэкапа. Сайт при этом не перезапускается:
|
||
|
||
```bash
|
||
docker compose up -d backup
|
||
```
|
||
|
||
8. Смотрите журнал первого запуска (выход — `Ctrl+C`, контейнер продолжит работать):
|
||
|
||
```bash
|
||
docker compose logs -f backup
|
||
```
|
||
|
||
Первый бэкап начинается сразу. В журнале должны появиться, в таком порядке:
|
||
- `Снимков в локальном репозитории ещё нет — делаю первый бэкап сразу.`
|
||
- `БД в порядке: игроков N, партий M.`
|
||
- `Репозиторий local ещё не создан — создаю …`
|
||
- `OK: репозиторий local, снимок xxxxxxxx.`
|
||
- `Репозиторий vps ещё не создан — создаю (sftp:fs-vps:/srv/fs-backups/restic …`
|
||
- `OK: репозиторий vps, снимок yyyyyyyy.`
|
||
- `Бэкап завершён.`
|
||
- `Расписание (TZ=Europe/Moscow):` и две строки расписания.
|
||
|
||
9. Проверьте состояние и хронологию:
|
||
|
||
```bash
|
||
docker compose exec backup fs-backup status
|
||
docker compose exec backup fs-backup list
|
||
docker compose exec backup fs-backup list vps
|
||
docker compose ps backup
|
||
```
|
||
|
||
10. На VPS убедитесь, что копия пришла (там только зашифрованные файлы restic):
|
||
|
||
```bash
|
||
ls -la /srv/fs-backups/restic
|
||
```
|
||
|
||
**Что должно получиться:**
|
||
- в `status` у `[local]` и `[vps]` есть строка `Последний бэкап: <сегодня> — снимок …`;
|
||
- `list` и `list vps` показывают по одному снимку с верным числом игроков и партий;
|
||
- на VPS в `/srv/fs-backups/restic` лежат `config`, `data`, `index`, `keys`, `snapshots`;
|
||
- `docker compose ps backup` показывает `Up`. Первые ~10 минут статус `(health: starting)`,
|
||
затем `(healthy)`.
|
||
|
||
---
|
||
|
||
## Шаг 6. ПК: доступ к Pi и выгрузка бэкапов
|
||
|
||
**Где:** ПК. **Зачем:** одной командой скачивать снимок с Pi на ПК — третья копия, которая
|
||
не зависит ни от Pi, ни от VPS.
|
||
|
||
1. Проверьте вход на Pi **без пароля**:
|
||
|
||
```powershell
|
||
ssh pi@<IP-адрес-Pi> "echo ok && cd ~/forbidden-stars && docker compose ps backup"
|
||
```
|
||
|
||
Если выводится `ok` и строка контейнера `backup` без запроса пароля — переходите
|
||
к пункту 3.
|
||
|
||
2. Если спрашивает пароль, настройте вход по ключу (один раз).
|
||
|
||
Создайте ключ ПК, если его ещё нет. На вопросы `passphrase` дважды нажмите Enter:
|
||
|
||
```powershell
|
||
if (-not (Test-Path "$env:USERPROFILE\.ssh\id_ed25519")) { ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\id_ed25519" }
|
||
```
|
||
|
||
Передайте публичный ключ на Pi (пароль Pi спросят в последний раз):
|
||
|
||
```powershell
|
||
Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub" | ssh pi@<IP-адрес-Pi> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && tr -d '\r' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||
```
|
||
|
||
Повторите пункт 1 — пароль спрашиваться не должен.
|
||
|
||
3. Добавьте в `.env` **на ПК** (корень репозитория) или исправьте, если строки уже есть:
|
||
|
||
```ini
|
||
BACKUP_PI_SSH=pi@<IP-адрес-Pi>
|
||
BACKUP_PI_DIR=~/forbidden-stars
|
||
```
|
||
|
||
4. Проверьте скрипт:
|
||
|
||
```powershell
|
||
.\scripts\fs-backup.ps1 status
|
||
.\scripts\fs-backup.ps1 list
|
||
```
|
||
|
||
Если PowerShell пишет `running scripts is disabled on this system`, выполните один раз:
|
||
`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.
|
||
|
||
5. Скачайте последний снимок:
|
||
|
||
```powershell
|
||
.\scripts\fs-backup.ps1 pull
|
||
```
|
||
|
||
Скрипт делает всё сам:
|
||
- выгружает снимок в файл на Pi;
|
||
- копирует файл на ПК по `scp`;
|
||
- сверяет контрольную сумму sha256;
|
||
- проверяет, что внутри есть БД;
|
||
- удаляет временный файл на Pi.
|
||
|
||
6. Посмотрите, что внутри архива:
|
||
|
||
```powershell
|
||
Get-ChildItem backups\fs_*.tar
|
||
tar -tf (Get-ChildItem backups\fs_*.tar | Sort-Object LastWriteTime | Select-Object -Last 1).FullName | Select-Object -First 10
|
||
```
|
||
|
||
**Что должно получиться:**
|
||
- `pull` заканчивается зелёной строкой `OK: ...\backups\fs_<дата>_<время>_<id>.tar (… MB, N files, sha256 verified)`;
|
||
- внутри архива есть `forbidden_stars.db`, `uploads/…`, `achievements/…`.
|
||
|
||
> Архив — обычный `.tar`, его открывает 7-Zip. БД внутри — файл SQLite, его можно
|
||
> посмотреть в [DB Browser for SQLite](https://sqlitebrowser.org). Архив **не зашифрован**.
|
||
>
|
||
> Другие варианты: снимок с VPS — `.\scripts\fs-backup.ps1 pull -Repo vps`,
|
||
> конкретный снимок — `.\scripts\fs-backup.ps1 pull -Snapshot 3f2a9c1d` (ID из `list`).
|
||
|
||
---
|
||
|
||
## Шаг 7. Учебное восстановление на тест-клоне
|
||
|
||
**Где:** ПК с Docker Desktop. **Зачем:** убедиться, что бэкап действительно
|
||
восстанавливается, **до** того как это понадобится по-настоящему. Прод не затрагивается.
|
||
|
||
> Данные тест-клона на ПК будут заменены данными из архива. Прежние данные тест-клона
|
||
> сохраняются в его собственный снимок `pre-restore`.
|
||
|
||
1. Восстановите скачанный архив в тест-клон (подставьте имя своего файла):
|
||
|
||
```powershell
|
||
.\scripts\fs-backup.ps1 restore-test -File backups\fs_20260914_0400_3f2a9c1d.tar
|
||
```
|
||
|
||
В первый раз Docker соберёт образы тест-клона — это несколько минут.
|
||
|
||
2. Проверьте, что приложение тест-клона поднялось:
|
||
|
||
```powershell
|
||
docker compose -f docker-compose.test.yml ps
|
||
docker compose -f docker-compose.test.yml logs --tail 20 app
|
||
```
|
||
|
||
3. Посмотрите на сайт: в `.env` на ПК временно поставьте `APP_ENV=test` и запустите
|
||
`.\run.ps1`. Тест-клон откроется на `https://forbidden-stars.ru`: проверьте топ,
|
||
профили, историю партий. Потом верните `APP_ENV=development`.
|
||
|
||
**Что должно получиться:**
|
||
- в выводе `restore-test`:
|
||
- `Развёрнутые данные в порядке: игроков N, партий M.` — те же числа, что в `list` на Pi;
|
||
- `Данные восстановлены.`;
|
||
- `Done. The test clone now runs on the restored data.`;
|
||
- `docker compose ... ps` показывает `app` в состоянии `Up … (healthy)`;
|
||
- на сайте тест-клона — данные прода на момент снимка.
|
||
|
||
> Этим же способом можно восстановить в тест-клон старые архивы `fs_*.tar.gz` прежнего
|
||
> `scripts/backup.sh`: `.\scripts\fs-backup.ps1 restore-test -File backups\fs_20260710_140914.tar.gz`.
|
||
|
||
---
|
||
|
||
## 8. Восстановление прода
|
||
|
||
**Когда:** данные испорчены или удалены по ошибке, неудачная миграция, «откатить на вчера».
|
||
|
||
### Как это работает (почему это безопасно)
|
||
|
||
Восстановление **никогда не пишет поверх текущих данных напрямую**:
|
||
|
||
1. **Проверка.** Если приложение работает, восстановление отказывается запускаться. Затем
|
||
проверяется, хватит ли места на диске.
|
||
2. **Разворачивание.** Снимок целиком разворачивается в промежуточную папку
|
||
`.restore-new` внутри каждого тома.
|
||
3. **Проверка развёрнутого:** целостность БД, наличие таблиц, число файлов совпадает со
|
||
снимком. Любая ошибка → промежуточная папка удаляется, **текущие данные не тронуты**.
|
||
4. **Страховка.** Текущие данные сохраняются в снимок `pre-restore`.
|
||
5. **Замена.** Текущие данные переносятся в `.restore-old`, новые — на их место.
|
||
Используется переименование: мгновенно, без копирования. Сбой на этом шаге → всё
|
||
возвращается как было.
|
||
6. **Уборка.** `.restore-old` удаляется.
|
||
|
||
### Порядок действий
|
||
|
||
1. Зайдите на Pi и выберите снимок:
|
||
|
||
```bash
|
||
cd ~/forbidden-stars
|
||
docker compose exec backup fs-backup list
|
||
```
|
||
|
||
Пример вывода:
|
||
|
||
```
|
||
ID Время Игроков Партий Размер Прирост Метки
|
||
3f2a9c1d 2026-09-13 04:00 12 87 45.1 MB 12.3 KB scheduled
|
||
8b1e0f44 2026-09-14 04:00 12 88 45.2 MB 40.1 KB scheduled
|
||
```
|
||
|
||
| Столбец | Что показывает |
|
||
|---|---|
|
||
| `ID` | ID снимка — его нужно подставить в команду восстановления |
|
||
| `Время` | когда сделан снимок |
|
||
| `Игроков` / `Партий` | сколько игроков и партий было в БД в этот момент — по ним легко найти «до поломки» |
|
||
| `Размер` | полный объём данных снимка |
|
||
| `Прирост` | сколько места снимок реально добавил в репозиторий |
|
||
| `Метки` | `scheduled` — по расписанию, `manual` — вручную, `pre-restore` — страховочный, прочие — имя, данное вручную |
|
||
|
||
Если локальный репозиторий повреждён или пуст, смотрите копию на VPS:
|
||
`docker compose exec backup fs-backup list vps`.
|
||
|
||
2. **По желанию, но рекомендуется:** сначала отрепетируйте на ПК:
|
||
`.\scripts\fs-backup.ps1 pull -Snapshot <ID>`, затем `restore-test` ([шаг 7](#шаг-7-учебное-восстановление-на-тест-клоне)).
|
||
|
||
3. Остановите приложение. Сайт покажет страницу «Технические шоколадки»:
|
||
|
||
```bash
|
||
docker compose stop app
|
||
```
|
||
|
||
4. Восстановите снимок. С VPS — добавьте в конец `--repo vps`:
|
||
|
||
```bash
|
||
docker compose exec backup fs-backup restore <ID> --yes
|
||
```
|
||
|
||
5. Запустите приложение и проверьте его:
|
||
|
||
```bash
|
||
docker compose start app
|
||
docker compose ps
|
||
```
|
||
|
||
Через 30–60 секунд у `app` должно быть `(healthy)`. Откройте `https://forbiddenstars.ru`.
|
||
|
||
**Что должно получиться:** в выводе `restore` — `Развёрнутые данные в порядке: …`,
|
||
`Страховочный снимок: <id>`, `Данные восстановлены.`; сайт работает на данных из снимка.
|
||
|
||
**Если что-то пошло не так**
|
||
|
||
- `restore` закончился строкой `ОШИБКА: … текущие данные НЕ тронуты` — данные прежние,
|
||
просто запустите приложение (`docker compose start app`) и разберитесь с причиной
|
||
([раздел 11](#11-неполадки)).
|
||
- Восстановили не тот снимок — верните «как было до»: в `list` найдите самый свежий снимок
|
||
с меткой `pre-restore` и восстановите его так же (пункты 3–5).
|
||
- После старта сайт показывает заглушку дольше пары минут: `docker compose logs --tail 50 app`,
|
||
затем `docker compose restart tunnel`.
|
||
|
||
### Восстановление из файла-архива
|
||
|
||
Например, из архива, скачанного на ПК, или из старого `fs_*.tar.gz`.
|
||
|
||
1. С ПК скопируйте архив на Pi:
|
||
|
||
```powershell
|
||
scp backups\fs_20260914_0400_3f2a9c1d.tar pi@<IP-адрес-Pi>:~/
|
||
```
|
||
|
||
2. На Pi:
|
||
|
||
```bash
|
||
cd ~/forbidden-stars
|
||
docker compose cp ~/fs_20260914_0400_3f2a9c1d.tar backup:/import/fs.tar
|
||
docker compose stop app
|
||
docker compose exec backup fs-backup import /import/fs.tar --yes
|
||
docker compose exec backup rm /import/fs.tar
|
||
docker compose start app
|
||
rm ~/fs_20260914_0400_3f2a9c1d.tar
|
||
```
|
||
|
||
`import` проходит те же проверки и так же делает снимок `pre-restore`.
|
||
|
||
---
|
||
|
||
## 9. Катастрофа: Pi умер
|
||
|
||
**Когда:** Pi сгорел, SD-карта испорчена, Pi украли. Копия на Pi потеряна, остаётся VPS
|
||
(и архивы на ПК).
|
||
|
||
**Что понадобится:**
|
||
- пароль бэкапов из менеджера паролей — **без него дальше идти бессмысленно**;
|
||
- копия `.env` старого Pi (если сохраняли, [шаг 1](#шаг-1-пароль-шифрования)).
|
||
|
||
1. Подготовьте новый Pi по [`deploy/pi/README.md`](../pi/README.md), пункты 1–3: Docker,
|
||
`docker-compose.yml`, `.env`.
|
||
- Есть копия старого `.env` — просто положите её.
|
||
- Нет копии — заполните `.env` заново. **`BACKUP_PASSWORD` должен быть прежним.**
|
||
Если ключа `deploy/backup/id_backup` на ПК больше нет, сделайте новый (шаг 2), добавьте
|
||
его `.pub` на VPS (шаг 3, пункт 4) и укажите новый base64 в `BACKUP_SSH_KEY_B64`.
|
||
2. Запустите всё:
|
||
|
||
```bash
|
||
cd ~/forbidden-stars
|
||
docker compose up -d
|
||
```
|
||
|
||
3. Дождитесь, пока приложение создаст пустую БД (`docker compose ps` → `app` `(healthy)`).
|
||
В журнале бэкапа это нормально (`docker compose logs backup`):
|
||
|
||
```
|
||
В БД нет ни игроков, ни партий, а последний снимок в репозитории vps — с данными.
|
||
Похоже на новый или очищенный сервер: бэкап НЕ сделан, чтобы пустые данные не вытеснили историю.
|
||
```
|
||
|
||
Это защита: пустой новый Pi не перезапишет историю на VPS.
|
||
|
||
4. Посмотрите снимки на VPS и выберите нужный (обычно самый свежий):
|
||
|
||
```bash
|
||
docker compose exec backup fs-backup list vps
|
||
```
|
||
|
||
5. Восстановите:
|
||
|
||
```bash
|
||
docker compose stop app
|
||
docker compose exec backup fs-backup restore latest --repo vps --yes
|
||
docker compose start app
|
||
```
|
||
|
||
Вместо `latest` можно указать ID из `list vps`.
|
||
|
||
6. Проверьте сайт. Затем сделайте снимок вручную, чтобы локальная копия появилась сразу:
|
||
|
||
```bash
|
||
docker compose exec backup fs-backup run
|
||
docker compose exec backup fs-backup status
|
||
```
|
||
|
||
**Что должно получиться:** на сайте прежние данные; `run` проходит (`OK: репозиторий local…`,
|
||
`OK: репозиторий vps…`); в `status` оба репозитория с сегодняшним бэкапом.
|
||
|
||
> **VPS тоже недоступен?** Восстанавливайте из последнего архива на ПК: скопируйте его на
|
||
> новый Pi и выполните [восстановление из файла-архива](#восстановление-из-файла-архива).
|
||
|
||
---
|
||
|
||
## 10. Повседневные действия
|
||
|
||
| Задача | На Pi (`cd ~/forbidden-stars`) | С ПК (папка репозитория) |
|
||
|---|---|---|
|
||
| Состояние бэкапов | `docker compose exec backup fs-backup status` | `.\scripts\fs-backup.ps1 status` |
|
||
| Хронология снимков | `docker compose exec backup fs-backup list` (`list vps`) | `.\scripts\fs-backup.ps1 list` (`-Repo vps`) |
|
||
| Снимок перед рискованным обновлением | `docker compose exec backup fs-backup run --tag before-update` | `.\scripts\fs-backup.ps1 now -Tag before-update` |
|
||
| Проверить целостность данных | `docker compose exec backup fs-backup verify` | `.\scripts\fs-backup.ps1 verify` |
|
||
| Скачать снимок на ПК | — | `.\scripts\fs-backup.ps1 pull` |
|
||
| Журнал контейнера | `docker compose logs --tail 100 backup` | — |
|
||
|
||
**Рекомендуемый ритм**
|
||
- **Перед каждым обновлением прода** — `now -Tag before-update` (метка — латиница, цифры, `.`, `_`, `-`).
|
||
- **Раз в месяц:**
|
||
- скачать снимок на ПК (`pull`);
|
||
- раз в пару месяцев сделать учебное восстановление (`restore-test`);
|
||
- удалить с ПК старые архивы — они не зашифрованы.
|
||
- **Иногда:** посмотреть `docker compose ps`. Статус `unhealthy` у `backup` означает, что
|
||
бэкапы перестали проходить (причину покажет `fs-backup status`).
|
||
|
||
**Изменить расписание или сроки хранения.** Отредактируйте `BACKUP_*` в `.env` на Pi, затем
|
||
пересоздайте контейнер:
|
||
|
||
```bash
|
||
docker compose up -d backup
|
||
```
|
||
|
||
**Удалить именованный или pre-restore снимок.** Автоматически они не удаляются, удалять
|
||
нужно в обоих репозиториях:
|
||
|
||
```bash
|
||
docker compose exec backup fs-backup restic local forget <ID> --prune
|
||
docker compose exec backup fs-backup restic vps forget <ID-на-vps> --prune
|
||
```
|
||
|
||
ID одного и того же снимка в `local` и `vps` разные — смотрите `list` и `list vps`.
|
||
|
||
**Обновить образ бэкапа** (после изменений в `deploy/backup/`). На ПК —
|
||
`.\scripts\build-push.ps1`, на Pi — `docker compose up -d backup`.
|
||
|
||
> **Никогда не выполняйте на проде `docker compose down -v`.** Флаг `-v` удаляет тома —
|
||
> данные приложения **и** локальную копию бэкапов. Обычный `docker compose down` данные
|
||
> не трогает.
|
||
|
||
---
|
||
|
||
## 11. Неполадки
|
||
|
||
Первое, что стоит сделать при любой проблеме:
|
||
|
||
```bash
|
||
cd ~/forbidden-stars
|
||
docker compose exec backup fs-backup status
|
||
docker compose logs --tail 100 backup
|
||
```
|
||
|
||
### На Pi (журнал и команды `fs-backup`)
|
||
|
||
| Симптом | Причина | Что сделать |
|
||
|---|---|---|
|
||
| `BACKUP_PASSWORD не задан в .env — бэкапы ОТКЛЮЧЕНЫ` | нет пароля в `.env` | добавить `BACKUP_PASSWORD` (шаг 5), затем `docker compose up -d backup` |
|
||
| `неверный BACKUP_PASSWORD для репозитория …` | пароль в `.env` не тот, с которым создан репозиторий | вернуть правильный пароль из менеджера паролей; `docker compose up -d backup` |
|
||
| `BACKUP_SSH_KEY_B64 не декодируется из base64` / `— не приватный SSH-ключ` | строка ключа обрезана, с пробелами или от `.pub` | заново скопировать base64 **приватного** ключа (шаг 5, пункт 5), одной строкой |
|
||
| `Репозиторий vps недоступен` и выше `Permission denied (publickey)` | на VPS нет публичного ключа или ключ другой | шаг 3, пункты 4 и 9: проверить `authorized_keys` и вход `sftp` с ПК этим ключом |
|
||
| `Host key verification failed` | VPS переустановлен, у него новый ключ хоста | `docker compose exec backup rm /backup/state/known_hosts`, затем `docker compose exec backup fs-backup run` |
|
||
| `Репозиторий vps недоступен` и `Connection timed out` | VPS недоступен или неверный `BACKUP_VPS_HOST`/`PORT` | проверить VPS; локальная копия при этом продолжает делаться |
|
||
| `В БД нет ни игроков, ни партий, а последний снимок … — с данными` | новый или очищенный сервер — защита от затирания истории | новый Pi: [раздел 9](#9-катастрофа-pi-умер). Данные удалены намеренно: `fs-backup run --allow-empty` |
|
||
| `копия БД не прошла PRAGMA integrity_check` | живая БД повреждена | снимок не создаётся, старые целы. Восстановить последний хороший снимок ([раздел 8](#8-восстановление-прода)) |
|
||
| `приложение работает — восстанавливать поверх него нельзя` | не остановлен `app` | `docker compose stop app`, повторить команду |
|
||
| `найдены следы прерванного восстановления` | восстановление оборвалось (выключили питание и т.п.) | `docker compose stop app`, `docker compose exec backup fs-backup recover`, затем при необходимости повторить `restore` |
|
||
| `уже выполняется другая операция бэкапа` | идёт бэкап по расписанию или проверка | подождать: `docker compose logs -f backup` |
|
||
| `мало места в …` | диск Pi заполнен | `df -h`; удалить неиспользуемые образы (`docker image prune`); уменьшить `BACKUP_KEEP_*` |
|
||
| `снимок '…' не найден в репозитории` | опечатка в ID или снимок в другом репозитории | `fs-backup list` и `fs-backup list vps` |
|
||
| `service "backup" is not running` | контейнер не запущен | `docker compose up -d backup` |
|
||
| `permission denied while trying to connect to the Docker daemon socket` | пользователь Pi не в группе `docker` | `sudo usermod -aG docker $USER`, перезайти по SSH |
|
||
| `docker compose ps` → `backup` `unhealthy` | последний успешный бэкап старше `BACKUP_MAX_AGE_HOURS` | `docker compose exec backup fs-backup health` покажет причину, дальше по таблице |
|
||
|
||
**Про `recover`.** Команда сама определяет, на каком этапе оборвалось восстановление:
|
||
- если замена успела пройти во всех томах — дочищает промежуточные папки, восстановленные
|
||
данные остаются;
|
||
- иначе возвращает каждому тому прежние данные.
|
||
|
||
После неё можно спокойно запускать приложение или повторить восстановление.
|
||
|
||
**Пароль бэкапов потерян навсегда.** Прочитать существующие снимки невозможно. Начать заново
|
||
(**это удалит все старые бэкапы**):
|
||
|
||
```bash
|
||
docker compose stop backup
|
||
docker compose rm -f backup
|
||
docker volume ls | grep backup-data # имя тома, обычно forbidden-stars_backup-data
|
||
docker volume rm <имя тома>
|
||
```
|
||
|
||
На VPS: `rm -rf /srv/fs-backups/restic`. Затем новый пароль в `.env`,
|
||
`docker compose up -d backup`.
|
||
|
||
### На ПК (скрипт `fs-backup.ps1`)
|
||
|
||
| Симптом | Причина | Что сделать |
|
||
|---|---|---|
|
||
| `running scripts is disabled on this system` | политика запуска PowerShell | `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` |
|
||
| `Set BACKUP_PI_SSH in .env` | не заполнен адрес Pi | шаг 6, пункт 3 |
|
||
| `ssh: connect to host … Connection timed out` | неверный IP Pi или ПК не в той сети | проверить `ssh pi@<IP-адрес-Pi>` вручную |
|
||
| Пароль Pi спрашивается несколько раз за команду | не настроен вход по ключу | шаг 6, пункт 2 |
|
||
| `WARNING: UNPROTECTED PRIVATE KEY FILE!` | у файла ключа слишком открытые права (ключ создан в Git Bash/WSL или скопирован) | `icacls <путь к ключу> /inheritance:r /grant:r "$($env:USERNAME):(R)"` |
|
||
| `Checksum mismatch … run pull again` | файл повредился при передаче | повторить `pull` (битый файл уже удалён) |
|
||
| `Already downloaded: …` | этот снимок уже скачан | ничего не делать; нужен новый — сначала `now`, потом `pull` |
|
||
| `restore-test`: `Import failed … The test clone data was not changed` | архив повреждён или неполный | скачать заново (`pull`); текст ошибки выше в выводе |
|
||
|
||
---
|
||
|
||
## 12. Справочник: команды и переменные
|
||
|
||
### `fs-backup` — внутри контейнера
|
||
|
||
Запуск на Pi из папки прода: `docker compose exec backup fs-backup <команда>`.
|
||
|
||
| Команда | Что делает |
|
||
|---|---|
|
||
| `status` | расписание, хранение, последний бэкап и проверка по каждому репозиторию, размеры |
|
||
| `list [local\|vps]` | хронология снимков |
|
||
| `run [--tag имя] [--allow-empty]` | снимок сейчас; `--tag` — именованный (не удаляется) |
|
||
| `verify` | проверка данных репозиториев и открываемости БД последнего снимка |
|
||
| `restore <ID\|latest> [--repo local\|vps] --yes` | восстановление снимка (приложение должно быть остановлено) |
|
||
| `import /import/<файл> --yes` | восстановление из `.tar` или старого `.tar.gz` |
|
||
| `recover` | разбор прерванного восстановления |
|
||
| `export <ID\|latest> [--repo …]` | снимок в tar в stdout: `docker compose exec -T backup fs-backup export latest > fs.tar` |
|
||
| `restic <local\|vps> <аргументы>` | любая команда restic с настройками контейнера, например `restic vps stats` |
|
||
| `help` | краткая справка |
|
||
|
||
Без `--yes` команды `restore` и `import` только показывают, что собираются сделать.
|
||
|
||
### Скрипт ПК
|
||
|
||
`.\scripts\fs-backup.ps1` (Windows) или `scripts/fs-backup.sh` (Linux/macOS/Git Bash):
|
||
|
||
| Команда | Что делает |
|
||
|---|---|
|
||
| `status`, `list [-Repo vps]`, `now [-Tag имя]`, `verify` | то же, что на Pi, но с ПК |
|
||
| `pull [-Snapshot ID] [-Repo vps]` | скачать снимок в `backups\` со сверкой sha256 |
|
||
| `restore-test -File <архив>` | учебное восстановление в локальный тест-клон |
|
||
| `-Target test` | выполнить `status`/`list`/`now`/`verify`/`pull` на локальном тест-клоне |
|
||
|
||
В bash-версии те же команды пишутся так: `list vps`, `now --tag имя`,
|
||
`pull <ID> --repo vps`, `restore-test <архив>`, `--test` первым аргументом.
|
||
|
||
### Переменные `.env`
|
||
|
||
| Переменная | Где | По умолчанию | Назначение |
|
||
|---|---|---|---|
|
||
| `BACKUP_PASSWORD` | Pi | — | пароль шифрования; пусто = бэкапы выключены |
|
||
| `BACKUP_SCHEDULE` | Pi | `0 4 * * *` | расписание бэкапа (cron) |
|
||
| `BACKUP_VERIFY_SCHEDULE` | Pi | `30 5 * * 0` | расписание проверки данных |
|
||
| `BACKUP_TZ` | Pi | `Europe/Moscow` | часовой пояс |
|
||
| `BACKUP_KEEP_DAILY` / `WEEKLY` / `MONTHLY` | Pi | `14` / `8` / `12` | глубина хранения |
|
||
| `BACKUP_COMPRESSION` | Pi | `max` | сжатие restic: `auto` / `max` / `off` |
|
||
| `BACKUP_MAX_AGE_HOURS` | Pi | `30` | порог `unhealthy` |
|
||
| `BACKUP_VPS_HOST` | Pi | — | адрес VPS; пусто = без оффсайт-копии |
|
||
| `BACKUP_VPS_USER` | Pi | `fsbackup` | SFTP-пользователь |
|
||
| `BACKUP_VPS_PORT` | Pi | `22` | SSH-порт VPS |
|
||
| `BACKUP_VPS_DIR` | Pi | `/srv/fs-backups/restic` | папка репозитория на VPS |
|
||
| `BACKUP_SSH_KEY_B64` | Pi | — | приватный ключ для VPS, base64 |
|
||
| `BACKUP_MEM_LIMIT` | Pi | `384m` | лимит памяти контейнера |
|
||
| `BACKUP_PI_SSH` | ПК | — | как зайти на Pi: `pi@<IP>` |
|
||
| `BACKUP_PI_DIR` | ПК | `~/forbidden-stars` | папка прода на Pi |
|
||
|
||
У скрипта ПК переменная окружения с тем же именем важнее значения из `.env`.
|
||
|
||
### Где что лежит
|
||
|
||
| Что | Где |
|
||
|---|---|
|
||
| Локальный репозиторий | том `backup-data` на Pi → `/backup/repo` в контейнере |
|
||
| Состояние (последние запуски, ключ хоста VPS) | том `backup-data` → `/backup/state` |
|
||
| Репозиторий на VPS | `/srv/fs-backups/restic` (пользователь `fsbackup`) |
|
||
| Архивы на ПК | `backups\` в папке репозитория (в git не попадают) |
|
||
| Ключ для VPS на ПК | `deploy\backup\id_backup` (в git не попадает) |
|
||
| Код | `deploy/backup/` (образ, `fs-backup.sh`), `scripts/fs-backup.ps1` / `.sh` |
|
||
|
||
---
|
||
|
||
## 13. Итоговый чек-лист
|
||
|
||
**Настройка**
|
||
- [ ] Пароль бэкапов сохранён в менеджере паролей **и** на бумаге (шаг 1)
|
||
- [ ] Копия `.env` с Pi сохранена в менеджере паролей (шаг 1, пункт 4)
|
||
- [ ] Ключ `deploy\backup\id_backup` создан, в git не попадает (шаг 2)
|
||
- [ ] На VPS пользователь `fsbackup`, `sftp` работает, shell закрыт (шаг 3)
|
||
- [ ] Образы опубликованы, `forbidden-stars-backup` есть под arm64 (шаг 4)
|
||
- [ ] На Pi первый бэкап прошёл в `local` и `vps`, `status` без ошибок (шаг 5)
|
||
- [ ] `docker compose ps` показывает `backup` `(healthy)` (шаг 5)
|
||
- [ ] С ПК `status`, `list`, `pull` работают без пароля (шаг 6)
|
||
- [ ] Учебное восстановление на тест-клоне прошло, данные на месте (шаг 7)
|
||
|
||
**Через сутки**
|
||
- [ ] В `list` появился снимок с меткой `scheduled` в 04:00
|
||
- [ ] В `list vps` — такой же
|
||
|
||
**Через неделю**
|
||
- [ ] В `status` есть строка `Последняя проверка данных: … данные целы`
|