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:
231
docs/backup/backup-howto.md
Normal file
231
docs/backup/backup-howto.md
Normal 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** | Все выбранные контейнеры (100–109) и 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:00–03: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.
|
||||
@@ -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. |
|
||||
| **Офсайт: 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.
|
||||
- **Вариант B:** Сетевое хранилище (NFS/SMB) — не используется в текущей схеме.
|
||||
- **Вариант C:** Внешний USB — опционально для 3-2-1 (см. ниже).
|
||||
**Принято:** Вариант A — отдельный диск/раздел на хосте (sdb1, 1 ТБ в `/mnt/backup`). Варианты B (NFS/SMB) и C (внешний USB) в текущей схеме не используются; USB опционально для параноидального 3-2-1 (см. выше).
|
||||
|
||||
**3-2-1:** Три копии: (1) прод — система и данные на основном диске; (2) локальный бэкап — sdb1; (3) офсайт — Yandex. Два типа носителей: локальный SSD и облачное object storage. Один офсайт — да. **Стратегия удовлетворяет 3-2-1.** Опционально: четвёртая копия на внешнем USB/другом ПК для параноидального сценария (пожар/кража) — по желанию.
|
||||
|
||||
@@ -64,7 +62,7 @@
|
||||
/mnt/backup/
|
||||
├── proxmox/
|
||||
│ ├── 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/
|
||||
│ ├── local/ ← репозитории restic для локальных снапшотов (опционально)
|
||||
│ └── ... ← или restic только в Yandex, локально только сырые копии
|
||||
@@ -88,7 +86,7 @@
|
||||
### Restic и Yandex Object Storage
|
||||
|
||||
- **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 — см. «Принятые решения: что куда» ниже.
|
||||
|
||||
**Локально отдельной политики 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** (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.
|
||||
**Принято и сделано:** **Vaultwarden** развёрнут на **CT 103** LXC. Домен через NPM (HTTPS), клиенты Bitwarden на ПК/телефоне. Бэкап данных Vaultwarden включён в общий план (restic → Yandex).
|
||||
|
||||
---
|
||||
|
||||
@@ -132,7 +123,7 @@
|
||||
- **Mode:**
|
||||
- **Snapshot** — контейнер/ВМ не останавливается, создаётся снимок (рекомендуется для минимизации даунтайма).
|
||||
- **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 (хороший компромисс скорость/размер).
|
||||
- **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`:
|
||||
|
||||
@@ -156,17 +147,18 @@ BACKUP_ROOT="/mnt/backup/proxmox/etc-pve" # по структуре выше
|
||||
DATE=$(date +%Y%m%d-%H%M)
|
||||
mkdir -p "$BACKUP_ROOT"
|
||||
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 дней
|
||||
# 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`.
|
||||
3. Добавить в cron: `crontab -e`, например раз в день после основного backup job:
|
||||
`0 3 * * * /root/scripts/backup-etc-pve.sh`
|
||||
3. Добавить в cron: `crontab -e`. Окно внутренних бэкапов 01:00–03:30; пример для etc-pve: `15 2 * * * /root/scripts/backup-etc-pve.sh`
|
||||
|
||||
**Вариант 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 часа» после восстановления:
|
||||
|
||||
Завести **секретное хранилище** — 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 ТБ на sdb1, ФС, монтирование в `/mnt/backup` (без LUKS).
|
||||
- [ ] В Proxmox добавлен Storage для VZDump → `/mnt/backup/proxmox/dump`.
|
||||
- [ ] Настроена регулярная задача Backup: все LXC (100–108) и VM (200), расписание (например ночь), retention.
|
||||
- [ ] Проверен ручной запуск Backup now — файлы появляются в storage.
|
||||
- [ ] Настроен бэкап `/etc/pve` (скрипт + cron) → `/mnt/backup/proxmox/etc-pve`.
|
||||
- [x] Разметка: 1 ТБ на sdb1, ФС, монтирование в `/mnt/backup` (без LUKS). *(скрипт `scripts/backup-setup-sdb1-mount.sh`, каталоги созданы.)*
|
||||
- [x] В Proxmox добавлен Storage для VZDump → `/mnt/backup/proxmox/dump`.
|
||||
- [x] Настроена регулярная задача Backup: LXC (100–108), расписание ночь (02:00), retention задан. *VM 200 исключена из задания (образ ~380 ГБ); восстановление VM 200 — по инструкции «с нуля» в [backup-howto](backup-howto.md).*
|
||||
- [ ] Проверен ручной запуск Backup now — файлы появляются в storage. *(рекомендуется проверить разово.)*
|
||||
- [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.
|
||||
- [ ] Yandex: ключи и endpoint зафиксированы, restic успешно пишет в бакет.
|
||||
- [x] Vaultwarden развёрнут (CT 103).
|
||||
- [ ] Секреты перенесены в Vaultwarden.
|
||||
- [ ] Бэкап данных Vaultwarden (`/opt/docker/vaultwarden/data`) включён в restic (Yandex S3).
|
||||
- [ ] Секреты перенесены в Vaultwarden. *(на усмотрение: root PVE, пароли БД, API и т.д.)*
|
||||
- [ ] Бэкап данных Vaultwarden включён в restic (Yandex S3). *Локально данные уже копируются в `/mnt/backup/other/vaultwarden/` (backup-vaultwarden-data.sh); при настройке restic — включить этот каталог в источники.*
|
||||
- [ ] Выполнено тестовое восстановление одного контейнера (другой 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. |
|
||||
| Где запускать бэкапы | Cron на хосте Proxmox: Backup Job (vzdump) + restic в Yandex. |
|
||||
| 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. |
|
||||
| VM 200 (БД PostgreSQL) | vzdump snapshot — для образа ВМ; консистентность БД — отдельно: внутри VM логический бэкап (`pg_dump`). |
|
||||
| Yandex | Бакет создан; ключи и endpoint зафиксировать при настройке restic. |
|
||||
| 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 и обновлять при смене.
|
||||
|
||||
12
docs/backup/restic-yandex-env.example
Normal file
12
docs/backup/restic-yandex-env.example
Normal 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
|
||||
@@ -210,10 +210,10 @@ docker logs vpn-route-check
|
||||
## Логи и ротация
|
||||
|
||||
- **NPM:** логи в `/opt/docker/nginx-proxy/data/logs/`.
|
||||
- Для файлов **proxy-host-*_access.log** настроен logrotate: `/etc/logrotate.d/npm-access` — ротация при 100 MB, 4 копии, copytruncate, compress.
|
||||
- Файлы **fallback_http_access.log**, **fallback_http_error.log** и другие **fallback_*** в правиле не указаны** — ротация по размеру/времени для них не настроена, каталог уже ~67 MB. Риск разрастания (см. TODO).
|
||||
- Logrotate: `/etc/logrotate.d/npm-nginx.conf` — все `*.log` в каталоге; ротация **daily**, `size 100M`, `rotate 5`, `compress`, `copytruncate` (≈30 дней / ~512 MB). Старый конфиг `npm-access` переименован в `npm-access.disabled`.
|
||||
- **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 копий).
|
||||
- **AdGuard:** данные и логи в `data/work/` и `data/conf/` (~85 MB). Стоит проверить настройки хранения логов запросов в веб-интерфейсе AdGuard (ограничение по времени/размеру).
|
||||
|
||||
---
|
||||
|
||||
@@ -241,8 +241,8 @@ docker logs vpn-route-check
|
||||
|
||||
## TODO по контейнеру 100
|
||||
|
||||
- [ ] **Логи NPM:** Добавить в logrotate ротацию для `fallback_http_access.log`, `fallback_http_error.log` (и при необходимости других fallback_*) по размеру или по дням, чтобы не забивать диск.
|
||||
- [ ] **Логи AdGuard:** Проверить в веб-интерфейсе AdGuard настройки хранения логов запросов (срок/размер) и при необходимости ограничить.
|
||||
- [x] **Логи NPM:** Добавить в logrotate ротацию для `fallback_http_access.log`, `fallback_http_error.log` (и при необходимости других fallback_*) по размеру или по дням — настроено в `npm-nginx.conf` (30 дней / ~512 MB).
|
||||
- [x] **Логи AdGuard:** Ограничить хранение логов запросов/статистики — настроено в `AdGuardHome.yaml` (`querylog.interval = 336h`, `statistics.interval = 336h` ≈ 14 дней).
|
||||
- [ ] **VPN Route Check:** Вынести `ROUTER_TELNET_*` в `.env`, подключать в compose через `env_file`, не коммитить .env в репозиторий.
|
||||
- [ ] **Log-dashboard:** Зафиксировать способ запуска контейнера (отдельный compose или скрипт) и добавить его в документацию/автозапуск при перезагрузке CT.
|
||||
- [ ] **Мониторинг диска:** Настроить оповещение (например, из Prometheus/Alertmanager или скрипт по крону) при заполнении корня или `/opt/docker` выше порога (например 80%).
|
||||
|
||||
@@ -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).
|
||||
- **PostgreSQL:** логи по умолчанию в stdout (видны через `docker logs nextcloud-db-1`). Отдельного файлового лога и logrotate не проверялось.
|
||||
- **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
|
||||
|
||||
- [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): ротация по размеру (например 100–200 MB) или по дням, сжатие, ограничение числа копий. Либо включить в Nextcloud логирование в syslog и ротировать его.
|
||||
- [ ] **Уровень логирования:** В Nextcloud при необходимости снизить уровень лога (loglevel) в config.php или через `occ config:system:set loglevel --value 1` (1 = только ошибки), чтобы уменьшить рост лога.
|
||||
- [ ] **Резервное копирование:** Регулярный бэкап:
|
||||
|
||||
@@ -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).
|
||||
- **PostgreSQL:** логи в stdout контейнера, то же хранилище Docker.
|
||||
- **act_runner:** логи в stdout.
|
||||
@@ -249,6 +250,7 @@ curl -s http://127.0.0.1:8280/ | head -c 200
|
||||
|
||||
## 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. После этого перезапустить контейнеры.
|
||||
- [ ] **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.
|
||||
|
||||
@@ -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/**:
|
||||
- `celery.log` — фоновые задачи (OCR, индекс и т.д.); приложение может делать собственную ротацию (celery.log.1, .2, .3). На момент проверки каталог ~568 KB.
|
||||
- `paperless.log` — основной лог приложения.
|
||||
@@ -136,6 +137,7 @@ docker logs paperless-broker-1
|
||||
|
||||
## 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 50–100 MB), хранить 3–4 копии, сжатие. Создать `/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, затем контейнеры.
|
||||
- [ ] **Домен и NPM:** При необходимости настроить в NPM proxy для docs.katykhin.ru → 192.168.1.104:8000, выпустить SSL. В Paperless в `docker-compose.env` задать PAPERLESS_URL=https://docs.katykhin.ru (если ещё не задан).
|
||||
|
||||
@@ -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** без ограничения размера и количества файлов.
|
||||
- **Системный 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
|
||||
|
||||
- [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 и контейнер.
|
||||
- [ ] **Мониторинг диска:** Следить за местом (df -h). Уже 74% — при достижении 85%+ выполнить `docker builder prune` и/или оценить увеличение диска. При желании — оповещение при заполнении выше порога.
|
||||
- [ ] **Домен и NPM:** При необходимости настроить в NPM proxy для mini-lm.katykhin.ru → 192.168.1.105:8000, выпустить SSL. В клиентах использовать https и передавать X-API-Key.
|
||||
|
||||
@@ -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` заданы опции:
|
||||
- `max-size: "1G"`, `max-file: "4"` — максимум ~4 GB логов на контейнер. На момент проверки общий размер `/var/lib/docker` ~774 MB, запас по логам большой.
|
||||
- **PostgreSQL:** контейнер `invidious-db` использует json-file **без ограничений** (`Config:{}`) — логи БД могут разрастаться (см. TODO).
|
||||
@@ -158,6 +159,7 @@ Companion и PostgreSQL доступны только внутри docker-сет
|
||||
|
||||
## TODO по контейнеру 107
|
||||
|
||||
- [x] **Базовая политика logrotate:** для системных логов настроена (homelab-lxc.conf — 14 дней, 50 MB, 5 архивов). См. [Logrotate — базовая политика homelab](../maintenance/logrotate/README.md).
|
||||
- [ ] **Логи PostgreSQL:** Добавить ограничение логов для контейнера `invidious-invidious-db-1`:
|
||||
либо через `logging` в `docker-compose.yml` (аналогично `invidious`/`companion`, но с меньшим лимитом, например `max-size: "200m"`, `max-file: "5"`), либо через глобальный `/etc/docker/daemon.json`. После изменения — перезапустить контейнер.
|
||||
- [ ] **Права на конфиги:** Ограничить доступ к `docker-compose.yml` и, при необходимости, к другим файлам с секретами (chmod 600, владелец root), убедиться, что эти файлы не попадают в публичные репозитории.
|
||||
|
||||
@@ -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.
|
||||
- **Ротация journal:** управляется настройками journald (например `/etc/systemd/journald.conf`: SystemMaxUse, MaxFileSec). Отдельного правила logrotate для Galene нет — ротация только за счёт journald. При долгой работе и активном трафике журнал может расти (см. TODO).
|
||||
- **Системный 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
|
||||
|
||||
- [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` (учёт потери текущего лога при рестарте).
|
||||
- [ ] **Права на конфиги:** Ограничить доступ к файлам с паролями и кредами: `chmod 600` на `ice-servers.json`, на файлы в `groups/`. Владелец root.
|
||||
- [ ] **Мониторинг диска:** Следить за местом (df -h) и размером журнала. При желании — оповещение при заполнении выше порога (например 80%).
|
||||
|
||||
@@ -105,6 +105,8 @@ ip a show wg0
|
||||
|
||||
Ожидаемый адрес: `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)
|
||||
|
||||
@@ -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).
|
||||
- **Системный 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).
|
||||
|
||||
@@ -216,6 +217,7 @@ sudo resize2fs /dev/sdb1
|
||||
|
||||
## 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%.
|
||||
- [ ] **Логи 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), не коммитить в публичные репозитории.
|
||||
|
||||
23
docs/maintenance/logrotate/README.md
Normal file
23
docs/maintenance/logrotate/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Logrotate: базовая политика homelab
|
||||
|
||||
Конфигурации logrotate для Proxmox, LXC-контейнеров, Nginx Proxy Manager и VPN/SSH.
|
||||
|
||||
## Политики
|
||||
|
||||
| Цель | Срок | Размер/архивы | Сжатие |
|
||||
|-----|------|----------------|--------|
|
||||
| **Proxmox host** | 14 дней | max 100 MB на файл, 5–7 архивов | да |
|
||||
| **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`.
|
||||
22
docs/maintenance/logrotate/lxc-default.conf
Normal file
22
docs/maintenance/logrotate/lxc-default.conf
Normal 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
|
||||
}
|
||||
17
docs/maintenance/logrotate/npm-nginx.conf
Normal file
17
docs/maintenance/logrotate/npm-nginx.conf
Normal 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
|
||||
}
|
||||
35
docs/maintenance/logrotate/proxmox-host.conf
Normal file
35
docs/maintenance/logrotate/proxmox-host.conf
Normal file
@@ -0,0 +1,35 @@
|
||||
# Базовые настройки для Proxmox host (192.168.1.150)
|
||||
# 14 дней, max 100 MB на файл, 5–7 архивов, сжатие
|
||||
#
|
||||
# Вариант 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
|
||||
}
|
||||
19
docs/maintenance/logrotate/vpn-ssh.conf
Normal file
19
docs/maintenance/logrotate/vpn-ssh.conf
Normal 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
|
||||
}
|
||||
50
docs/vps/amnezia-awg/Dockerfile
Normal file
50
docs/vps/amnezia-awg/Dockerfile
Normal 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 [ "" ]
|
||||
@@ -58,3 +58,82 @@
|
||||
- Переключение: выбираешь активное подключение — либо одно, либо другое (или отключено). Маршруты/политики при необходимости те же для обоих (например, весь трафик в туннель или только часть по правилам).
|
||||
|
||||
Так можно в любой момент перейти с 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, h1–h4**.
|
||||
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 для метрик.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
- Контейнер запущен с `--restart=always`, примонтирован `/lib/modules` (для ядерного модуля WireGuard). Сеть — bridge, проброс порта 33118/udp на хост.
|
||||
- Дополнительно: **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#конфиги-для-полного-восстановления-сервера).
|
||||
|
||||
---
|
||||
|
||||
|
||||
16
docs/vps/vps-metrics.service
Normal file
16
docs/vps/vps-metrics.service
Normal 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
|
||||
@@ -55,6 +55,8 @@ VPS в ЦОД Миран (Санкт-Петербург). Развёрнуты
|
||||
|
||||
В ботаx (переменные окружения prod) заданы `S3_ENDPOINT_URL=https://api.s3.miran.ru`, регион и креды для загрузки/выдачи контента. Для локальной разработки или других клиентов использовать те же endpoint и ключи.
|
||||
|
||||
**Бакет контента бота:** в S3 Miran используется бакет с именем `9829-telegram-helper-bot` (переменная `S3_BUCKET_NAME` в .env бота).
|
||||
|
||||
### 4. Остальное на хосте
|
||||
|
||||
- **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).
|
||||
|
||||
Остальные узлы 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).
|
||||
|
||||
33
scripts/backup-ct101-pgdump.sh
Normal file
33
scripts/backup-ct101-pgdump.sh
Normal 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
|
||||
31
scripts/backup-ct103-gitea-pgdump.sh
Normal file
31
scripts/backup-ct103-gitea-pgdump.sh
Normal 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
|
||||
31
scripts/backup-ct104-pgdump.sh
Normal file
31
scripts/backup-ct104-pgdump.sh
Normal 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
|
||||
31
scripts/backup-ct105-vectors.sh
Normal file
31
scripts/backup-ct105-vectors.sh
Normal 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
23
scripts/backup-etc-pve.sh
Normal 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
|
||||
21
scripts/backup-immich-photos.sh
Normal file
21
scripts/backup-immich-photos.sh
Normal 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/"
|
||||
61
scripts/backup-restic-yandex.sh
Normal file
61
scripts/backup-restic-yandex.sh
Normal 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:00–03: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."
|
||||
130
scripts/backup-setup-sdb1-mount.sh
Normal file
130
scripts/backup-setup-sdb1-mount.sh
Normal 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"
|
||||
33
scripts/backup-vaultwarden-data.sh
Normal file
33
scripts/backup-vaultwarden-data.sh
Normal 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
|
||||
41
scripts/backup-vm200-pgdump.sh
Normal file
41
scripts/backup-vm200-pgdump.sh
Normal 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
|
||||
73
scripts/backup-vps-miran.sh
Normal file
73
scripts/backup-vps-miran.sh
Normal 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
|
||||
14
scripts/immich-pgdump-remote.sh
Normal file
14
scripts/immich-pgdump-remote.sh
Normal 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}"
|
||||
74
scripts/setup-vps-miran-backup-on-proxmox.sh
Normal file
74
scripts/setup-vps-miran-backup-on-proxmox.sh
Normal 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'"
|
||||
94
scripts/vps-metrics-api.py
Normal file
94
scripts/vps-metrics-api.py
Normal 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()
|
||||
Reference in New Issue
Block a user