diff --git a/docs/backup/backup-howto.md b/docs/backup/backup-howto.md new file mode 100644 index 0000000..52d5a40 --- /dev/null +++ b/docs/backup/backup-howto.md @@ -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 /path/to/backup.vma.zst --storage local-lvm` (и т.п., см. `pct restore --help`). +- VM: `qm restore /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 -d + ``` + Или распаковать `.sql.gz`, затем: + ```bash + docker compose exec -T database psql -U -d < backup.sql + ``` + `` и `` — из `/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. diff --git a/docs/backup/proxmox-phase1-backup.md b/docs/backup/proxmox-phase1-backup.md index 986a229..5802bbc 100644 --- a/docs/backup/proxmox-phase1-backup.md +++ b/docs/backup/proxmox-phase1-backup.md @@ -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 и обновлять при смене. diff --git a/docs/backup/restic-yandex-env.example b/docs/backup/restic-yandex-env.example new file mode 100644 index 0000000..8a78c01 --- /dev/null +++ b/docs/backup/restic-yandex-env.example @@ -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 diff --git a/docs/containers/container-100.md b/docs/containers/container-100.md index bee3ca6..e824f21 100644 --- a/docs/containers/container-100.md +++ b/docs/containers/container-100.md @@ -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%). diff --git a/docs/containers/container-101.md b/docs/containers/container-101.md index c2b974e..ca56e1b 100644 --- a/docs/containers/container-101.md +++ b/docs/containers/container-101.md @@ -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 = только ошибки), чтобы уменьшить рост лога. - [ ] **Резервное копирование:** Регулярный бэкап: diff --git a/docs/containers/container-103.md b/docs/containers/container-103.md index 7efb282..57288dd 100644 --- a/docs/containers/container-103.md +++ b/docs/containers/container-103.md @@ -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//*.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. diff --git a/docs/containers/container-104.md b/docs/containers/container-104.md index 5d2facd..ac309a4 100644 --- a/docs/containers/container-104.md +++ b/docs/containers/container-104.md @@ -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 (если ещё не задан). diff --git a/docs/containers/container-105.md b/docs/containers/container-105.md index f2b612e..5d4f7cb 100644 --- a/docs/containers/container-105.md +++ b/docs/containers/container-105.md @@ -79,6 +79,7 @@ curl -s -H "X-API-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: " 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. diff --git a/docs/containers/container-107.md b/docs/containers/container-107.md index 1a27f53..e92e455 100644 --- a/docs/containers/container-107.md +++ b/docs/containers/container-107.md @@ -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), убедиться, что эти файлы не попадают в публичные репозитории. diff --git a/docs/containers/container-108.md b/docs/containers/container-108.md index bb7002d..c6f11d9 100644 --- a/docs/containers/container-108.md +++ b/docs/containers/container-108.md @@ -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%). diff --git a/docs/containers/container-109.md b/docs/containers/container-109.md index 6fe4466..0d2fd12 100644 --- a/docs/containers/container-109.md +++ b/docs/containers/container-109.md @@ -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) diff --git a/docs/containers/container-200.md b/docs/containers/container-200.md index a78c802..a4f3584 100644 --- a/docs/containers/container-200.md +++ b/docs/containers/container-200.md @@ -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), не коммитить в публичные репозитории. diff --git a/docs/maintenance/logrotate/README.md b/docs/maintenance/logrotate/README.md new file mode 100644 index 0000000..db6499e --- /dev/null +++ b/docs/maintenance/logrotate/README.md @@ -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`. diff --git a/docs/maintenance/logrotate/lxc-default.conf b/docs/maintenance/logrotate/lxc-default.conf new file mode 100644 index 0000000..1883d3b --- /dev/null +++ b/docs/maintenance/logrotate/lxc-default.conf @@ -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 +} diff --git a/docs/maintenance/logrotate/npm-nginx.conf b/docs/maintenance/logrotate/npm-nginx.conf new file mode 100644 index 0000000..d1cd61a --- /dev/null +++ b/docs/maintenance/logrotate/npm-nginx.conf @@ -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 +} diff --git a/docs/maintenance/logrotate/proxmox-host.conf b/docs/maintenance/logrotate/proxmox-host.conf new file mode 100644 index 0000000..11a436b --- /dev/null +++ b/docs/maintenance/logrotate/proxmox-host.conf @@ -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 +} diff --git a/docs/maintenance/logrotate/vpn-ssh.conf b/docs/maintenance/logrotate/vpn-ssh.conf new file mode 100644 index 0000000..bd32dfb --- /dev/null +++ b/docs/maintenance/logrotate/vpn-ssh.conf @@ -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 +} diff --git a/docs/vps/amnezia-awg/Dockerfile b/docs/vps/amnezia-awg/Dockerfile new file mode 100644 index 0000000..8928c89 --- /dev/null +++ b/docs/vps/amnezia-awg/Dockerfile @@ -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 [ "" ] diff --git a/docs/vps/vpn-migrate-config.md b/docs/vps/vpn-migrate-config.md index 572b6e9..6db07b9 100644 --- a/docs/vps/vpn-migrate-config.md +++ b/docs/vps/vpn-migrate-config.md @@ -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 для метрик. diff --git a/docs/vps/vpn-vps-amneziawg.md b/docs/vps/vpn-vps-amneziawg.md index 84ecb69..6fc58b0 100644 --- a/docs/vps/vpn-vps-amneziawg.md +++ b/docs/vps/vpn-vps-amneziawg.md @@ -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#конфиги-для-полного-восстановления-сервера). --- diff --git a/docs/vps/vps-metrics.service b/docs/vps/vps-metrics.service new file mode 100644 index 0000000..4325345 --- /dev/null +++ b/docs/vps/vps-metrics.service @@ -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 diff --git a/docs/vps/vps-miran-bots.md b/docs/vps/vps-miran-bots.md index 4e5a190..91ef73a 100644 --- a/docs/vps/vps-miran-bots.md +++ b/docs/vps/vps-miran-bots.md @@ -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). diff --git a/scripts/backup-ct101-pgdump.sh b/scripts/backup-ct101-pgdump.sh new file mode 100644 index 0000000..e83c15e --- /dev/null +++ b/scripts/backup-ct101-pgdump.sh @@ -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 diff --git a/scripts/backup-ct103-gitea-pgdump.sh b/scripts/backup-ct103-gitea-pgdump.sh new file mode 100644 index 0000000..f05c030 --- /dev/null +++ b/scripts/backup-ct103-gitea-pgdump.sh @@ -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 diff --git a/scripts/backup-ct104-pgdump.sh b/scripts/backup-ct104-pgdump.sh new file mode 100644 index 0000000..b2f4328 --- /dev/null +++ b/scripts/backup-ct104-pgdump.sh @@ -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 diff --git a/scripts/backup-ct105-vectors.sh b/scripts/backup-ct105-vectors.sh new file mode 100644 index 0000000..25b19bd --- /dev/null +++ b/scripts/backup-ct105-vectors.sh @@ -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 diff --git a/scripts/backup-etc-pve.sh b/scripts/backup-etc-pve.sh new file mode 100644 index 0000000..93af2ac --- /dev/null +++ b/scripts/backup-etc-pve.sh @@ -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 diff --git a/scripts/backup-immich-photos.sh b/scripts/backup-immich-photos.sh new file mode 100644 index 0000000..6127bec --- /dev/null +++ b/scripts/backup-immich-photos.sh @@ -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/" diff --git a/scripts/backup-restic-yandex.sh b/scripts/backup-restic-yandex.sh new file mode 100644 index 0000000..589764f --- /dev/null +++ b/scripts/backup-restic-yandex.sh @@ -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." diff --git a/scripts/backup-setup-sdb1-mount.sh b/scripts/backup-setup-sdb1-mount.sh new file mode 100644 index 0000000..d08f10e --- /dev/null +++ b/scripts/backup-setup-sdb1-mount.sh @@ -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" diff --git a/scripts/backup-vaultwarden-data.sh b/scripts/backup-vaultwarden-data.sh new file mode 100644 index 0000000..fe55154 --- /dev/null +++ b/scripts/backup-vaultwarden-data.sh @@ -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 diff --git a/scripts/backup-vm200-pgdump.sh b/scripts/backup-vm200-pgdump.sh new file mode 100644 index 0000000..d798081 --- /dev/null +++ b/scripts/backup-vm200-pgdump.sh @@ -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 diff --git a/scripts/backup-vps-miran.sh b/scripts/backup-vps-miran.sh new file mode 100644 index 0000000..921860c --- /dev/null +++ b/scripts/backup-vps-miran.sh @@ -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 diff --git a/scripts/immich-pgdump-remote.sh b/scripts/immich-pgdump-remote.sh new file mode 100644 index 0000000..ef1015d --- /dev/null +++ b/scripts/immich-pgdump-remote.sh @@ -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}" diff --git a/scripts/setup-vps-miran-backup-on-proxmox.sh b/scripts/setup-vps-miran-backup-on-proxmox.sh new file mode 100644 index 0000000..1578203 --- /dev/null +++ b/scripts/setup-vps-miran-backup-on-proxmox.sh @@ -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'" diff --git a/scripts/vps-metrics-api.py b/scripts/vps-metrics-api.py new file mode 100644 index 0000000..05049fd --- /dev/null +++ b/scripts/vps-metrics-api.py @@ -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()