Update backup and logging documentation for Proxmox and containers. Adjust retention policies for Yandex Object Storage and enhance log rotation settings across various containers. Include detailed instructions for VPS backup processes and configurations for AmneziaVPN.

This commit is contained in:
2026-02-26 20:14:30 +03:00
parent cf212cefc5
commit feaa31f702
36 changed files with 1272 additions and 41 deletions

231
docs/backup/backup-howto.md Normal file
View File

@@ -0,0 +1,231 @@
# Бэкапы: как устроены и как восстанавливать
Краткая справка: что бэкапится, куда, когда и как восстановить.
---
## Где хранятся бэкапы
Все локальные бэкапы лежат на отдельном диске хоста Proxmox: **/dev/sdb1**, смонтирован в **/mnt/backup**.
```
/mnt/backup/
├── proxmox/
│ ├── dump/ ← vzdump (LXC и VM целиком)
│ └── etc-pve/ ← конфиги хоста (etc/pve, interfaces, hosts, resolv.conf)
├── databases/ ← логические дампы БД (pg_dump)
│ ├── ct101-nextcloud/
│ ├── ct103-gitea/
│ ├── ct104-paperless/
│ └── vm200-immich/
├── photos/
│ └── library/ ← копия библиотеки Immich (оригиналы фото) с VM 200
├── other/
│ ├── vaultwarden/ ← архив данных Vaultwarden (пароли); для restic → Yandex
│ └── ct105-vectors/ ← векторы RAG (vectors.npz) из CT 105
├── restic/ ← (опционально)
└── vps/
└── miran/ ← VPS Миран: БД бота, voice_users, копия S3 (telegram-helper-bot)
```
---
## Что, откуда, куда, когда
| Что | Откуда | Куда (локально) | Когда | Хранение |
|-----|--------|------------------|------|----------|
| **LXC и VM** | Все выбранные контейнеры (100109) и VM 200 | `/mnt/backup/proxmox/dump/` | **02:00** ежедневно (задание в Proxmox UI) | По настройкам задания (например: 7 daily, 4 weekly, 6 monthly) |
| **Конфиги хоста** | `/etc/pve`, `/etc/network/interfaces`, `/etc/hosts`, `/etc/resolv.conf` | `/mnt/backup/proxmox/etc-pve/` | **02:15** ежедневно (cron: `backup-etc-pve.sh`) | 30 дней |
| **БД Nextcloud (PostgreSQL)** | CT 101, контейнер `nextcloud-db-1` в `/opt/nextcloud` | `/mnt/backup/databases/ct101-nextcloud/` | **01:15** ежедневно (cron: `backup-ct101-pgdump.sh`) | 14 дней |
| **БД Gitea (PostgreSQL)** | CT 103, контейнер `gitea-db-1` в `/opt/gitea` | `/mnt/backup/databases/ct103-gitea/` | **03:00** ежедневно (cron: `backup-ct103-gitea-pgdump.sh`) | 14 дней |
| **БД Paperless (PostgreSQL)** | CT 104, контейнер `paperless-db-1` в `/opt/paperless` | `/mnt/backup/databases/ct104-paperless/` | **02:30** ежедневно (cron: `backup-ct104-pgdump.sh`) | 14 дней |
| **Данные Vaultwarden (пароли)** | CT 103, `/opt/docker/vaultwarden/data` | `/mnt/backup/other/vaultwarden/` | **02:45** ежедневно (cron: `backup-vaultwarden-data.sh`); каталог в restic → Yandex | 14 дней |
| **БД Immich (PostgreSQL)** | VM 200, контейнер `database` в `/opt/immich` | `/mnt/backup/databases/vm200-immich/` | **03:15** ежедневно (cron: `backup-vm200-pgdump.sh`) | 14 дней |
| **Векторы RAG (CT 105)** | CT 105, `/home/rag-service/data/vectors/` (vectors.npz) | `/mnt/backup/other/ct105-vectors/` | **03:30** ежедневно (cron: `backup-ct105-vectors.sh`) | 14 дней |
| **Оригиналы фото Immich** | VM 200, `/mnt/data/library/` | `/mnt/backup/photos/library/` | **01:30** ежедневно (cron: `backup-immich-photos.sh`, rsync) | Все копии (без автоудаления) |
| **VPS Миран (telegram-helper-bot)** | VPS 185.147.80.190: БД `tg-bot-database.db`, каталог `voice_users`, бакет S3 (Miran) | `/mnt/backup/vps/miran/` (db/, voice_users/, s3/) | **01:00** ежедневно (cron: `backup-vps-miran.sh`) | БД: 14 дней; voice_users и S3 — перезапись |
| **Выгрузка в Yandex (restic)** | `/mnt/backup` целиком | Yandex Object Storage | **04:00** ежедневно (cron: `backup-restic-yandex.sh`) | 3 daily, 2 weekly, 2 monthly |
**Окно бэкапов:** внутренние копии (синк внутри сервера) — **01:0003:30**; выгрузка в облако — **04:00**. **05:00** зарезервировано под плановую перезагрузку сервера. Задание vzdump — из веб-интерфейса Proxmox (Центр обработки данных → Резервная копия).
---
## Восстановление
### 1. Восстановление контейнера (LXC) или виртуальной машины (VM) из vzdump
**Когда нужно:** потеря или поломка одной/нескольких гостевых систем.
1. В Proxmox: **Центр обработки данных → Резервная копия** (или узел → Резервная копия).
2. Выбрать хранилище **backup** (или то, куда пишет задание).
3. Найти нужный бэкап по VMID и дате.
4. **Восстановить** → указать новый VMID (если восстанавливаем как копию) или тот же (если заменяем сломанный), узел и storage для дисков.
5. Запустить ВМ/контейнер и проверить доступность.
**С CLI (на хосте):**
- LXC: `pct restore <vmid> /path/to/backup.vma.zst --storage local-lvm` (и т.п., см. `pct restore --help`).
- VM: `qm restore <vmid> /path/to/backup.vma.zst` (и т.п., см. `qm restore --help`).
Путь к файлу бэкапа на хосте: `/mnt/backup/proxmox/dump/` (имя файла вида `vzdump-lxc-100-...` или `vzdump-qemu-200-...`).
---
### 2. Восстановление конфигов хоста (/etc/pve и сеть)
**Когда нужно:** переустановка Proxmox или потеря конфигов узла (при этом диск с бэкапами доступен).
1. Скопировать нужный архив с хоста, например:
`etc-pve-YYYYMMDD-HHMM.tar.gz` и/или `etc-host-configs-YYYYMMDD-HHMM.tar.gz` из `/mnt/backup/proxmox/etc-pve/`.
2. **Восстановление /etc/pve** (на переустановленном хосте, от root):
```bash
tar -xzf etc-pve-YYYYMMDD-HHMM.tar.gz -C /
```
При одномузловой установке обычно достаточно распаковать в `/`. При кластере — аккуратно с нодами и storage.
3. **Восстановление конфигов сети/хоста** (interfaces, hosts, resolv.conf):
```bash
tar -xzf etc-host-configs-YYYYMMDD-HHMM.tar.gz -C /
```
При необходимости поправить под текущее железо (интерфейсы, IP) и перезапустить сеть.
После восстановления конфигов — заново добавить storage для бэкапов (если переустанавливали с нуля) и восстанавливать гостей из vzdump по шагу 1.
---
### 3. Восстановление БД Immich (PostgreSQL) на VM 200
**Когда нужно:** повреждение или потеря базы Immich при рабочей ВМ (образ VM можно не трогать, восстанавливаем только БД).
1. Скопировать нужный дамп на VM 200, например:
`immich-db-YYYYMMDD-HHMM.sql.gz` из `/mnt/backup/databases/vm200-immich/`.
2. На VM 200 (ssh admin@192.168.1.200):
```bash
cd /opt/immich
gunzip -c /path/to/immich-db-YYYYMMDD-HHMM.sql.gz | docker compose exec -T database psql -U <DB_USERNAME> -d <DB_DATABASE_NAME>
```
Или распаковать `.sql.gz`, затем:
```bash
docker compose exec -T database psql -U <DB_USERNAME> -d <DB_DATABASE_NAME> < backup.sql
```
`<DB_USERNAME>` и `<DB_DATABASE_NAME>` — из `/opt/immich/.env` (обычно `postgres` и `immich`).
Перед восстановлением лучше остановить приложение Immich (или как минимум не писать в БД). При полной пересоздании БД — очистить каталог данных PostgreSQL в контейнере и затем загрузить дамп.
---
### 4. Восстановление библиотеки фото Immich
**Когда нужно:** потеря данных на диске VM 200 (например `/mnt/data/library`).
На VM 200 (или с хоста через rsync в обратную сторону): скопировать содержимое `/mnt/backup/photos/library/` обратно в каталог библиотеки Immich на VM 200 (в .env указан `UPLOAD_LOCATION`, обычно `/mnt/data/library`). Пример с хоста Proxmox:
```bash
rsync -av /mnt/backup/photos/library/ admin@192.168.1.200:/mnt/data/library/
```
После копирования на VM 200 выставить владельца/права под контейнер Immich (если нужно) и перезапустить сервисы.
---
### 5. Восстановление БД Paperless (CT 104), Gitea (CT 103)
**Paperless:** дамп из `/mnt/backup/databases/ct104-paperless/paperless-db-*.sql.gz`. На CT 104: остановить приложение, `gunzip -c backup.sql.gz | docker exec -i paperless-db-1 psql -U paperless -d paperless`, запустить стек.
**Gitea:** дамп из `/mnt/backup/databases/ct103-gitea/gitea-db-*.sql.gz`. На CT 103: остановить Gitea, восстановить в контейнер `gitea-db-1` (psql -U gitea -d gitea), запустить стек.
---
### 6. Восстановление данных Vaultwarden (CT 103)
Архив из `/mnt/backup/other/vaultwarden/vaultwarden-data-*.tar.gz`. На CT 103: остановить Vaultwarden, распаковать в `/opt/docker/vaultwarden/` (получится каталог `data/`), выставить владельца/права под контейнер, запустить Vaultwarden.
---
### 7. Восстановление бэкапа VPS Миран (telegram-helper-bot)
**Когда нужно:** потеря данных на VPS или перенос бота на другой хост.
В бэкапе есть:
- **БД:** `/mnt/backup/vps/miran/db/tg-bot-database-YYYYMMDD.db` — копии SQLite.
- **Голосовые сообщения:** `/mnt/backup/vps/miran/voice_users/` — каталог .ogg.
- **S3 (контент бота):** `/mnt/backup/vps/miran/s3/` — полная копия бакета (photos, videos, voice и т.д.).
**Восстановление на VPS:**
1. Скопировать выбранный файл БД на VPS:
`scp -P 15722 /mnt/backup/vps/miran/db/tg-bot-database-YYYYMMDD.db deploy@185.147.80.190:/home/prod/bots/telegram-helper-bot/database/tg-bot-database.db`
2. Восстановить `voice_users`:
`rsync -avz -e "ssh -p 15722" /mnt/backup/vps/miran/voice_users/ deploy@185.147.80.190:/home/prod/bots/telegram-helper-bot/voice_users/`
3. При потере данных в S3 — загрузить из бэкапа в бакет Miran (через aws s3 sync или панель), используя endpoint `https://api.s3.miran.ru` и креды из [VPS Миран](vps-miran-bots.md).
**Требования для бэкапа:** на хосте Proxmox — SSH-ключ root → deploy@185.147.80.190 (порт 15722); для S3 — установленный `aws` cli и файл `/root/.vps-miran-s3.env` с переменными S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET_NAME (см. [VPS Миран](../vps/vps-miran-bots.md)).
---
### 8. Восстановление векторов RAG (CT 105)
Архив из `/mnt/backup/other/ct105-vectors/vectors-*.tar.gz`. Распаковать на хосте и скопировать в контейнер: `tar -xzf vectors-*.tar.gz` → затем `pct push 105 ./vectors /home/rag-service/data/` или распаковать внутри CT 105 в `/home/rag-service/data/` (получится каталог `vectors/` с `vectors.npz`).
---
### 9. Восстановление VM 200 (Immich) с нуля
VM 200 **не входит** в задание vzdump (образ ~380 ГБ, не помещается в политику 7 копий). В бэкапе есть: **конфиг ВМ** (в архивах `/etc/pve`), **БД** (pg_dump), **фото** (rsync в `photos/library`). Восстановление — создание новой ВМ с теми же параметрами и перенос данных.
**Что есть после восстановления хоста:**
- Из бэкапа `etc-pve`: файл `/etc/pve/qemu-server/200.conf` — полное описание ВМ (CPU, память, диски, **hostpci для GPU**, сеть). Его можно использовать как образец при создании новой ВМ.
- Дамп БД: `/mnt/backup/databases/vm200-immich/immich-db-*.sql.gz`.
- Фото: `/mnt/backup/photos/library/`.
**Ключевые параметры VM 200** (если восстанавливать вручную без конфига):
- **Ресурсы:** 3 ядра, 10 GB RAM.
- **GPU:** проброс видеокарты (hostpci) — в Proxmox: Hardware → Add → PCI Device → выбрать VGA/NVIDIA, поставить «All Functions» и «ROM-Bar» при необходимости. В конфиге это выглядит как `hostpci0: 0000:xx:00.0` и т.п.
- **Диски:** первый — системный (~35 GB), второй — данные (~350 GB) под `/mnt/data` (библиотека, PostgreSQL, Docker).
- **Сеть:** статический IP 192.168.1.200/24, шлюз 192.168.1.1.
- **ОС:** Debian 13 (trixie), пользователь **admin**, SSH.
**Порядок восстановления:**
1. **Создать ВМ 200** в Proxmox с теми же параметрами (скопировать из восстановленного `200.conf` или задать вручную: CPU, RAM, hostpci для GPU, два диска, сеть с IP 192.168.1.200).
2. **Установить ОС** (Debian 13), создать пользователя admin, настроить SSH.
3. **Разметить второй диск** и смонтировать в `/mnt/data` (как в [container-200](../containers/container-200.md)).
4. **Установить Docker**, склонировать/восстановить каталоги Immich: `/opt/immich/` (docker-compose.yml, .env — из своих заметок или копии; секреты из Vaultwarden).
5. **Создать каталоги** `/mnt/data/library`, `/mnt/data/postgres` (и др. по .env).
6. **Скопировать фото** с хоста бэкапов на ВМ:
`rsync -av /mnt/backup/photos/library/ admin@192.168.1.200:/mnt/data/library/`
7. **Запустить только контейнер БД** (database), восстановить дамп (см. раздел 3 выше), затем поднять весь стек Immich.
8. Проверить NPM (прокси на 192.168.1.200:2283), при необходимости заново включить ML и настройки в Immich.
Подробное описание сервисов, образов и портов — в [container-200](../containers/container-200.md).
---
## Restic и Yandex
Скрипт **`backup-restic-yandex.sh`** выгружает весь каталог `/mnt/backup` в Yandex Object Storage (S3). Retention: **3 daily, 2 weekly, 2 monthly** (`restic forget --keep-daily 3 --keep-weekly 2 --keep-monthly 2`). Пароли и дампы — чувствительные данные; не выкладывать в открытый доступ.
---
## Скрипты на хосте Proxmox
| Скрипт | Назначение | Cron |
|--------|------------|------|
| `/root/scripts/backup-vps-miran.sh` | Бэкап VPS Миран: БД бота, voice_users, S3 (Miran) | 0 1 * * * |
| `/root/scripts/backup-ct101-pgdump.sh` | Логический дамп БД Nextcloud из CT 101 | 15 1 * * * |
| `/root/scripts/backup-immich-photos.sh` | Копирование библиотеки фото Immich (rsync с VM 200) | 30 1 * * * |
| `/root/scripts/backup-etc-pve.sh` | Бэкап /etc/pve и конфигов хоста | 15 2 * * * |
| `/root/scripts/backup-ct104-pgdump.sh` | Логический дамп БД Paperless из CT 104 | 30 2 * * * |
| `/root/scripts/backup-vaultwarden-data.sh` | Копирование данных Vaultwarden (пароли) из CT 103 | 45 2 * * * |
| `/root/scripts/backup-ct103-gitea-pgdump.sh` | Логический дамп БД Gitea из CT 103 | 0 3 * * * |
| `/root/scripts/backup-vm200-pgdump.sh` | Логический дамп БД Immich с VM 200 | 15 3 * * * |
| `/root/scripts/backup-ct105-vectors.sh` | Копирование векторов RAG (vectors.npz) из CT 105 | 30 3 * * * |
| `/root/scripts/backup-restic-yandex.sh` | Выгрузка /mnt/backup в Yandex S3 (restic), retention 3/2/2 | 0 4 * * * |
Задание vzdump (LXC/VM) настраивается в Proxmox UI (расписание 02:00). **05:00** оставлено свободным для плановой перезагрузки сервера.
---
## Связанные документы
- [Стратегия бэкапов (фаза 1)](proxmox-phase1-backup.md) — общий план и принятые решения.
- [Архитектура](../architecture/architecture.md) — хост, IP, доступ.
- [VM 200 (Immich)](../containers/container-200.md) — сервисы, пути, .env.

View File

@@ -24,11 +24,9 @@
| Место | Описание | Что туда | | Место | Описание | Что туда |
|-------|----------|----------| |-------|----------|----------|
| **Локально: /dev/sdb1 (1 ТБ под бэкапы)** | Отдельный SSD 2 ТБ; под копии выделен 1 ТБ, смонтирован в `/mnt/backup`. Второй ТБ — в запас. | Proxmox vzdump (через UI), затем те же данные (dump, etc-pve, фотки, VPS) в Yandex через restic. Фотки: оригиналы + метаданные + БД Immich; остальное пересчитать можно. VPS: Amnezia — конфиг; Миран — БД бота + контент (контент можно в S3 Мирана; копию на sdb1 — опционально). Конфигурацию серверов не бэкапим — есть Ansible. | | **Локально: /dev/sdb1 (1 ТБ под бэкапы)** | Отдельный SSD 2 ТБ; под копии выделен 1 ТБ, смонтирован в `/mnt/backup`. Второй ТБ — в запас. | Proxmox vzdump (через UI), затем те же данные (dump, etc-pve, фотки, VPS) в Yandex через restic. Фотки: оригиналы + метаданные + БД Immich; остальное пересчитать можно. VPS: Amnezia — конфиг; Миран — БД бота + контент (контент можно в S3 Мирана; копию на sdb1 — опционально). Конфигурацию серверов не бэкапим — есть Ansible. |
| **Офсайт: Yandex Object Storage (S3)** | Арендованный бакет, S3-совместимый API. [Yandex Object Storage](https://yandex.cloud/ru/docs/storage/s3/). | **Restic** с хоста (cron на ноде Proxmox): выгрузка содержимого `/mnt/backup` (или выбранных каталогов). Retention: 7 daily, 4 weekly, 6 monthly. | | **Офсайт: Yandex Object Storage (S3)** | Арендованный бакет, S3-совместимый API. [Yandex Object Storage](https://yandex.cloud/ru/docs/storage/s3/). | **Restic** с хоста (cron на ноде Proxmox): выгрузка содержимого `/mnt/backup`. Retention: 3 daily, 2 weekly, 2 monthly. |
- **Вариант A (альтернатива):** Отдельный диск/раздел на том же хосте — у тебя это sdb1. **Принято:** Вариант A — отдельный диск/раздел на хосте (sdb1, 1 ТБ в `/mnt/backup`). Варианты B (NFS/SMB) и C (внешний USB) в текущей схеме не используются; USB опционально для параноидального 3-2-1 (см. выше).
- **Вариант B:** Сетевое хранилище (NFS/SMB) — не используется в текущей схеме.
- **Вариант C:** Внешний USB — опционально для 3-2-1 (см. ниже).
**3-2-1:** Три копии: (1) прод — система и данные на основном диске; (2) локальный бэкап — sdb1; (3) офсайт — Yandex. Два типа носителей: локальный SSD и облачное object storage. Один офсайт — да. **Стратегия удовлетворяет 3-2-1.** Опционально: четвёртая копия на внешнем USB/другом ПК для параноидального сценария (пожар/кража) — по желанию. **3-2-1:** Три копии: (1) прод — система и данные на основном диске; (2) локальный бэкап — sdb1; (3) офсайт — Yandex. Два типа носителей: локальный SSD и облачное object storage. Один офсайт — да. **Стратегия удовлетворяет 3-2-1.** Опционально: четвёртая копия на внешнем USB/другом ПК для параноидального сценария (пожар/кража) — по желанию.
@@ -64,7 +62,7 @@
/mnt/backup/ /mnt/backup/
├── proxmox/ ├── proxmox/
│ ├── dump/ ← Proxmox Backup Job (VZDump) — сюда добавляем Storage в PVE │ ├── dump/ ← Proxmox Backup Job (VZDump) — сюда добавляем Storage в PVE
│ └── etc-pve/ ← архивы tar.gz из cron (backup-etc-pve.sh) │ └── etc-pve/ ← архивы tar.gz из cron: etc-pve-*, etc-host-configs-* (backup-etc-pve.sh)
├── restic/ ├── restic/
│ ├── local/ ← репозитории restic для локальных снапшотов (опционально) │ ├── local/ ← репозитории restic для локальных снапшотов (опционально)
│ └── ... ← или restic только в Yandex, локально только сырые копии │ └── ... ← или restic только в Yandex, локально только сырые копии
@@ -88,7 +86,7 @@
### Restic и Yandex Object Storage ### Restic и Yandex Object Storage
- **Restic** поддерживает бэкенд **S3**. Yandex Object Storage совместим с S3 API — используешь endpoint бакета и ключи (Access Key / Secret). - **Restic** поддерживает бэкенд **S3**. Yandex Object Storage совместим с S3 API — используешь endpoint бакета и ключи (Access Key / Secret).
- **Retention в Yandex:** 7 daily, 4 weekly, 6 monthly — `restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6` и затем `prune`. - **Retention в Yandex:** 3 daily, 2 weekly, 2 monthly — `restic forget --keep-daily 3 --keep-weekly 2 --keep-monthly 2` и затем `prune`.
- **Что гнать в Yandex через restic:** Proxmox dump (каталог `proxmox/dump`), `/etc/pve` (архивы из `proxmox/etc-pve`), фотки (оригиналы + метаданные + БД Immich), бэкапы с VPS — см. «Принятые решения: что куда» ниже. - **Что гнать в Yandex через restic:** Proxmox dump (каталог `proxmox/dump`), `/etc/pve` (архивы из `proxmox/etc-pve`), фотки (оригиналы + метаданные + БД Immich), бэкапы с VPS — см. «Принятые решения: что куда» ниже.
**Локально отдельной политики retention для restic не нужно:** на sdb1 retention задаётся в самом Proxmox Backup Job (например «keep last 7») и в скрипте бэкапа `/etc/pve` (удалять архивы старше N дней). Restic используется только для отправки в Yandex с политикой 7/4/6. Локальных restic-репозиториев можно не заводить — только каталоги и выгрузка их содержимого в облако. **Локально отдельной политики retention для restic не нужно:** на sdb1 retention задаётся в самом Proxmox Backup Job (например «keep last 7») и в скрипте бэкапа `/etc/pve` (удалять архивы старше N дней). Restic используется только для отправки в Yandex с политикой 7/4/6. Локальных restic-репозиториев можно не заводить — только каталоги и выгрузка их содержимого в облако.
@@ -97,18 +95,11 @@
--- ---
### Хранилище паролей (варианты) ### Хранилище паролей
Чтобы не терять пароли при восстановлении и держать креды в одном месте: Чтобы не терять пароли при восстановлении и держать креды в одном месте. Рассматривались варианты: Vaultwarden (self-hosted), Bitwarden Cloud, KeePass/KeePassXC, 1Password и др.
| Вариант | Плюсы | Минусы | **Принято и сделано:** **Vaultwarden** развёрнут на **CT 103** LXC. Домен через NPM (HTTPS), клиенты Bitwarden на ПК/телефоне. Бэкап данных Vaultwarden включён в общий план (restic → Yandex).
|--------|--------|--------|
| **Vaultwarden** (self-hosted, Bitwarden-совместимый) | Один сервер в твоём контуре (например CT 100 или отдельный LXC), клиенты Bitwarden на ПК/телефоне, бесплатно | Нужен HTTPS (NPM уже есть), бэкап базы Vaultwarden — в общий план бэкапов |
| **Bitwarden Cloud** (официальный облачный) | Не нужно поднимать сервер, синхронизация везде | Платно (или ограничения бесплатного), данные у третьей стороны |
| **KeePass / KeePassXC** (файл .kdbx) | Локально, один зашифрованный файл; можно положить в бэкап (restic/local) и открывать с любого ПК | Нет удобной синхронизации на телефон из коробки (нужен общий файл через Nextcloud/флешку) |
| **1Password / др. облачные менеджеры** | Удобно, кросс-девайс | Платно, данные у провайдера |
**Принято:** Развернуть **Vaultwarden** на контейнере **107 или 103** (не на 100). Домен через NPM, бэкап данных Vaultwarden включить в то, что уходит в restic → Yandex.
--- ---
@@ -132,7 +123,7 @@
- **Mode:** - **Mode:**
- **Snapshot** — контейнер/ВМ не останавливается, создаётся снимок (рекомендуется для минимизации даунтайма). - **Snapshot** — контейнер/ВМ не останавливается, создаётся снимок (рекомендуется для минимизации даунтайма).
- **Suspend** — ВМ приостанавливается на время бэкапа (более консистентно для БД, но даунтайм). - **Suspend** — ВМ приостанавливается на время бэкапа (более консистентно для БД, но даунтайм).
Для LXC обычно достаточно **Snapshot**; для VM 200 (Immich с БД) можно оставить Snapshot, при необходимости позже добавить отдельный консистентный бэкап БД изнутри гостя. Для LXC обычно достаточно **Snapshot**. Для **VM 200** (PostgreSQL и др.): Snapshot **не гарантирует консистентность БД** — PostgreSQL может быть в середине транзакции. **Правильная стратегия:** внутри VM делать логический бэкап БД (`pg_dump`), а **vzdump snapshot** использовать для остального (ОС, конфиги, файлы). Итого: VM 200 — vzdump snapshot ок для образа; консистентность БД — отдельно через `pg_dump` внутри гостя.
- **Compression:** ZSTD (хороший компромисс скорость/размер). - **Compression:** ZSTD (хороший компромисс скорость/размер).
- **Retention:** например «Keep last 7» или «Keep last 4 weekly» — чтобы не забивать диск. - **Retention:** например «Keep last 7» или «Keep last 4 weekly» — чтобы не забивать диск.
@@ -142,11 +133,11 @@
--- ---
### Шаг 4. Бэкап /etc/pve ### Шаг 4. Бэкап /etc/pve и конфигов хоста
Конфиги кластера и виртуалок лежат в `/etc/pve`. Их нужно копировать регулярно и хранить в безопасном месте (желательно не только на том же диске, что и система). Конфиги кластера и виртуалок лежат в `/etc/pve`. Плюс для восстановления хоста полезны: `/etc/network/interfaces`, `/etc/hosts`, `/etc/resolv.conf`. Всё это нужно копировать регулярно и хранить в безопасном месте (желательно не только на том же диске, что и система).
**Вариант A: cron на хосте Proxmox** **Принято: вариант A cron на хосте Proxmox.**
1. Создать скрипт, например `/root/scripts/backup-etc-pve.sh`: 1. Создать скрипт, например `/root/scripts/backup-etc-pve.sh`:
@@ -156,17 +147,18 @@ BACKUP_ROOT="/mnt/backup/proxmox/etc-pve" # по структуре выше
DATE=$(date +%Y%m%d-%H%M) DATE=$(date +%Y%m%d-%H%M)
mkdir -p "$BACKUP_ROOT" mkdir -p "$BACKUP_ROOT"
tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
tar -czf "$BACKUP_ROOT/etc-host-configs-$DATE.tar.gz" -C / etc/network/interfaces etc/hosts etc/resolv.conf
# опционально: удалять бэкапы старше N дней # опционально: удалять бэкапы старше N дней
# find "$BACKUP_ROOT" -name 'etc-pve-*.tar.gz' -mtime +30 -delete # find "$BACKUP_ROOT" -name 'etc-pve-*.tar.gz' -mtime +30 -delete
# find "$BACKUP_ROOT" -name 'etc-host-configs-*.tar.gz' -mtime +30 -delete
``` ```
2. Сделать исполняемым: `chmod +x /root/scripts/backup-etc-pve.sh`. 2. Сделать исполняемым: `chmod +x /root/scripts/backup-etc-pve.sh`.
3. Добавить в cron: `crontab -e`, например раз в день после основного backup job: 3. Добавить в cron: `crontab -e`. Окно внутренних бэкапов 01:0003:30; пример для etc-pve: `15 2 * * * /root/scripts/backup-etc-pve.sh`
`0 3 * * * /root/scripts/backup-etc-pve.sh`
**Вариант B:** Тот же скрипт можно вызывать из задачи в Proxmox (Script/Command в задаче типа Hook script), но проще и надёжнее — отдельный cron на хосте. **Вариант B:** Тот же скрипт можно вызывать из задачи в Proxmox (Script/Command в задаче типа Hook script), но проще и надёжнее — отдельный cron на хосте.
Бэкапы `/etc/pve` хранить **локально** (`/mnt/backup/proxmox/etc-pve`) и **в Yandex** — включить этот каталог в источники restic (мало весит, критично при потере хоста). Файлы с ограниченными правами (chmod 600); `/etc/pve` содержит секреты — не выкладывать в открытый доступ. Бэкапы (`etc-pve-*.tar.gz`, `etc-host-configs-*.tar.gz`) хранить **локально** (`/mnt/backup/proxmox/etc-pve`) и **в Yandex** — включить этот каталог в источники restic (мало весит, критично при потере хоста). Файлы с ограниченными правами (chmod 600); `/etc/pve` содержит секреты — не выкладывать в открытый доступ.
--- ---
@@ -174,7 +166,7 @@ tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
Чтобы «не вспоминать пароли 3 часа» после восстановления: Чтобы «не вспоминать пароли 3 часа» после восстановления:
Завести **секретное хранилище** — Vaultwarden на CT 107 или 103 (см. выше). Туда: root Proxmox, пользователи PVE, пароли БД и сервисов (Nextcloud, Gitea, Paperless, Immich, NPM, Galene и т.д.), API-ключи (Beget, certbot, Wallos и др.). Полный список кредов по контейнерам — в статьях container-100 … container-200; свести в один список в Vaultwarden и обновлять при смене паролей. **Секретное хранилище** — Vaultwarden на **CT 103** (см. выше). Туда: root Proxmox, пользователи PVE, пароли БД и сервисов (Nextcloud, Gitea, Paperless, Immich, NPM, Galene и т.д.), API-ключи (Beget, certbot, Wallos и др.). Полный список кредов по контейнерам — в статьях container-100 … container-200; свести в один список в Vaultwarden и обновлять при смене паролей.
Это не «шаг бэкапа», но обязательная часть восстановления: без паролей восстановленные контейнеры не войдут в сервисы. Это не «шаг бэкапа», но обязательная часть восстановления: без паролей восстановленные контейнеры не войдут в сервисы.
@@ -218,18 +210,18 @@ tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
## Чек-лист фазы 1 ## Чек-лист фазы 1
- [ ] Разметка: 1 ТБ на sdb1, ФС, монтирование в `/mnt/backup` (без LUKS). - [x] Разметка: 1 ТБ на sdb1, ФС, монтирование в `/mnt/backup` (без LUKS). *(скрипт `scripts/backup-setup-sdb1-mount.sh`, каталоги созданы.)*
- [ ] В Proxmox добавлен Storage для VZDump → `/mnt/backup/proxmox/dump`. - [x] В Proxmox добавлен Storage для VZDump → `/mnt/backup/proxmox/dump`.
- [ ] Настроена регулярная задача Backup: все LXC (100108) и VM (200), расписание (например ночь), retention. - [x] Настроена регулярная задача Backup: LXC (100108), расписание ночь (02:00), retention задан. *VM 200 исключена из задания (образ ~380 ГБ); восстановление VM 200 — по инструкции «с нуля» в [backup-howto](backup-howto.md).*
- [ ] Проверен ручной запуск Backup now — файлы появляются в storage. - [ ] Проверен ручной запуск Backup now — файлы появляются в storage. *(рекомендуется проверить разово.)*
- [ ] Настроен бэкап `/etc/pve` (скрипт + cron) → `/mnt/backup/proxmox/etc-pve`. - [x] Настроен бэкап `/etc/pve` (скрипт + cron) → `/mnt/backup/proxmox/etc-pve`. *(backup-etc-pve.sh, 03:00, 30 дней.)*
- [ ] Restic: cron на хосте, выгрузка нужных каталогов из `/mnt/backup` в Yandex S3, retention 7/4/6. - [ ] Restic: cron на хосте, выгрузка нужных каталогов из `/mnt/backup` в Yandex S3, retention 7/4/6.
- [ ] Yandex: ключи и endpoint зафиксированы, restic успешно пишет в бакет. - [ ] Yandex: ключи и endpoint зафиксированы, restic успешно пишет в бакет.
- [x] Vaultwarden развёрнут (CT 103). - [x] Vaultwarden развёрнут (CT 103).
- [ ] Секреты перенесены в Vaultwarden. - [ ] Секреты перенесены в Vaultwarden. *(на усмотрение: root PVE, пароли БД, API и т.д.)*
- [ ] Бэкап данных Vaultwarden (`/opt/docker/vaultwarden/data`) включён в restic (Yandex S3). - [ ] Бэкап данных Vaultwarden включён в restic (Yandex S3). *Локально данные уже копируются в `/mnt/backup/other/vaultwarden/` (backup-vaultwarden-data.sh); при настройке restic — включить этот каталог в источники.*
- [ ] Выполнено тестовое восстановление одного контейнера (другой VMID), проверена работоспособность. - [ ] Выполнено тестовое восстановление одного контейнера (другой VMID), проверена работоспособность.
- [ ] В документации зафиксирована процедура полного восстановления Proxmox «с нуля». - [x] В документации зафиксирована процедура полного восстановления Proxmox «с нуля». *[backup-howto.md](backup-howto.md): восстановление из vzdump, конфигов, БД, VM 200 с нуля, Vaultwarden, VPS и др.*
--- ---
@@ -252,8 +244,9 @@ tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
| VPS | Amnezia — конфиг. Миран — БД бота + контент (контент можно в S3 Мирана; копия на sdb1 — по желанию). Конфиг серверов не бэкапим — Ansible. | | VPS | Amnezia — конфиг. Миран — БД бота + контент (контент можно в S3 Мирана; копия на sdb1 — по желанию). Конфиг серверов не бэкапим — Ansible. |
| Где запускать бэкапы | Cron на хосте Proxmox: Backup Job (vzdump) + restic в Yandex. | | Где запускать бэкапы | Cron на хосте Proxmox: Backup Job (vzdump) + restic в Yandex. |
| Retention локально | Только в Proxmox Job и в скрипте etc-pve; отдельного restic-репозитория локально не делаем. | | Retention локально | Только в Proxmox Job и в скрипте etc-pve; отдельного restic-репозитория локально не делаем. |
| /etc/pve | Локально и в Yandex (через restic). | | /etc/pve + конфиги хоста (interfaces, hosts, resolv.conf) | Вариант A: cron на хосте → `etc-pve` и `etc-host-configs` в `/mnt/backup/proxmox/etc-pve`; локально и в Yandex (restic). |
| Пароли | Vaultwarden на CT 103. | | Пароли | Vaultwarden на CT 103. |
| VM 200 (БД PostgreSQL) | vzdump snapshot — для образа ВМ; консистентность БД — отдельно: внутри VM логический бэкап (`pg_dump`). |
| Yandex | Бакет создан; ключи и endpoint зафиксировать при настройке restic. | | Yandex | Бакет создан; ключи и endpoint зафиксировать при настройке restic. |
| MinIO | Не используем; директории + restic (s3 для Yandex). | | MinIO | Не используем; директории + restic (s3 для Yandex). |
@@ -261,4 +254,7 @@ tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
## Осталось сделать ## Осталось сделать
- **Yandex:** подготовить Static Key (Access Key + Secret), записать endpoint и имя бакета — понадобятся для настройки restic. - **Yandex + Restic:** подготовить Static Key (Access Key + Secret), записать endpoint и имя бакета; настроить restic на хосте (cron): выгрузка `/mnt/backup` в Yandex S3, retention 3 daily / 2 weekly / 2 monthly. Скрипт: `scripts/backup-restic-yandex.sh`.
- **Проверка:** один раз запустить Backup now в Proxmox UI и убедиться, что файлы появляются в storage.
- **Тестовое восстановление:** восстановить один контейнер (например 105 или 107) под другим VMID, проверить доступ и сервисы, затем удалить тестовый.
- **Секреты:** при необходимости перенести пароли/ключи (root PVE, БД, API) в Vaultwarden и обновлять при смене.

View File

@@ -0,0 +1,12 @@
# Пример конфига для restic + Yandex Object Storage.
# Скопировать на хост Proxmox: cp restic-yandex-env.example /root/.restic-yandex.env
# Подставить свои Access Key ID и Secret Key, chmod 600 /root/.restic-yandex.env
# Не коммитить файл с реальными ключами.
# Сервисному аккаунту в Yandex нужна роль storage.editor (не только uploader) — restic требует List объектов в бакете.
RESTIC_REPOSITORY=s3:storage.yandexcloud.net/backup-local
AWS_ACCESS_KEY_ID=ваш_идентификатор_ключа
AWS_SECRET_ACCESS_KEY=ваш_секретный_ключ
RESTIC_PASSWORD_FILE=/root/.restic-password
# Регион Yandex (по умолчанию ru-central1)
AWS_DEFAULT_REGION=ru-central1

View File

@@ -210,10 +210,10 @@ docker logs vpn-route-check
## Логи и ротация ## Логи и ротация
- **NPM:** логи в `/opt/docker/nginx-proxy/data/logs/`. - **NPM:** логи в `/opt/docker/nginx-proxy/data/logs/`.
- Для файлов **proxy-host-*_access.log** настроен logrotate: `/etc/logrotate.d/npm-access` — ротация при 100 MB, 4 копии, copytruncate, compress. - Logrotate: `/etc/logrotate.d/npm-nginx.conf` — все `*.log` в каталоге; ротация **daily**, `size 100M`, `rotate 5`, `compress`, `copytruncate` (≈30 дней / ~512 MB). Старый конфиг `npm-access` переименован в `npm-access.disabled`.
- Файлы **fallback_http_access.log**, **fallback_http_error.log** и другие **fallback_*** в правиле не указаны** — ротация по размеру/времени для них не настроена, каталог уже ~67 MB. Риск разрастания (см. TODO). - **AdGuard:** данные и логи в `data/work/` и `data/conf/` (~85 MB). Хранение логов запросов и статистики ограничено **14 днями**: в `AdGuardHome.yaml` заданы `querylog.interval = 336h` и `statistics.interval = 336h`.
- **Базовая политика LXC:** системные логи контейнера ротируются по `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов. Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Certbot:** `/etc/logrotate.d/certbot` — ротация логов letsencrypt (weekly, 12 копий). - **Certbot:** `/etc/logrotate.d/certbot` — ротация логов letsencrypt (weekly, 12 копий).
- **AdGuard:** данные и логи в `data/work/` и `data/conf/` (~85 MB). Стоит проверить настройки хранения логов запросов в веб-интерфейсе AdGuard (ограничение по времени/размеру).
--- ---
@@ -241,8 +241,8 @@ docker logs vpn-route-check
## TODO по контейнеру 100 ## TODO по контейнеру 100
- [ ] **Логи NPM:** Добавить в logrotate ротацию для `fallback_http_access.log`, `fallback_http_error.log` (и при необходимости других fallback_*) по размеру или по дням, чтобы не забивать диск. - [x] **Логи NPM:** Добавить в logrotate ротацию для `fallback_http_access.log`, `fallback_http_error.log` (и при необходимости других fallback_*) по размеру или по дням — настроено в `npm-nginx.conf` (30 дней / ~512 MB).
- [ ] **Логи AdGuard:** Проверить в веб-интерфейсе AdGuard настройки хранения логов запросов (срок/размер) и при необходимости ограничить. - [x] **Логи AdGuard:** Ограничить хранение логов запросов/статистики — настроено в `AdGuardHome.yaml` (`querylog.interval = 336h`, `statistics.interval = 336h` ≈ 14 дней).
- [ ] **VPN Route Check:** Вынести `ROUTER_TELNET_*` в `.env`, подключать в compose через `env_file`, не коммитить .env в репозиторий. - [ ] **VPN Route Check:** Вынести `ROUTER_TELNET_*` в `.env`, подключать в compose через `env_file`, не коммитить .env в репозиторий.
- [ ] **Log-dashboard:** Зафиксировать способ запуска контейнера (отдельный compose или скрипт) и добавить его в документацию/автозапуск при перезагрузке CT. - [ ] **Log-dashboard:** Зафиксировать способ запуска контейнера (отдельный compose или скрипт) и добавить его в документацию/автозапуск при перезагрузке CT.
- [ ] **Мониторинг диска:** Настроить оповещение (например, из Prometheus/Alertmanager или скрипт по крону) при заполнении корня или `/opt/docker` выше порога (например 80%). - [ ] **Мониторинг диска:** Настроить оповещение (например, из Prometheus/Alertmanager или скрипт по крону) при заполнении корня или `/opt/docker` выше порога (например 80%).

View File

@@ -107,6 +107,7 @@ PostgreSQL и Redis не проброшены на хост — доступ т
## Логи и ротация ## Логи и ротация
- **Базовая политика LXC:** в контейнере настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов (системные логи в `/var/log`). Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Nextcloud:** основной лог приложения — `/var/www/html/data/nextcloud.log` (в контейнере; на хосте — `/mnt/nextcloud-data/html/data/nextcloud.log`). На момент проверки файл уже порядка **сотен MB**. Встроенной ротации по размеру/времени в Nextcloud нет; в контейнере и на хосте **logrotate для этого файла не настроен** — риск разрастания и заполнения раздела (см. TODO). - **Nextcloud:** основной лог приложения — `/var/www/html/data/nextcloud.log` (в контейнере; на хосте — `/mnt/nextcloud-data/html/data/nextcloud.log`). На момент проверки файл уже порядка **сотен MB**. Встроенной ротации по размеру/времени в Nextcloud нет; в контейнере и на хосте **logrotate для этого файла не настроен** — риск разрастания и заполнения раздела (см. TODO).
- **PostgreSQL:** логи по умолчанию в stdout (видны через `docker logs nextcloud-db-1`). Отдельного файлового лога и logrotate не проверялось. - **PostgreSQL:** логи по умолчанию в stdout (видны через `docker logs nextcloud-db-1`). Отдельного файлового лога и logrotate не проверялось.
- **Docker:** вывод контейнеров — `docker logs`. Ротация логов Docker (json-file) зависит от настроек демона; при необходимости задать max-size/max-file в `/etc/docker/daemon.json`. - **Docker:** вывод контейнеров — `docker logs`. Ротация логов Docker (json-file) зависит от настроек демона; при необходимости задать max-size/max-file в `/etc/docker/daemon.json`.
@@ -145,6 +146,7 @@ docker compose logs -f nextcloud
## TODO по контейнеру 101 ## TODO по контейнеру 101
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Ротация nextcloud.log:** Настроить logrotate для `/mnt/nextcloud-data/html/data/nextcloud.log` (или пути на хосте CT 101): ротация по размеру (например 100200 MB) или по дням, сжатие, ограничение числа копий. Либо включить в Nextcloud логирование в syslog и ротировать его. - [ ] **Ротация nextcloud.log:** Настроить logrotate для `/mnt/nextcloud-data/html/data/nextcloud.log` (или пути на хосте CT 101): ротация по размеру (например 100200 MB) или по дням, сжатие, ограничение числа копий. Либо включить в Nextcloud логирование в syslog и ротировать его.
- [ ] **Уровень логирования:** В Nextcloud при необходимости снизить уровень лога (loglevel) в config.php или через `occ config:system:set loglevel --value 1` (1 = только ошибки), чтобы уменьшить рост лога. - [ ] **Уровень логирования:** В Nextcloud при необходимости снизить уровень лога (loglevel) в config.php или через `occ config:system:set loglevel --value 1` (1 = только ошибки), чтобы уменьшить рост лога.
- [ ] **Резервное копирование:** Регулярный бэкап: - [ ] **Резервное копирование:** Регулярный бэкап:

View File

@@ -205,6 +205,7 @@ curl -s http://127.0.0.1:8280/ | head -c 200
## Логи и ротация ## Логи и ротация
- **Базовая политика LXC:** в контейнере настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов (системные логи в `/var/log`). Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Gitea:** в app.ini задано `MODE = console`, `ROOT_PATH = /data/gitea/log`. Логи идут в stdout и попадают в драйвер Docker **json-file** без ограничения размера и количества файлов — со временем каталог `/var/lib/docker/containers/<id>/*.log` может разрастаться (см. TODO). - **Gitea:** в app.ini задано `MODE = console`, `ROOT_PATH = /data/gitea/log`. Логи идут в stdout и попадают в драйвер Docker **json-file** без ограничения размера и количества файлов — со временем каталог `/var/lib/docker/containers/<id>/*.log` может разрастаться (см. TODO).
- **PostgreSQL:** логи в stdout контейнера, то же хранилище Docker. - **PostgreSQL:** логи в stdout контейнера, то же хранилище Docker.
- **act_runner:** логи в stdout. - **act_runner:** логи в stdout.
@@ -249,6 +250,7 @@ curl -s http://127.0.0.1:8280/ | head -c 200
## TODO по контейнеру 103 ## TODO по контейнеру 103
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Логи Docker:** Включить ограничение размера логов для контейнеров: в `docker-compose.yml` (Gitea) добавить для сервисов `logging: driver: json-file options: max-size: "50m" max-file: "3"` или задать default в `/etc/docker/daemon.json` и перезапустить Docker. После этого перезапустить контейнеры. - [ ] **Логи Docker:** Включить ограничение размера логов для контейнеров: в `docker-compose.yml` (Gitea) добавить для сервисов `logging: driver: json-file options: max-size: "50m" max-file: "3"` или задать default в `/etc/docker/daemon.json` и перезапустить Docker. После этого перезапустить контейнеры.
- [ ] **CouchDB compose:** Создать `/opt/docker/couchdb/docker-compose.yml` с текущими томами и портами, перейти на `docker compose up -d` вместо ручного `docker run`, зафиксировать в документации. - [ ] **CouchDB compose:** Создать `/opt/docker/couchdb/docker-compose.yml` с текущими томами и портами, перейти на `docker compose up -d` вместо ручного `docker run`, зафиксировать в документации.
- [ ] **Домены и NPM:** При необходимости настроить в NPM proxy для git.katykhin.ru → 192.168.1.103:3000 и obsidian.katykhin.ru → 192.168.1.103:5984, выпустить SSL (certbot + deploy в NPM). В Gitea при использовании домена обновить ROOT_URL и DOMAIN в compose/app.ini. - [ ] **Домены и NPM:** При необходимости настроить в NPM proxy для git.katykhin.ru → 192.168.1.103:3000 и obsidian.katykhin.ru → 192.168.1.103:5984, выпустить SSL (certbot + deploy в NPM). В Gitea при использовании домена обновить ROOT_URL и DOMAIN в compose/app.ini.

View File

@@ -103,6 +103,7 @@ docker logs paperless-broker-1
## Логи и ротация ## Логи и ротация
- **Базовая политика LXC:** в контейнере настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов (системные логи в `/var/log`). Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Paperless:** логи приложения в **/mnt/paperless-data/data/log/**: - **Paperless:** логи приложения в **/mnt/paperless-data/data/log/**:
- `celery.log` — фоновые задачи (OCR, индекс и т.д.); приложение может делать собственную ротацию (celery.log.1, .2, .3). На момент проверки каталог ~568 KB. - `celery.log` — фоновые задачи (OCR, индекс и т.д.); приложение может делать собственную ротацию (celery.log.1, .2, .3). На момент проверки каталог ~568 KB.
- `paperless.log` — основной лог приложения. - `paperless.log` — основной лог приложения.
@@ -136,6 +137,7 @@ docker logs paperless-broker-1
## TODO по контейнеру 104 ## TODO по контейнеру 104
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Логи Paperless:** Настроить logrotate для `/mnt/paperless-data/data/log/*.log`: например еженедельная ротация или по размеру (max 50100 MB), хранить 34 копии, сжатие. Создать `/etc/logrotate.d/paperless` и проверить работу. - [ ] **Логи Paperless:** Настроить logrotate для `/mnt/paperless-data/data/log/*.log`: например еженедельная ротация или по размеру (max 50100 MB), хранить 34 копии, сжатие. Создать `/etc/logrotate.d/paperless` и проверить работу.
- [ ] **Логи Docker:** Включить ограничение размера логов контейнеров: в `docker-compose.yml` добавить для сервисов `logging: driver: json-file options: max-size: "50m" max-file: "3"` или задать default в `/etc/docker/daemon.json` и перезапустить Docker, затем контейнеры. - [ ] **Логи Docker:** Включить ограничение размера логов контейнеров: в `docker-compose.yml` добавить для сервисов `logging: driver: json-file options: max-size: "50m" max-file: "3"` или задать default в `/etc/docker/daemon.json` и перезапустить Docker, затем контейнеры.
- [ ] **Домен и NPM:** При необходимости настроить в NPM proxy для docs.katykhin.ru → 192.168.1.104:8000, выпустить SSL. В Paperless в `docker-compose.env` задать PAPERLESS_URL=https://docs.katykhin.ru (если ещё не задан). - [ ] **Домен и NPM:** При необходимости настроить в NPM proxy для docs.katykhin.ru → 192.168.1.104:8000, выпустить SSL. В Paperless в `docker-compose.env` задать PAPERLESS_URL=https://docs.katykhin.ru (если ещё не задан).

View File

@@ -79,6 +79,7 @@ curl -s -H "X-API-Key: <key>" http://192.168.1.105:8000/api/v1/health
## Логи и ротация ## Логи и ротация
- **Базовая политика LXC:** в контейнере настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов (системные логи в `/var/log`). Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **RAG-service:** логи приложения идут в **stdout** контейнера (LOG_LEVEL из .env) и попадают в драйвер Docker **json-file** без ограничения размера и количества файлов. - **RAG-service:** логи приложения идут в **stdout** контейнера (LOG_LEVEL из .env) и попадают в драйвер Docker **json-file** без ограничения размера и количества файлов.
- **Системный logrotate** в CT — только стандартные правила (apt, dpkg, btmp, wtmp). Отдельных правил для RAG или Docker нет. - **Системный logrotate** в CT — только стандартные правила (apt, dpkg, btmp, wtmp). Отдельных правил для RAG или Docker нет.
@@ -108,6 +109,7 @@ curl -s -H "X-API-Key: <key>" http://192.168.1.105:8000/api/v1/health
## TODO по контейнеру 105 ## TODO по контейнеру 105
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Логи Docker:** Включить ограничение размера логов: в `docker-compose.yml` добавить для сервиса `logging: driver: json-file options: max-size: "50m" max-file: "3"` или задать default в `/etc/docker/daemon.json`, перезапустить Docker и контейнер. - [ ] **Логи Docker:** Включить ограничение размера логов: в `docker-compose.yml` добавить для сервиса `logging: driver: json-file options: max-size: "50m" max-file: "3"` или задать default в `/etc/docker/daemon.json`, перезапустить Docker и контейнер.
- [ ] **Мониторинг диска:** Следить за местом (df -h). Уже 74% — при достижении 85%+ выполнить `docker builder prune` и/или оценить увеличение диска. При желании — оповещение при заполнении выше порога. - [ ] **Мониторинг диска:** Следить за местом (df -h). Уже 74% — при достижении 85%+ выполнить `docker builder prune` и/или оценить увеличение диска. При желании — оповещение при заполнении выше порога.
- [ ] **Домен и NPM:** При необходимости настроить в NPM proxy для mini-lm.katykhin.ru → 192.168.1.105:8000, выпустить SSL. В клиентах использовать https и передавать X-API-Key. - [ ] **Домен и NPM:** При необходимости настроить в NPM proxy для mini-lm.katykhin.ru → 192.168.1.105:8000, выпустить SSL. В клиентах использовать https и передавать X-API-Key.

View File

@@ -111,6 +111,7 @@ Companion и PostgreSQL доступны только внутри docker-сет
## Логи и ротация ## Логи и ротация
- **Базовая политика LXC:** в контейнере настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов (системные логи в `/var/log`). Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Invidious и Companion:** логи идут в stdout контейнеров и сохраняются драйвером Docker **json-file**. В compose для `invidious` и `companion` заданы опции: - **Invidious и Companion:** логи идут в stdout контейнеров и сохраняются драйвером Docker **json-file**. В compose для `invidious` и `companion` заданы опции:
- `max-size: "1G"`, `max-file: "4"` — максимум ~4 GB логов на контейнер. На момент проверки общий размер `/var/lib/docker` ~774 MB, запас по логам большой. - `max-size: "1G"`, `max-file: "4"` — максимум ~4 GB логов на контейнер. На момент проверки общий размер `/var/lib/docker` ~774 MB, запас по логам большой.
- **PostgreSQL:** контейнер `invidious-db` использует json-file **без ограничений** (`Config:{}`) — логи БД могут разрастаться (см. TODO). - **PostgreSQL:** контейнер `invidious-db` использует json-file **без ограничений** (`Config:{}`) — логи БД могут разрастаться (см. TODO).
@@ -158,6 +159,7 @@ Companion и PostgreSQL доступны только внутри docker-сет
## TODO по контейнеру 107 ## TODO по контейнеру 107
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Логи PostgreSQL:** Добавить ограничение логов для контейнера `invidious-invidious-db-1`: - [ ] **Логи PostgreSQL:** Добавить ограничение логов для контейнера `invidious-invidious-db-1`:
либо через `logging` в `docker-compose.yml` (аналогично `invidious`/`companion`, но с меньшим лимитом, например `max-size: "200m"`, `max-file: "5"`), либо через глобальный `/etc/docker/daemon.json`. После изменения — перезапустить контейнер. либо через `logging` в `docker-compose.yml` (аналогично `invidious`/`companion`, но с меньшим лимитом, например `max-size: "200m"`, `max-file: "5"`), либо через глобальный `/etc/docker/daemon.json`. После изменения — перезапустить контейнер.
- [ ] **Права на конфиги:** Ограничить доступ к `docker-compose.yml` и, при необходимости, к другим файлам с секретами (chmod 600, владелец root), убедиться, что эти файлы не попадают в публичные репозитории. - [ ] **Права на конфиги:** Ограничить доступ к `docker-compose.yml` и, при необходимости, к другим файлам с секретами (chmod 600, владелец root), убедиться, что эти файлы не попадают в публичные репозитории.

View File

@@ -88,6 +88,7 @@ curl -s -k https://192.168.1.108:8443/ # или через NPM: https://call.ka
## Логи и ротация ## Логи и ротация
- **Базовая политика LXC:** в контейнере настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов (системные логи в `/var/log`). Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Galene:** логи пишутся в **stdout/stderr** и попадают в **systemd journal** (journalctl -u galene.service). Отдельных файловых логов в `/opt/galene-data/` нет. На момент проверки журнал занимает ~8 MB. - **Galene:** логи пишутся в **stdout/stderr** и попадают в **systemd journal** (journalctl -u galene.service). Отдельных файловых логов в `/opt/galene-data/` нет. На момент проверки журнал занимает ~8 MB.
- **Ротация journal:** управляется настройками journald (например `/etc/systemd/journald.conf`: SystemMaxUse, MaxFileSec). Отдельного правила logrotate для Galene нет — ротация только за счёт journald. При долгой работе и активном трафике журнал может расти (см. TODO). - **Ротация journal:** управляется настройками journald (например `/etc/systemd/journald.conf`: SystemMaxUse, MaxFileSec). Отдельного правила logrotate для Galene нет — ротация только за счёт journald. При долгой работе и активном трафике журнал может расти (см. TODO).
- **Системный logrotate** в CT — стандартные правила (apt, dpkg, btmp, wtmp). Для Galene отдельного файла логов нет. - **Системный logrotate** в CT — стандартные правила (apt, dpkg, btmp, wtmp). Для Galene отдельного файла логов нет.
@@ -117,6 +118,7 @@ curl -s -k https://192.168.1.108:8443/ # или через NPM: https://call.ka
## TODO по контейнеру 108 ## TODO по контейнеру 108
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Ротация journal:** Проверить/задать в `/etc/systemd/journald.conf` параметры `SystemMaxUse=`, `MaxFileSec=` (или аналог), чтобы журнал не разрастался бесконечно. После изменений — `systemctl restart systemd-journald` (учёт потери текущего лога при рестарте). - [ ] **Ротация journal:** Проверить/задать в `/etc/systemd/journald.conf` параметры `SystemMaxUse=`, `MaxFileSec=` (или аналог), чтобы журнал не разрастался бесконечно. После изменений — `systemctl restart systemd-journald` (учёт потери текущего лога при рестарте).
- [ ] **Права на конфиги:** Ограничить доступ к файлам с паролями и кредами: `chmod 600` на `ice-servers.json`, на файлы в `groups/`. Владелец root. - [ ] **Права на конфиги:** Ограничить доступ к файлам с паролями и кредами: `chmod 600` на `ice-servers.json`, на файлы в `groups/`. Владелец root.
- [ ] **Мониторинг диска:** Следить за местом (df -h) и размером журнала. При желании — оповещение при заполнении выше порога (например 80%). - [ ] **Мониторинг диска:** Следить за местом (df -h) и размером журнала. При желании — оповещение при заполнении выше порога (например 80%).

View File

@@ -105,6 +105,8 @@ ip a show wg0
Ожидаемый адрес: `10.10.99.1/24`. Ожидаемый адрес: `10.10.99.1/24`.
**Логи и ротация:** для auth/syslog настроен logrotate `/etc/logrotate.d/homelab-vpn-ssh.conf` — 90 дней, небольшой лимит размера (10 MB, 4 архива). Базовая политика LXC: `/etc/logrotate.d/homelab-lxc.conf`. См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
--- ---
## Конфиги клиентов (split-tunnel + DNS через AdGuard) ## Конфиги клиентов (split-tunnel + DNS через AdGuard)

View File

@@ -183,8 +183,9 @@ sudo resize2fs /dev/sdb1
## Логи и ротация ## Логи и ротация
- **Базовая политика (как в LXC):** на ВМ настроен logrotate `/etc/logrotate.d/homelab-lxc.conf` — 14 дней, 50 MB, 5 архивов, сжатие (системные логи в `/var/log`). На ВМ 200 пакет `logrotate` был установлен вручную (в образе по умолчанию не было); после установки активен таймер `logrotate.timer`. Подробнее: [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- **Docker:** данные Docker (образы, контейнеры, overlay) хранятся в **/mnt/data/docker** (Docker Root Dir). Логи контейнеров — драйвер **json-file** без ограничения размера и количества файлов (Config:{} у immich_server и immich_postgres). При активной работе логи могут разрастаться и занимать место на **корневом** разделе (если логи пишутся на корень) или в overlay на /mnt/data — уточнить расположение логов контейнеров (часто в /mnt/data/docker/containers). В любом случае ограничение логов не задано (см. TODO). - **Docker:** данные Docker (образы, контейнеры, overlay) хранятся в **/mnt/data/docker** (Docker Root Dir). Логи контейнеров — драйвер **json-file** без ограничения размера и количества файлов (Config:{} у immich_server и immich_postgres). При активной работе логи могут разрастаться и занимать место на **корневом** разделе (если логи пишутся на корень) или в overlay на /mnt/data — уточнить расположение логов контейнеров (часто в /mnt/data/docker/containers). В любом случае ограничение логов не задано (см. TODO).
- **Системный logrotate:** стандартные правила (apt, dpkg, cloud-init, unattended-upgrades, wtmp). Отдельных правил для Immich или Docker нет. - **Системный logrotate:** стандартные правила (apt, dpkg, cloud-init, unattended-upgrades, wtmp) плюс homelab-lxc.conf. Отдельных правил для Immich или Docker нет.
**Риск:** корневой диск заполнен на 87%. Рост логов, обновления и кэш могут привести к нехватке места. Необходимо ограничить логи Docker и следить за местом на корне (см. TODO). **Риск:** корневой диск заполнен на 87%. Рост логов, обновления и кэш могут привести к нехватке места. Необходимо ограничить логи Docker и следить за местом на корне (см. TODO).
@@ -216,6 +217,7 @@ sudo resize2fs /dev/sdb1
## TODO по ВМ 200 ## TODO по ВМ 200
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов, как в LXC). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
- [ ] **Корневой диск:** Снизить использование корня (87%). Варианты: перенести логи Docker на /mnt/data (если сейчас пишутся на корень), очистить старые образы/кэш (`docker system prune` с осторожностью), увеличить размер корневого диска ВМ в Proxmox. Настроить мониторинг и оповещение при заполнении >90%. - [ ] **Корневой диск:** Снизить использование корня (87%). Варианты: перенести логи Docker на /mnt/data (если сейчас пишутся на корень), очистить старые образы/кэш (`docker system prune` с осторожностью), увеличить размер корневого диска ВМ в Proxmox. Настроить мониторинг и оповещение при заполнении >90%.
- [ ] **Логи Docker:** Включить ограничение размера логов для всех контейнеров Immich и deduper: в `docker-compose.yml` добавить для каждого сервиса `logging: driver: json-file options: max-size: "100m" max-file: "3"` или задать default в `/etc/docker/daemon.json`. Убедиться, что Docker Root Dir остаётся на /mnt/data и логи не пишутся на корень. После изменений перезапустить контейнеры. - [ ] **Логи Docker:** Включить ограничение размера логов для всех контейнеров Immich и deduper: в `docker-compose.yml` добавить для каждого сервиса `logging: driver: json-file options: max-size: "100m" max-file: "3"` или задать default в `/etc/docker/daemon.json`. Убедиться, что Docker Root Dir остаётся на /mnt/data и логи не пишутся на корень. После изменений перезапустить контейнеры.
- [ ] **Права на конфиги:** Ограничить доступ к .env (chmod 600), не коммитить в публичные репозитории. - [ ] **Права на конфиги:** Ограничить доступ к .env (chmod 600), не коммитить в публичные репозитории.

View File

@@ -0,0 +1,23 @@
# Logrotate: базовая политика homelab
Конфигурации logrotate для Proxmox, LXC-контейнеров, Nginx Proxy Manager и VPN/SSH.
## Политики
| Цель | Срок | Размер/архивы | Сжатие |
|-----|------|----------------|--------|
| **Proxmox host** | 14 дней | max 100 MB на файл, 57 архивов | да |
| **LXC контейнеры** | 14 дней | 50 MB ротация, 5 архивов | да |
| **ВМ 200 (Immich)** | 14 дней | 50 MB ротация, 5 архивов (то же, что LXC) | да |
| **nginx reverse proxy (CT 100)** | 30 дней или 512 MB суммарно | rotate 5 × ~100 MB | да |
| **VPN / SSH (CT 109 и auth)** | 90 дней | небольшой лимит размера | да |
## Размещение на хостах
- **Proxmox:** правки в `/etc/logrotate.conf` (секция default) + при необходимости `/etc/logrotate.d/`.
- **LXC:** `/etc/logrotate.d/homelab-lxc.conf` (и при необходимости специфичные файлы).
- **ВМ 200:** `/etc/logrotate.d/homelab-lxc.conf` (тот же конфиг, что и в LXC). На ВМ 200 пакет `logrotate` при необходимости установить: `sudo apt-get install logrotate` (после установки активен `logrotate.timer`).
- **CT 100 (nginx):** `/etc/logrotate.d/npm-nginx.conf` (логи NPM в `/opt/docker/nginx-proxy/data/logs/`).
- **CT 109 (VPN) и везде auth/SSH:** `/etc/logrotate.d/homelab-vpn-ssh.conf`.
Конфиги из этой папки можно копировать на соответствующие хосты и проверять: `logrotate -d /path/to/config`.

View File

@@ -0,0 +1,22 @@
# LXC контейнеры: базовая политика
# 14 дней, 50 MB ротация, 5 архивов, сжатие
# Размещение: /etc/logrotate.d/homelab-lxc.conf
/var/log/*.log
/var/log/mail.log
/var/log/syslog
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
{
daily
maxage 14
size 50M
rotate 5
compress
delaycompress
missingok
notifempty
sharedscripts
}

View File

@@ -0,0 +1,17 @@
# Nginx Proxy Manager (контейнер 100): логи в /opt/docker/nginx-proxy/data/logs/
# 30 дней или ~512 MB суммарно (5 архивов × ~100 MB), сжатие
# Размещение на CT 100: /etc/logrotate.d/npm-nginx.conf
/opt/docker/nginx-proxy/data/logs/*.log
{
daily
maxage 30
size 100M
rotate 5
compress
delaycompress
missingok
notifempty
copytruncate
sharedscripts
}

View File

@@ -0,0 +1,35 @@
# Базовые настройки для Proxmox host (192.168.1.150)
# 14 дней, max 100 MB на файл, 57 архивов, сжатие
#
# Вариант 1: встроить в /etc/logrotate.conf в секцию default:
# daily
# maxage 14
# size 100M
# rotate 6
# compress
# delaycompress
# missingok
# notifempty
#
# Вариант 2: положить этот файл как /etc/logrotate.d/99-proxmox.conf
# (применяется к перечисленным путям; пакетные конфиги имеют приоритет по имени)
/var/log/syslog
/var/log/messages
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/pve/tasks/active
/var/log/pve/tasks/index
{
daily
maxage 14
size 100M
rotate 6
compress
delaycompress
missingok
notifempty
sharedscripts
}

View File

@@ -0,0 +1,19 @@
# VPN / SSH: длительное хранение (90 дней), небольшой лимит размера
# Контейнер 109 (WireGuard) и общие auth/SSH логи
# Размещение: CT 109 — /etc/logrotate.d/homelab-vpn-ssh.conf
# На других хостах (опционально) — то же для auth.log / secure
/var/log/auth.log
/var/log/secure
/var/log/syslog
{
daily
maxage 90
size 10M
rotate 4
compress
delaycompress
missingok
notifempty
sharedscripts
}

View File

@@ -0,0 +1,50 @@
# Образ для AmneziaWG на VPS. Используется на 185.103.253.99 (Germany) и 147.45.124.117 (USA).
# Сборка: docker build -t amnezia-awg .
# Конфиг WireGuard/обфускация задаётся через приложение AmneziaVPN при установке протокола на сервер.
FROM amneziavpn/amnezia-wg:latest
LABEL maintainer="AmneziaVPN"
#Install required packages
RUN apk add --no-cache bash curl dumb-init
RUN apk --update upgrade --no-cache
RUN mkdir -p /opt/amnezia
RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh
RUN chmod a+x /opt/amnezia/start.sh
# Tune network
RUN echo -e " \n\
fs.file-max = 51200 \n\
\n\
net.core.rmem_max = 67108864 \n\
net.core.wmem_max = 67108864 \n\
net.core.netdev_max_backlog = 250000 \n\
net.core.somaxconn = 4096 \n\
\n\
net.ipv4.tcp_syncookies = 1 \n\
net.ipv4.tcp_tw_reuse = 1 \n\
net.ipv4.tcp_tw_recycle = 0 \n\
net.ipv4.tcp_fin_timeout = 30 \n\
net.ipv4.tcp_keepalive_time = 1200 \n\
net.ipv4.ip_local_port_range = 10000 65000 \n\
net.ipv4.tcp_max_syn_backlog = 8192 \n\
net.ipv4.tcp_max_tw_buckets = 5000 \n\
net.ipv4.tcp_fastopen = 3 \n\
net.ipv4.tcp_mem = 25600 51200 102400 \n\
net.ipv4.tcp_rmem = 4096 87380 67108864 \n\
net.ipv4.tcp_wmem = 4096 65536 67108864 \n\
net.ipv4.tcp_mtu_probing = 1 \n\
net.ipv4.tcp_congestion_control = hybla \n\
# for low-latency network, use cubic instead \n\
# net.ipv4.tcp_congestion_control = cubic \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/sysctl.conf && \
mkdir -p /etc/security && \
echo -e " \n\
* soft nofile 51200 \n\
* hard nofile 51200 \n\
" | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf
ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ]
CMD [ "" ]

View File

@@ -58,3 +58,82 @@
- Переключение: выбираешь активное подключение — либо одно, либо другое (или отключено). Маршруты/политики при необходимости те же для обоих (например, весь трафик в туннель или только часть по правилам). - Переключение: выбираешь активное подключение — либо одно, либо другое (или отключено). Маршруты/политики при необходимости те же для обоих (например, весь трафик в туннель или только часть по правилам).
Так можно в любой момент перейти с USA на новый VPS: добавляешь новый профиль с конфигом нового сервера и переключаешься на него. Так можно в любой момент перейти с USA на новый VPS: добавляешь новый профиль с конфигом нового сервера и переключаешься на него.
---
## Конфиги для полного восстановления сервера
Все файлы, чтобы поднять новый VPS с нуля (тот же образ и параметры контейнера). Ключи и пиры WireGuard создаются приложением AmneziaVPN при установке протокола — их не копируем.
### Файлы в репозитории
| Назначение | Путь в репо |
|------------|-------------|
| Dockerfile AmneziaWG | `docs/vps/amnezia-awg/Dockerfile` |
| Метрики для Homepage | `scripts/vps-metrics-api.py` |
| Systemd-юнит метрик | `docs/vps/vps-metrics.service` |
### 1. Подготовка VPS
- ОС: Ubuntu (или [поддерживаемая Amnezia](https://docs.amnezia.org/ru/documentation/supported-linux-os-for-vps)).
- Установить Docker, настроить SSH-доступ с твоего ключа (для AmneziaVPN).
- На хосте: скопировать `docs/vps/amnezia-awg/Dockerfile` в каталог на VPS (например `/opt/amnezia/amnezia-awg/`), собрать образ:
```bash
cd /opt/amnezia/amnezia-awg
docker build -t amnezia-awg .
```
### 2. Запуск контейнера AmneziaWG
**Германия (или новый сервер с портом 33118):**
```bash
docker run -d --name amnezia-awg --restart=always \
-p 33118:33118/udp \
-v /lib/modules:/lib/modules \
--cap-add=NET_ADMIN --cap-add=SYS_MODULE \
--privileged \
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
--security-opt label=disable \
amnezia-awg
```
**США (или второй сервер с портом 37135):**
```bash
docker run -d --name amnezia-awg2 --restart=always \
-p 37135:37135/udp \
-v /lib/modules:/lib/modules \
--cap-add=NET_ADMIN --cap-add=SYS_MODULE \
--privileged \
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
--security-opt label=disable \
amnezia-awg
```
Важно: внутри контейнера AmneziaWG слушает тот порт, который задаётся при установке протокола через приложение (на Germany — 33118, на USA — 37135). Если поднимаешь новый сервер с другим портом, в команде меняй оба: `-p ПОРТ:ПОРТ/udp` и при установке AmneziaWG в приложении укажи тот же порт.
### 3. Установка протокола через AmneziaVPN
1. В приложении AmneziaVPN добавь сервер по IP нового VPS (SSH по ключу).
2. Установи протокол **AmneziaWG** на этот сервер (приложение подключится по SSH и настроит контейнер).
3. В настройках протокола на сервере задай параметры обфускации из таблицы выше: **jc, jmin, jmax, s1, s2, h1h4**.
4. Сгенерируй гостевые конфиги / конфиг с полным доступом — в них будет Endpoint = IP:порт нового VPS.
### 4. Метрики для Homepage (опционально)
Чтобы виджет метрик на Homepage показывал данные с этого VPS:
```bash
# Скопировать скрипт и юнит на VPS
cp scripts/vps-metrics-api.py /usr/local/bin/
chmod +x /usr/local/bin/vps-metrics-api.py
cp docs/vps/vps-metrics.service /etc/systemd/system/
systemctl daemon-reload && systemctl enable --now vps-metrics
```
Порт **3497/tcp** должен быть доступен с хоста, где крутится Homepage (или открыт в файрволе VPS).
### 5. Файрвол
На текущем сервере в Германии UFW не включён, политика iptables по умолчанию ACCEPT. Достаточно убедиться, что хостинг не режет входящий **UDP на порт AmneziaWG** (33118 или 37135) и при необходимости SSH (22) и 3497 для метрик.

View File

@@ -24,7 +24,7 @@
- Контейнер запущен с `--restart=always`, примонтирован `/lib/modules` (для ядерного модуля WireGuard). Сеть — bridge, проброс порта 33118/udp на хост. - Контейнер запущен с `--restart=always`, примонтирован `/lib/modules` (для ядерного модуля WireGuard). Сеть — bridge, проброс порта 33118/udp на хост.
- Дополнительно: **vps-metrics** (systemd-сервис) — API метрик для виджета Homepage. - Дополнительно: **vps-metrics** (systemd-сервис) — API метрик для виджета Homepage.
Каталог образа/скриптов на хосте: `/opt/amnezia/amnezia-awg/` (там лежит Dockerfile; сам сервер AmneziaWG настраивается через приложение AmneziaVPN при установке протокола на этот VPS). Каталог образа/скриптов на хосте: `/opt/amnezia/amnezia-awg/` (там лежит Dockerfile; сам сервер AmneziaWG настраивается через приложение AmneziaVPN при установке протокола на этот VPS). Копия Dockerfile и команды для полного развёртывания на новом VPS — в [перенос конфигурации](vpn-migrate-config.md#конфиги-для-полного-восстановления-сервера).
--- ---

View File

@@ -0,0 +1,16 @@
# Копировать на VPS: /etc/systemd/system/vps-metrics.service
# Затем: systemctl daemon-reload && systemctl enable --now vps-metrics
# Скрипт: scripts/vps-metrics-api.py → /usr/local/bin/vps-metrics-api.py
[Unit]
Description=VPS metrics API for Homepage
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/local/bin/vps-metrics-api.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

View File

@@ -55,6 +55,8 @@ VPS в ЦОД Миран (Санкт-Петербург). Развёрнуты
В ботаx (переменные окружения prod) заданы `S3_ENDPOINT_URL=https://api.s3.miran.ru`, регион и креды для загрузки/выдачи контента. Для локальной разработки или других клиентов использовать те же endpoint и ключи. В ботаx (переменные окружения prod) заданы `S3_ENDPOINT_URL=https://api.s3.miran.ru`, регион и креды для загрузки/выдачи контента. Для локальной разработки или других клиентов использовать те же endpoint и ключи.
**Бакет контента бота:** в S3 Miran используется бакет с именем `9829-telegram-helper-bot` (переменная `S3_BUCKET_NAME` в .env бота).
### 4. Остальное на хосте ### 4. Остальное на хосте
- **nginx** — порты 80 и 443; по умолчанию отдаёт статику из `/var/www/html`. - **nginx** — порты 80 и 443; по умолчанию отдаёт статику из `/var/www/html`.
@@ -103,3 +105,26 @@ docker compose logs -f telegram-bot
- **Homepage** (контейнер 100): виджет метрик может показывать данные с vps-metrics (185.147.80.190:3497). - **Homepage** (контейнер 100): виджет метрик может показывать данные с vps-metrics (185.147.80.190:3497).
Остальные узлы homelab (Proxmox, NPM и т.д.) описаны в [архитектуре](../architecture/architecture.md). Остальные узлы homelab (Proxmox, NPM и т.д.) описаны в [архитектуре](../architecture/architecture.md).
---
## Бэкап VPS (telegram-helper-bot)
Бэкап выполняется **с хоста Proxmox** скриптом `backup-vps-miran.sh` (cron 01:00). Копируются:
1. **БД:** `/home/prod/bots/telegram-helper-bot/database/tg-bot-database.db``/mnt/backup/vps/miran/db/` (с датой в имени, хранение 14 дней).
2. **Голосовые сообщения:** `/home/prod/bots/telegram-helper-bot/voice_users/``/mnt/backup/vps/miran/voice_users/` (rsync).
3. **S3 (Miran):** полная выгрузка бакета `9829-telegram-helper-bot``/mnt/backup/vps/miran/s3/` (через `aws s3 sync`).
**Что нужно на Proxmox:**
- **SSH:** с хоста (root) должен работать вход без пароля на `deploy@185.147.80.190 -p 15722` (добавить публичный ключ хоста в `~/.ssh/authorized_keys` пользователя deploy на VPS).
- **S3:** установить `awscli` (`apt install awscli`) и создать файл `/root/.vps-miran-s3.env` с содержимым (подставить свои креды):
```bash
S3_ACCESS_KEY=j3tears100@gmail.com
S3_SECRET_KEY=...
S3_BUCKET_NAME=9829-telegram-helper-bot
```
Файл читается только root; в репозиторий не коммитить.
Подробности и восстановление — в [Бэкапы: как устроены и как восстанавливать](../backup/backup-howto.md).

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# Логический бэкап PostgreSQL (Nextcloud) из контейнера 101.
# Запускать на хосте Proxmox под root. Использует pct exec (SSH не нужен).
# Результат: /mnt/backup/databases/ct101-nextcloud/nextcloud-db-YYYYMMDD-HHMM.sql.gz
set -e
CT_ID=101
BACKUP_DIR="/mnt/backup/databases/ct101-nextcloud"
RETENTION_DAYS=14
# Имя контейнера БД в compose-проекте nextcloud (при смене имени в compose — поправить)
PG_CONTAINER="nextcloud-db-1"
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M)
OUTPUT="$BACKUP_DIR/nextcloud-db-$DATE.sql.gz"
pct exec $CT_ID -- docker exec "$PG_CONTAINER" pg_dump -U nextcloud nextcloud 2>/dev/null | gzip > "$OUTPUT"
if [ -s "$OUTPUT" ]; then
echo "Создан: $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
else
echo "Ошибка: дамп пустой или контейнер недоступен."
rm -f "$OUTPUT"
exit 1
fi
find "$BACKUP_DIR" -name 'nextcloud-db-*.sql.gz' -mtime +$RETENTION_DAYS -delete

View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Логический бэкап PostgreSQL (Gitea) из контейнера 103.
# Запускать на хосте Proxmox под root. Использует pct exec.
# Результат: /mnt/backup/databases/ct103-gitea/gitea-db-YYYYMMDD-HHMM.sql.gz
set -e
CT_ID=103
BACKUP_DIR="/mnt/backup/databases/ct103-gitea"
RETENTION_DAYS=14
PG_CONTAINER="gitea-db-1"
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M)
OUTPUT="$BACKUP_DIR/gitea-db-$DATE.sql.gz"
pct exec $CT_ID -- docker exec "$PG_CONTAINER" pg_dump -U gitea gitea 2>/dev/null | gzip > "$OUTPUT"
if [ -s "$OUTPUT" ]; then
echo "Создан: $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
else
echo "Ошибка: дамп пустой или контейнер недоступен."
rm -f "$OUTPUT"
exit 1
fi
find "$BACKUP_DIR" -name 'gitea-db-*.sql.gz' -mtime +$RETENTION_DAYS -delete

View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Логический бэкап PostgreSQL (Paperless) из контейнера 104.
# Запускать на хосте Proxmox под root. Использует pct exec.
# Результат: /mnt/backup/databases/ct104-paperless/paperless-db-YYYYMMDD-HHMM.sql.gz
set -e
CT_ID=104
BACKUP_DIR="/mnt/backup/databases/ct104-paperless"
RETENTION_DAYS=14
PG_CONTAINER="paperless-db-1"
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M)
OUTPUT="$BACKUP_DIR/paperless-db-$DATE.sql.gz"
pct exec $CT_ID -- docker exec "$PG_CONTAINER" pg_dump -U paperless paperless 2>/dev/null | gzip > "$OUTPUT"
if [ -s "$OUTPUT" ]; then
echo "Создан: $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
else
echo "Ошибка: дамп пустой или контейнер недоступен."
rm -f "$OUTPUT"
exit 1
fi
find "$BACKUP_DIR" -name 'paperless-db-*.sql.gz' -mtime +$RETENTION_DAYS -delete

View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Копирование векторов RAG (vectors.npz и др.) из контейнера 105.
# Запускать на хосте Proxmox под root. Использует pct exec.
# Результат: /mnt/backup/other/ct105-vectors/vectors-YYYYMMDD-HHMM.tar.gz
set -e
CT_ID=105
REMOTE_PATH="/home/rag-service/data/vectors"
BACKUP_DIR="/mnt/backup/other/ct105-vectors"
RETENTION_DAYS=14
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M)
OUTPUT="$BACKUP_DIR/vectors-$DATE.tar.gz"
pct exec $CT_ID -- tar cf - -C /home/rag-service/data vectors 2>/dev/null | gzip > "$OUTPUT"
if [ -s "$OUTPUT" ]; then
echo "Создан: $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
else
echo "Ошибка: архив пустой или каталог недоступен."
rm -f "$OUTPUT"
exit 1
fi
find "$BACKUP_DIR" -name 'vectors-*.tar.gz' -mtime +$RETENTION_DAYS -delete

23
scripts/backup-etc-pve.sh Normal file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Бэкап /etc/pve и конфигов хоста (interfaces, hosts, resolv.conf).
# Запускать на хосте Proxmox под root. Cron: 0 3 * * * (после основного backup job в 02:00).
set -e
BACKUP_ROOT="/mnt/backup/proxmox/etc-pve"
RETENTION_DAYS=30
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_ROOT"
DATE=$(date +%Y%m%d-%H%M)
tar -czf "$BACKUP_ROOT/etc-pve-$DATE.tar.gz" -C / etc/pve
tar -czf "$BACKUP_ROOT/etc-host-configs-$DATE.tar.gz" -C / etc/network/interfaces etc/hosts etc/resolv.conf 2>/dev/null || true
chmod 600 "$BACKUP_ROOT"/etc-pve-*.tar.gz "$BACKUP_ROOT"/etc-host-configs-*.tar.gz 2>/dev/null || true
find "$BACKUP_ROOT" -name 'etc-pve-*.tar.gz' -mtime +$RETENTION_DAYS -delete
find "$BACKUP_ROOT" -name 'etc-host-configs-*.tar.gz' -mtime +$RETENTION_DAYS -delete

View File

@@ -0,0 +1,21 @@
#!/bin/bash
# Копирование библиотеки фото Immich (оригиналы) с VM 200 на диск бэкапов хоста.
# Запускать на хосте Proxmox под root. Требуется SSH без пароля: root → admin@192.168.1.200.
# Результат: /mnt/backup/photos/library/ (зеркало /mnt/data/library с VM 200).
# Без --delete: удалённые в Immich фото в бэкапе остаются (страховка).
set -e
VM_SSH="admin@192.168.1.200"
REMOTE_PATH="/mnt/data/library"
BACKUP_PATH="/mnt/backup/photos/library"
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_PATH"
rsync -az --timeout=3600 \
--exclude=".stfolder" \
"$VM_SSH:$REMOTE_PATH/" "$BACKUP_PATH/"

View File

@@ -0,0 +1,61 @@
#!/bin/bash
# Выгрузка /mnt/backup в Yandex Object Storage (S3) через restic.
# Запускать на хосте Proxmox под root.
# Перед первым запуском: установить restic, создать /root/.restic-yandex.env и /root/.restic-password, выполнить restic init.
# Cron: 0 4 * * * (04:00, после окна 01:0003:30; 05:00 зарезервировано под перезагрузку).
set -e
ENV_FILE="/root/.restic-yandex.env"
BACKUP_PATH="/mnt/backup"
# Исключаем служебные каталоги
EXCLUDE_OPTS=(--exclude="$BACKUP_PATH/lost+found")
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
if [ ! -f "$ENV_FILE" ]; then
echo "Нет файла $ENV_FILE. Скопируйте restic-yandex-env.example и задайте ключи."
exit 1
fi
# shellcheck source=/dev/null
source "$ENV_FILE"
for var in RESTIC_REPOSITORY AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY; do
if [ -z "${!var}" ]; then
echo "В $ENV_FILE не задано: $var"
exit 1
fi
done
if [ -z "$RESTIC_PASSWORD_FILE" ]; then
RESTIC_PASSWORD_FILE="/root/.restic-password"
fi
if [ ! -f "$RESTIC_PASSWORD_FILE" ]; then
echo "Файл с паролем репозитория не найден: $RESTIC_PASSWORD_FILE. Создайте его и выполните restic init."
exit 1
fi
export AWS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY
export RESTIC_PASSWORD_FILE
export RESTIC_REPOSITORY
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-ru-central1}"
if ! command -v restic >/dev/null 2>&1; then
echo "restic не установлен. Установите: apt install restic."
exit 1
fi
echo "Restic backup: $BACKUP_PATH -> $RESTIC_REPOSITORY"
restic backup "$BACKUP_PATH" "${EXCLUDE_OPTS[@]}" --quiet
echo "Restic forget (retention 3 daily, 2 weekly, 2 monthly)..."
restic forget --keep-daily 3 --keep-weekly 2 --keep-monthly 2 --prune --quiet
echo "Restic prune..."
restic prune --quiet
echo "Restic backup done."

View File

@@ -0,0 +1,130 @@
#!/bin/bash
# Шаг 1 бэкапов: разметка 1 ТБ на /dev/sdb1, ФС ext4, монтирование в /mnt/backup, структура каталогов.
# Запускать на хосте Proxmox под root.
#
# Использование:
# ./backup-setup-sdb1-mount.sh --yes # разметить (если нужно), смонтировать, создать каталоги
# ./backup-setup-sdb1-mount.sh --structure-only # только fstab, mount и каталоги (раздел уже есть)
# ./backup-setup-sdb1-mount.sh # вывод действий без выполнения (без --yes)
#
set -e
BACKUP_MOUNT="/mnt/backup"
DEV_DISK="/dev/sdb"
DEV_PART="/dev/sdb1"
# 1 ТБ на диске 2 ТБ — первая половина
PART_END="50%"
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
if [ "$(uname -s)" != "Linux" ]; then
echo "Скрипт предназначен для запуска на хосте Proxmox (Linux)."
exit 1
fi
DO_IT=false
STRUCTURE_ONLY=false
for arg in "$@"; do
case "$arg" in
--yes) DO_IT=true ;;
--structure-only) STRUCTURE_ONLY=true ;;
esac
done
echo "=== Шаг 1: хранилище бэкапов (sdb1 → ${BACKUP_MOUNT}) ==="
echo ""
# Проверка наличия диска
if [ ! -b "$DEV_DISK" ]; then
echo "Ошибка: диск $DEV_DISK не найден."
exit 1
fi
# Проверка: раздел уже есть и примонтирован?
if mountpoint -q "$BACKUP_MOUNT" 2>/dev/null; then
echo "$BACKUP_MOUNT уже примонтирован. Создаём только структуру каталогов (если нет)."
DO_PART=false
DO_MOUNT=false
elif [ -b "$DEV_PART" ]; then
echo "Раздел $DEV_PART уже существует."
if [ "$STRUCTURE_ONLY" = true ]; then
DO_PART=false
DO_MOUNT=true
else
echo "Пропускаем разметку. Добавим fstab и монтирование."
DO_PART=false
DO_MOUNT=true
fi
else
DO_PART=true
DO_MOUNT=true
fi
# --- Разметка (при необходимости) ---
if [ "$DO_PART" = true ]; then
echo ""
echo "Будет выполнено:"
echo " 1. Создание GPT на $DEV_DISK"
echo " 2. Создание раздела $DEV_PART до $PART_END (1 ТБ)"
echo " 3. Создание ФС ext4 на $DEV_PART"
if [ "$DO_IT" != true ]; then
echo ""
echo "Для выполнения добавьте флаг: $0 --yes"
exit 0
fi
echo ""
parted -s "$DEV_DISK" mklabel gpt
parted -s "$DEV_DISK" mkpart primary ext4 0% "$PART_END"
partprobe "$DEV_DISK" || true
sleep 1
mkfs.ext4 -L backup "$DEV_PART"
echo "Разметка и ФС созданы."
fi
# --- Монтирование ---
mkdir -p "$BACKUP_MOUNT"
if [ "$DO_MOUNT" = true ] && ! mountpoint -q "$BACKUP_MOUNT" 2>/dev/null; then
UUID=$(blkid -s UUID -o value "$DEV_PART")
if [ -z "$UUID" ]; then
echo "Ошибка: не удалось получить UUID для $DEV_PART"
exit 1
fi
if ! grep -q "UUID=$UUID" /etc/fstab 2>/dev/null; then
if [ "$DO_IT" != true ] && [ "$STRUCTURE_ONLY" != true ]; then
echo ""
echo "Добавьте в /etc/fstab:"
echo " UUID=$UUID $BACKUP_MOUNT ext4 defaults,nofail 0 2"
echo ""
echo "Затем: mount $BACKUP_MOUNT"
echo "Для автоматического выполнения: $0 --yes или $0 --structure-only"
exit 0
fi
echo "UUID=$UUID $BACKUP_MOUNT ext4 defaults,nofail 0 2" >> /etc/fstab
echo "Добавлена запись в /etc/fstab."
fi
mount "$BACKUP_MOUNT"
echo "Смонтировано: $BACKUP_MOUNT"
fi
# --- Структура каталогов (всегда, если примонтировано) ---
if ! mountpoint -q "$BACKUP_MOUNT" 2>/dev/null; then
echo "Ошибка: $BACKUP_MOUNT не примонтирован. Сначала смонтируйте раздел."
exit 1
fi
mkdir -p "$BACKUP_MOUNT/proxmox/dump"
mkdir -p "$BACKUP_MOUNT/proxmox/etc-pve"
mkdir -p "$BACKUP_MOUNT/restic/local"
mkdir -p "$BACKUP_MOUNT/photos"
mkdir -p "$BACKUP_MOUNT/vps"
mkdir -p "$BACKUP_MOUNT/other"
echo ""
echo "Структура каталогов создана:"
ls -la "$BACKUP_MOUNT"
echo ""
echo "Готово. Точка монтирования: $BACKUP_MOUNT"

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# Копирование данных Vaultwarden (пароли) из контейнера 103 на диск бэкапов.
# Для выгрузки в Yandex — включить каталог в источники restic.
# Запускать на хосте Proxmox под root. Использует pct exec.
# Результат: /mnt/backup/other/vaultwarden/vaultwarden-data-YYYYMMDD-HHMM.tar.gz
set -e
CT_ID=103
REMOTE_PATH="/opt/docker/vaultwarden/data"
BACKUP_DIR="/mnt/backup/other/vaultwarden"
RETENTION_DAYS=14
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M)
OUTPUT="$BACKUP_DIR/vaultwarden-data-$DATE.tar.gz"
pct exec $CT_ID -- tar cf - -C /opt/docker/vaultwarden data 2>/dev/null | gzip > "$OUTPUT"
if [ -s "$OUTPUT" ]; then
chmod 600 "$OUTPUT"
echo "Создан: $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
else
echo "Ошибка: архив пустой или каталог недоступен."
rm -f "$OUTPUT"
exit 1
fi
find "$BACKUP_DIR" -name 'vaultwarden-data-*.tar.gz' -mtime +$RETENTION_DAYS -delete

View File

@@ -0,0 +1,41 @@
#!/bin/bash
# Логический бэкап PostgreSQL (Immich) с VM 200.
# Запускать на хосте Proxmox под root. Требуется SSH без пароля: root@host → admin@192.168.1.200
# (ssh-copy-id или ключ в ~/.ssh).
#
# На VM 200 должен быть установлен скрипт /opt/immich/scripts/backup-db.sh (см. immich-pgdump-remote.sh).
# Результат: /mnt/backup/databases/vm200-immich/immich-db-YYYYMMDD-HHMM.sql.gz
#
set -e
VM_SSH="admin@192.168.1.200"
REMOTE_SCRIPT="/opt/immich/scripts/backup-db.sh"
BACKUP_DIR="/mnt/backup/databases/vm200-immich"
RETENTION_DAYS=14
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M)
OUTPUT="$BACKUP_DIR/immich-db-$DATE.sql.gz"
if ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "$VM_SSH" "test -x $REMOTE_SCRIPT"; then
ssh -o ConnectTimeout=30 "$VM_SSH" "sudo $REMOTE_SCRIPT" | gzip > "$OUTPUT"
else
echo "На VM 200 не найден скрипт $REMOTE_SCRIPT. Скопируйте туда scripts/immich-pgdump-remote.sh и сделайте исполняемым."
exit 1
fi
if [ -s "$OUTPUT" ]; then
echo "Создан: $OUTPUT ($(du -h "$OUTPUT" | cut -f1))"
else
echo "Ошибка: дамп пустой или не создан."
rm -f "$OUTPUT"
exit 1
fi
# Удалить дампы старше RETENTION_DAYS
find "$BACKUP_DIR" -name 'immich-db-*.sql.gz' -mtime +$RETENTION_DAYS -delete

View File

@@ -0,0 +1,73 @@
#!/bin/bash
# Бэкап VPS Миран (telegram-helper-bot): БД, voice_users и контент из S3 (Miran).
# Запускать на хосте Proxmox под root.
# Требуется: SSH без пароля с хоста к deploy@185.147.80.190 -p 15722 (ключ в ~/.ssh).
# Для S3: установить aws cli и задать креды в /root/.vps-miran-s3.env (см. ниже).
# Результат: /mnt/backup/vps/miran/{db,voice_users,s3}.
set -e
VPS_HOST="185.147.80.190"
VPS_USER="deploy"
VPS_PORT="15722"
VPS_SSH="${VPS_USER}@${VPS_HOST}"
SSH_OPTS=(-o ConnectTimeout=15 -o BatchMode=yes -p "$VPS_PORT")
RSYNC_SSH="ssh -p $VPS_PORT"
REMOTE_DB="/home/prod/bots/telegram-helper-bot/database/tg-bot-database.db"
REMOTE_VOICE="/home/prod/bots/telegram-helper-bot/voice_users"
BACKUP_ROOT="/mnt/backup/vps/miran"
RETENTION_DAYS=14
# S3 (опционально): задать в /root/.vps-miran-s3.env:
# S3_ACCESS_KEY=...
# S3_SECRET_KEY=...
# S3_BUCKET_NAME=telegram-helper-bot
S3_ENV="/root/.vps-miran-s3.env"
S3_ENDPOINT="https://api.s3.miran.ru"
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
mkdir -p "$BACKUP_ROOT/db" "$BACKUP_ROOT/voice_users" "$BACKUP_ROOT/s3"
# ---- 1) Копирование БД с VPS ----
DATE=$(date +%Y%m%d)
DB_DEST="$BACKUP_ROOT/db/tg-bot-database-$DATE.db"
if rsync -az -e "$RSYNC_SSH" --timeout=60 "${VPS_SSH}:${REMOTE_DB}" "$DB_DEST" 2>/dev/null; then
echo "БД скопирована: $DB_DEST ($(du -h "$DB_DEST" | cut -f1))"
else
echo "Предупреждение: не удалось скопировать БД с VPS (проверьте SSH и путь)."
fi
find "$BACKUP_ROOT/db" -name 'tg-bot-database-*.db' -mtime +$RETENTION_DAYS -delete
# ---- 2) Копирование voice_users с VPS ----
if rsync -az -e "$RSYNC_SSH" --timeout=600 "${VPS_SSH}:${REMOTE_VOICE}/" "$BACKUP_ROOT/voice_users/" 2>/dev/null; then
echo "voice_users обновлён: $BACKUP_ROOT/voice_users/"
else
echo "Предупреждение: не удалось скопировать voice_users с VPS."
fi
# ---- 3) Выгрузка S3 (Miran) в локальную копию ----
if [ -f "$S3_ENV" ]; then
# shellcheck source=/dev/null
source "$S3_ENV"
if [ -n "$S3_ACCESS_KEY" ] && [ -n "$S3_SECRET_KEY" ] && [ -n "$S3_BUCKET_NAME" ]; then
if command -v aws >/dev/null 2>&1; then
export AWS_ACCESS_KEY_ID="$S3_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$S3_SECRET_KEY"
if aws s3 sync "s3://${S3_BUCKET_NAME}" "$BACKUP_ROOT/s3/" --endpoint-url "$S3_ENDPOINT" --no-progress 2>/dev/null; then
echo "S3 синхронизирован: $BACKUP_ROOT/s3/"
else
echo "Предупреждение: aws s3 sync завершился с ошибкой."
fi
else
echo "Предупреждение: aws cli не установлен — S3 не бэкапится. Установите: apt install awscli."
fi
else
echo "Предупреждение: в $S3_ENV заданы не все переменные (S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET_NAME)."
fi
else
echo "Подсказка: для бэкапа S3 создайте $S3_ENV с S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET_NAME."
fi

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Логический дамп БД Immich (PostgreSQL). Запускать на VM 200 (admin или root).
# Установить на VM: /opt/immich/scripts/backup-db.sh
# Использует .env из /opt/immich (DB_USERNAME, DB_DATABASE_NAME).
set -e
cd /opt/immich
[ -f .env ] || { echo "No .env in /opt/immich" >&2; exit 1; }
set -a
source .env
set +a
# В compose сервис БД называется "database", не "immich_postgres"
docker compose exec -T database pg_dump -U "${DB_USERNAME}" "${DB_DATABASE_NAME}"

View File

@@ -0,0 +1,74 @@
#!/bin/bash
# Однократная настройка бэкапа VPS Миран на хосте Proxmox.
# Запускать на Proxmox под root. Перед запуском скопировать сюда backup-vps-miran.sh в /root/scripts/.
# Делает: SSH-ключ (если нет), awscli, /root/.vps-miran-s3.env, cron.
set -e
if [ "$(id -u)" -ne 0 ]; then
echo "Запускайте под root."
exit 1
fi
SCRIPT_DIR="/root/scripts"
BACKUP_SCRIPT="$SCRIPT_DIR/backup-vps-miran.sh"
S3_ENV="/root/.vps-miran-s3.env"
CRON_ENTRY="30 1 * * * $BACKUP_SCRIPT"
VPS_DEPLOY="deploy@185.147.80.190"
VPS_PORT="15722"
# ---- 1) SSH-ключ для доступа к VPS ----
if [ ! -f /root/.ssh/id_ed25519 ] && [ ! -f /root/.ssh/id_rsa ]; then
mkdir -p /root/.ssh
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "" -q
echo "Создан новый SSH-ключ. Добавьте публичный ключ на VPS (пользователь deploy):"
echo ""
cat /root/.ssh/id_ed25519.pub
echo ""
echo "На VPS выполните (или вставьте вывод выше в ~/.ssh/authorized_keys):"
echo " ssh -p $VPS_PORT $VPS_DEPLOY 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys' < /root/.ssh/id_ed25519.pub"
else
echo "SSH-ключ уже есть. Публичный ключ (должен быть в authorized_keys на VPS):"
[ -f /root/.ssh/id_ed25519.pub ] && cat /root/.ssh/id_ed25519.pub || cat /root/.ssh/id_rsa.pub
fi
# ---- 2) awscli для S3 ----
if ! command -v aws >/dev/null 2>&1; then
echo "Установка awscli..."
apt-get update -qq && apt-get install -y -qq awscli
echo "awscli установлен."
else
echo "awscli уже установлен."
fi
# ---- 3) Креды S3 (Miran) ----
if [ ! -f "$S3_ENV" ]; then
cat > "$S3_ENV" << 'ENVFILE'
S3_ACCESS_KEY=j3tears100@gmail.com
S3_SECRET_KEY=wQ1-6sZEPs92sbZTSf96
S3_BUCKET_NAME=9829-telegram-helper-bot
ENVFILE
chmod 600 "$S3_ENV"
echo "Создан $S3_ENV с кредами S3 Miran."
else
echo "Файл $S3_ENV уже существует, не перезаписываю."
fi
# ---- 4) Скрипт бэкапа и права ----
mkdir -p "$SCRIPT_DIR"
if [ -f "$BACKUP_SCRIPT" ]; then
chmod +x "$BACKUP_SCRIPT"
echo "Скрипт бэкапа: $BACKUP_SCRIPT (исполняемый)."
else
echo "Внимание: $BACKUP_SCRIPT не найден. Скопируйте backup-vps-miran.sh в $SCRIPT_DIR/ и снова запустите этот setup."
fi
# ---- 5) Cron ----
if crontab -l 2>/dev/null | grep -qF "backup-vps-miran.sh"; then
echo "Запись cron для backup-vps-miran.sh уже есть."
else
(crontab -l 2>/dev/null || true; echo "$CRON_ENTRY") | crontab -
echo "Добавлен cron: $CRON_ENTRY"
fi
echo ""
echo "Готово. Проверка SSH на VPS: ssh -p $VPS_PORT $VPS_DEPLOY 'echo ok'"

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""
Скрипт метрик для VPS: CPU, RAM, диск.
Запуск: python3 vps-metrics-api.py [--port 3497]
Установи на каждый VPS и открой порт в файрволе (3497/tcp).
Systemd (опционально):
sudo cp vps-metrics-api.py /usr/local/bin/
sudo chmod +x /usr/local/bin/vps-metrics-api.py
Создай юнит /etc/systemd/system/vps-metrics.service с ExecStart=/usr/bin/python3 /usr/local/bin/vps-metrics-api.py
sudo systemctl enable --now vps-metrics
"""
import json
import subprocess
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
PORT = 3497
def get_metrics():
"""Собирает метрики из /proc и df. Только stdlib."""
out = {}
try:
# Load average (1 min)
with open("/proc/loadavg") as f:
parts = f.read().strip().split()
out["load_1"] = round(float(parts[0]), 2)
# Memory: MemTotal, MemAvailable
mem = {}
with open("/proc/meminfo") as f:
for line in f:
if ":" in line:
k, v = line.strip().split(":", 1)
mem[k.strip()] = int(v.strip().split()[0]) # kB
total_kb = mem.get("MemTotal", 0)
avail_kb = mem.get("MemAvailable", mem.get("MemFree", 0))
used_kb = total_kb - avail_kb
out["memory_total_gb"] = round(total_kb / 1024 / 1024, 2)
out["memory_used_gb"] = round(used_kb / 1024 / 1024, 2)
out["memory_percent"] = round(100 * used_kb / total_kb, 1) if total_kb else 0
out["memory.status"] = f"{out['memory_used_gb']:.1f} / {out['memory_total_gb']:.1f} GB"
# Disk: root /
result = subprocess.run(
["df", "-k", "/"],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode == 0 and result.stdout:
lines = result.stdout.strip().split("\n")
if len(lines) >= 2:
parts = lines[1].split()
if len(parts) >= 4:
total_kb = int(parts[1])
used_kb = int(parts[2])
avail_kb = int(parts[3])
total_gb = total_kb / 1024 / 1024
used_gb = used_kb / 1024 / 1024
avail_gb = avail_kb / 1024 / 1024
out["disk_total_gb"] = round(total_gb, 1)
out["disk_used_gb"] = round(used_gb, 1)
out["disk_free_gb"] = round(avail_gb, 1)
out["disk_percent"] = round(100 * used_kb / total_kb, 1) if total_kb else 0
out["disk.status"] = f"{out['disk_free_gb']:.1f} / {out['disk_total_gb']:.1f} GB свободно"
# Uptime
with open("/proc/uptime") as f:
out["uptime_seconds"] = int(float(f.read().split()[0]))
except Exception as e:
out["error"] = str(e)
return out
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(get_metrics(), ensure_ascii=False).encode())
def log_message(self, *args):
pass
def main():
port = PORT
if len(sys.argv) > 1 and sys.argv[1] == "--port" and len(sys.argv) > 2:
port = int(sys.argv[2])
print(f"VPS metrics API: http://0.0.0.0:{port}", file=sys.stderr)
HTTPServer(("0.0.0.0", port), Handler).serve_forever()
if __name__ == "__main__":
main()