удаление test-контура (дополнение)
This commit is contained in:
+9
-17
@@ -1,6 +1,6 @@
|
||||
# Публикация: домены, VPS, туннели
|
||||
|
||||
Приложение крутится дома (Pi — прод) и на твоём ПК (dev/test). Дома белого IP нет
|
||||
Приложение крутится дома (Pi — прод) и на твоём ПК (dev). Дома белого IP нет
|
||||
(CGNAT), поэтому наружу выставляем через **VPS-привратник**: на нём Caddy терминирует
|
||||
HTTPS твоими сертификатами и проксирует трафик в SSH reverse-туннели, которые
|
||||
приложение само открывает к VPS.
|
||||
@@ -11,10 +11,8 @@ HTTPS твоими сертификатами и проксирует трафи
|
||||
│ ▲ туннель-КОНТЕЙНЕР │
|
||||
│ └── Pi : app:8000 PROD │
|
||||
forbidden-stars.ru ──►│ :443 (cert твой) → 127.0.0.1:9001 │
|
||||
│ ▲ контейнер (test) ИЛИ │
|
||||
│ ▲ ssh с ПК (dev) │
|
||||
│ ├── ПК test : app:8000 │
|
||||
│ └── ПК dev : vite:5173 │
|
||||
│ ▲ ssh с ПК (по требованию) │
|
||||
│ └── ПК dev : vite:5173 DEV │
|
||||
└───────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -23,32 +21,26 @@ HTTPS твоими сертификатами и проксирует трафи
|
||||
хост нет** — наружу его выставляет только туннель-контейнер (`ssh -R 9000:app:8000` к VPS).
|
||||
Работает постоянно: при обрыве `ssh` завершается, и Docker перезапускает контейнер
|
||||
(`restart: unless-stopped`). См. [`pi/`](pi/README.md).
|
||||
- **TEST** — ПК. То же самое: `docker compose -f docker-compose.test.yml up` собирает образы
|
||||
локально и поднимает `app` + `tunnel` (`ssh -R 9001:app:8000`) + `backup` (без расписания и
|
||||
без VPS). Портов на хост нет — тест виден только на `forbidden-stars.ru`. Обычно
|
||||
запускается лаунчером при `APP_ENV=test`.
|
||||
- **DEV** — ПК, нативно (`uvicorn`+`vite`). По умолчанию только на localhost; при
|
||||
`LOCAL_PUBLIC=vps` лаунчер (`run.ps1` / `run.sh`) дополнительно поднимает SSH-туннель с ПК
|
||||
(`ssh -R 9001:localhost:5173`) → дев виден на `forbidden-stars.ru`.
|
||||
- DEV и TEST делят слот **9001** (`forbidden-stars.ru`) → поднимай что-то **одно за раз**.
|
||||
PROD на отдельном слоте **9000** (`forbiddenstars.ru`) — работает независимо.
|
||||
(`ssh -R 9001:localhost:5173`) → дев виден на `forbidden-stars.ru` (слот **9001**).
|
||||
- PROD на отдельном слоте **9000** (`forbiddenstars.ru`) — работает независимо от dev.
|
||||
Временный прод на ПК (`docker-compose.temp.yml`) тоже занимает **9000** — одновременно с Pi нельзя.
|
||||
|
||||
Ключи туннеля (приватные, в git не идут; публичные части — в `authorized_keys` пользователя
|
||||
`tunnel` на VPS):
|
||||
- **Pi** — `TUNNEL_KEY_B64` (base64 приватного ключа) в `.env`; файла ключа на Pi нет.
|
||||
- **ПК, test и временный прод** — файл `deploy/tunnel/id_tunnel`, монтируется в туннель-контейнер.
|
||||
- **ПК, временный прод** — файл `deploy/tunnel/id_tunnel`, монтируется в туннель-контейнер.
|
||||
- **ПК, dev** — `run.ps1`/`run.sh` зовут системный `ssh` без `-i`, то есть с ключом по
|
||||
умолчанию из `~/.ssh`. Он должен быть в `authorized_keys` (можно тем же, что `id_tunnel`).
|
||||
|
||||
Настройка по шагам:
|
||||
1. **VPS** — [`vps/README.md`](vps/README.md): Caddy, файрвол, пользователь `tunnel`, сертификаты, `Caddyfile`.
|
||||
2. **Pi (прод)** — [`pi/README.md`](pi/README.md): ключ туннеля в `TUNNEL_KEY_B64`, `.env`, `docker compose up -d`.
|
||||
3. **ПК (dev/test)** — ключ в `deploy/tunnel/id_tunnel` (для test-контейнера) и/или ключ по
|
||||
умолчанию в `~/.ssh` (для dev-туннеля); pubkey — в `authorized_keys` у `tunnel@VPS`.
|
||||
3. **ПК (dev)** — ключ по умолчанию в `~/.ssh` (для dev-туннеля) и, если нужен временный прод,
|
||||
файл `deploy/tunnel/id_tunnel`; pubkey — в `authorized_keys` у `tunnel@VPS`.
|
||||
4. **Бэкапы** — [`backup/README.md`](backup/README.md): контейнер `backup` (restic) делает
|
||||
снимки на Pi и на VPS (`fsbackup@VPS`, только SFTP), скрипты ПК скачивают их и проверяют
|
||||
восстановление на тест-клоне.
|
||||
снимки на Pi и на VPS (`fsbackup@VPS`, только SFTP), скрипты ПК скачивают их на ПК.
|
||||
|
||||
Секреты не в git: сертификаты/ключи (`*.pem`, `*.key`, `id_tunnel*`, `id_backup*`) живут на
|
||||
VPS/Pi/ПК, в репозитории только `Caddyfile`, страница-заглушка, образы `deploy/tunnel/` и
|
||||
|
||||
+31
-33
@@ -16,7 +16,7 @@
|
||||
4. [Сборка и публикация образов](#шаг-4-сборка-и-публикация-образов) — ПК
|
||||
5. [Pi: включить бэкапы](#шаг-5-pi-включить-бэкапы) — Pi
|
||||
6. [ПК: доступ к Pi и выгрузка бэкапов](#шаг-6-пк-доступ-к-pi-и-выгрузка-бэкапов) — ПК
|
||||
7. [Учебное восстановление на тест-клоне](#шаг-7-учебное-восстановление-на-тест-клоне) — ПК
|
||||
7. [Проверка скачанного архива](#шаг-7-проверка-скачанного-архива) — ПК
|
||||
8. [Восстановление прода](#8-восстановление-прода) — Pi
|
||||
9. [Катастрофа: Pi умер](#9-катастрофа-pi-умер) — новый Pi
|
||||
10. [Повседневные действия](#10-повседневные-действия)
|
||||
@@ -562,43 +562,42 @@
|
||||
|
||||
---
|
||||
|
||||
## Шаг 7. Учебное восстановление на тест-клоне
|
||||
## Шаг 7. Проверка скачанного архива
|
||||
|
||||
**Где:** ПК с Docker Desktop. **Зачем:** убедиться, что бэкап действительно
|
||||
восстанавливается, **до** того как это понадобится по-настоящему. Прод не затрагивается.
|
||||
**Где:** ПК. **Зачем:** убедиться, что БД в скачанном снимке целая и в ней те данные, что
|
||||
ожидаются, **до** того как это понадобится по-настоящему. Прод не затрагивается.
|
||||
|
||||
> Данные тест-клона на ПК будут заменены данными из архива. Прежние данные тест-клона
|
||||
> сохраняются в его собственный снимок `pre-restore`.
|
||||
> Отдельного тестового контейнера для учебного восстановления больше нет. Сам механизм
|
||||
> `restore`/`import` отрабатывает только на Pi ([раздел 8](#8-восстановление-прода)); здесь
|
||||
> проверяется содержимое архива.
|
||||
|
||||
1. Восстановите скачанный архив в тест-клон (подставьте имя своего файла):
|
||||
1. Распакуйте последний скачанный архив во временную папку:
|
||||
|
||||
```powershell
|
||||
.\scripts\fs-backup.ps1 restore-test -File backups\fs_20260914_0400_3f2a9c1d.tar
|
||||
$f = (Get-ChildItem backups\fs_*.tar | Sort-Object LastWriteTime | Select-Object -Last 1).FullName
|
||||
$d = Join-Path $env:TEMP "fs-check"; New-Item -ItemType Directory -Force $d | Out-Null
|
||||
tar -xf $f -C $d
|
||||
```
|
||||
|
||||
В первый раз Docker соберёт образы тест-клона — это несколько минут.
|
||||
2. Откройте `%TEMP%\fs-check\forbidden_stars.db` в [DB Browser for SQLite](https://sqlitebrowser.org)
|
||||
(вкладка «Выполнить SQL») и выполните:
|
||||
|
||||
2. Проверьте, что приложение тест-клона поднялось:
|
||||
```sql
|
||||
PRAGMA integrity_check;
|
||||
SELECT (SELECT count(*) FROM users WHERE role = 'player') AS players,
|
||||
(SELECT count(*) FROM matches) AS matches;
|
||||
```
|
||||
|
||||
3. Закройте DB Browser и удалите временную папку — данные в ней не зашифрованы:
|
||||
|
||||
```powershell
|
||||
docker compose -f docker-compose.test.yml ps
|
||||
docker compose -f docker-compose.test.yml logs --tail 20 app
|
||||
Remove-Item -Recurse -Force (Join-Path $env:TEMP "fs-check")
|
||||
```
|
||||
|
||||
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`.
|
||||
- `PRAGMA integrity_check` → `ok`;
|
||||
- `players` и `matches` совпадают со столбцами `Игроков` / `Партий` этого снимка в `list` на Pi;
|
||||
- в папке рядом с БД есть `uploads\…` (фото партий) и, если заводились, `achievements\…`.
|
||||
|
||||
---
|
||||
|
||||
@@ -651,8 +650,9 @@
|
||||
Если локальный репозиторий повреждён или пуст, смотрите копию на VPS:
|
||||
`docker compose exec backup fs-backup list vps`.
|
||||
|
||||
2. **По желанию, но рекомендуется:** сначала отрепетируйте на ПК:
|
||||
`.\scripts\fs-backup.ps1 pull -Snapshot <ID>`, затем `restore-test` ([шаг 7](#шаг-7-учебное-восстановление-на-тест-клоне)).
|
||||
2. **По желанию, но рекомендуется:** перед восстановлением проверьте выбранный снимок на ПК:
|
||||
`.\scripts\fs-backup.ps1 pull -Snapshot <ID>`, затем [шаг 7](#шаг-7-проверка-скачанного-архива).
|
||||
Заодно у вас останется копия этого снимка вне Pi.
|
||||
|
||||
3. Остановите приложение. Сайт покажет страницу «Технические шоколадки»:
|
||||
|
||||
@@ -806,7 +806,7 @@
|
||||
- **Перед каждым обновлением прода** — `now -Tag before-update` (метка — латиница, цифры, `.`, `_`, `-`).
|
||||
- **Раз в месяц:**
|
||||
- скачать снимок на ПК (`pull`);
|
||||
- раз в пару месяцев сделать учебное восстановление (`restore-test`);
|
||||
- проверить скачанный архив ([шаг 7](#шаг-7-проверка-скачанного-архива));
|
||||
- удалить с ПК старые архивы — они не зашифрованы.
|
||||
- **Иногда:** посмотреть `docker compose ps`. Статус `unhealthy` у `backup` означает, что
|
||||
бэкапы перестали проходить (причину покажет `fs-backup status`).
|
||||
@@ -899,7 +899,7 @@ docker volume rm <имя тома>
|
||||
| `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`); текст ошибки выше в выводе |
|
||||
| шаг 7: `integrity_check` не `ok` или счётчики не совпадают с `list` | архив повреждён или скачан не тот снимок | удалить файл из `backups\` и скачать заново (`pull -Snapshot <ID>`); если повторяется — `fs-backup verify` на Pi |
|
||||
|
||||
---
|
||||
|
||||
@@ -935,11 +935,9 @@ docker volume rm <имя тома>
|
||||
|---|---|
|
||||
| `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` первым аргументом.
|
||||
`pull <ID> --repo vps`.
|
||||
|
||||
### Переменные `.env`
|
||||
|
||||
@@ -987,7 +985,7 @@ docker volume rm <имя тома>
|
||||
- [ ] На Pi первый бэкап прошёл в `local` и `vps`, `status` без ошибок (шаг 5)
|
||||
- [ ] `docker compose ps` показывает `backup` `(healthy)` (шаг 5)
|
||||
- [ ] С ПК `status`, `list`, `pull` работают без пароля (шаг 6)
|
||||
- [ ] Учебное восстановление на тест-клоне прошло, данные на месте (шаг 7)
|
||||
- [ ] Скачанный архив проверен: БД целая, числа совпадают с `list` (шаг 7)
|
||||
|
||||
**Через сутки**
|
||||
- [ ] В `list` появился снимок с меткой `scheduled` в 04:00
|
||||
|
||||
@@ -10,7 +10,7 @@ VPS_TUNNEL_USER="${VPS_TUNNEL_USER:-tunnel}"
|
||||
UPSTREAM="${UPSTREAM:-app:8000}"
|
||||
|
||||
# Источник приватного ключа: либо TUNNEL_KEY_B64 (base64 в .env — прод: только compose+env),
|
||||
# либо смонтированный файл /key/id_tunnel (dev/test, где репозиторий есть на хосте).
|
||||
# либо смонтированный файл /key/id_tunnel (временный прод на ПК, где репозиторий есть на хосте).
|
||||
mkdir -p /root/.ssh
|
||||
KEY=/root/.ssh/id_tunnel
|
||||
if [ -n "${TUNNEL_KEY_B64:-}" ]; then
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Два домена, ОБА с твоими сертификатами; проксируют в SSH-туннели:
|
||||
#
|
||||
# forbiddenstars.ru → 127.0.0.1:9000 ← Pi (autossh, постоянно) PROD
|
||||
# forbidden-stars.ru → 127.0.0.1:9001 ← ПК (по требованию) DEV/TEST
|
||||
# forbidden-stars.ru → 127.0.0.1:9001 ← ПК (по требованию) DEV
|
||||
#
|
||||
# Caddy сам терминирует TLS (он и есть edge: видит реального клиента), а вниз к
|
||||
# приложению передаёт X-Forwarded-Proto=https / X-Forwarded-For / Host —
|
||||
@@ -38,10 +38,10 @@
|
||||
-Server
|
||||
}
|
||||
|
||||
# Content-Security-Policy подготовлена, но ВЫКЛЮЧЕНА до проверки на test-клоне: строгая
|
||||
# политика легко ломает SPA (инлайновые стили Vite), Telegram-виджет входа (скрипт с
|
||||
# telegram.org + iframe oauth.telegram.org) и EventSource (/api/events). Раскомментировать
|
||||
# после проверки на forbidden-stars.ru, что вход и реал-тайм работают (#61).
|
||||
# Content-Security-Policy подготовлена, но ВЫКЛЮЧЕНА до проверки: строгая политика легко
|
||||
# ломает SPA (инлайновые стили Vite), Telegram-виджет входа (скрипт с telegram.org + iframe
|
||||
# oauth.telegram.org) и EventSource (/api/events). Раскомментировать после проверки,
|
||||
# что вход и реал-тайм работают (#61).
|
||||
# header Content-Security-Policy "default-src 'self'; script-src 'self' https://telegram.org https://oauth.telegram.org; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self'; frame-src https://oauth.telegram.org; font-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
|
||||
|
||||
# HTML-документ (навигации, Accept: text/html) НЕ кэшируем. Иначе браузер отдаёт старый
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# VPS (186.246.51.17) — реверс-прокси Caddy + точка входа SSH-туннелей
|
||||
|
||||
Единственная публичная точка. На VPS: Caddy терминирует HTTPS твоими сертификатами
|
||||
для двух доменов и проксирует трафик в SSH reverse-туннели от Pi (прод) и ПК (dev/test).
|
||||
для двух доменов и проксирует трафик в SSH reverse-туннели от Pi (прод) и ПК (dev).
|
||||
|
||||
```
|
||||
forbiddenstars.ru → 127.0.0.1:9000 ← Pi (туннель-контейнер, постоянно) PROD
|
||||
forbidden-stars.ru → 127.0.0.1:9001 ← ПК (контейнер test или ssh dev, по требованию) DEV/TEST
|
||||
forbiddenstars.ru → 127.0.0.1:9000 ← Pi (туннель-контейнер, постоянно) PROD
|
||||
forbidden-stars.ru → 127.0.0.1:9001 ← ПК (ssh из лаунчера, по требованию) DEV
|
||||
```
|
||||
|
||||
> Туннель `ssh -R` по умолчанию слушает на loopback VPS (127.0.0.1) — ровно туда смотрит
|
||||
@@ -106,8 +106,8 @@ systemctl reload caddy
|
||||
> файл читается на каждый запрос).
|
||||
|
||||
## 7. Проверка
|
||||
1. Подними туннель прода на Pi (см. `deploy/pi/README.md`) и/или dev/test на ПК
|
||||
(лаунчер `run.ps1`: dev — при `LOCAL_PUBLIC=vps`, test — при `APP_ENV=test`).
|
||||
1. Подними туннель прода на Pi (см. `deploy/pi/README.md`) и/или dev на ПК
|
||||
(лаунчер `run.ps1` при `LOCAL_PUBLIC=vps`).
|
||||
2. Открой `https://forbiddenstars.ru` и `https://forbidden-stars.ru`.
|
||||
3. Пока соответствующий туннель не поднят — Caddy отдаёт страницу-заглушку «Технические шоколадки»
|
||||
(HTTP 503), это ожидаемо.
|
||||
|
||||
Reference in New Issue
Block a user