1159 lines
36 KiB
Markdown
1159 lines
36 KiB
Markdown
# 11. Резервное копирование
|
||
|
||
## Обзор
|
||
|
||
Резервное копирование критически важно для защиты данных и возможности восстановления после сбоев, ошибок или атак.
|
||
|
||
**Текущий статус (апрель 2026)**: ✅ **Настроена полнофункциональная система резервного копирования на базе Proxmox Backup Server**
|
||
|
||
- Отдельный физический сервер PBS (192.168.1.199)
|
||
- Хранилище 1 TB с дедупликацией и компрессией
|
||
- Автоматические бэкапы всех критичных систем
|
||
- Протестированное восстановление
|
||
- ✅ Настроены уведомления в Telegram о статусе задач
|
||
|
||
---
|
||
|
||
## Текущее состояние
|
||
|
||
### ✅ Настроено и работает
|
||
|
||
| Компонент | Статус | Описание |
|
||
|-----------|--------|----------|
|
||
| **Proxmox Backup Server** | ✅ | Отдельный сервер (Olimpbs, 192.168.1.199) |
|
||
| **Хранилище** | ✅ | 1 TB (LVM), datastore `olimpbkp` |
|
||
| **Бэкапы ВМ/LXC** | ✅ | Автоматические, по расписанию |
|
||
| **Дедупликация** | ✅ | Встроена в PBS |
|
||
| **Компрессия** | ✅ | ZSTD (fast and good) |
|
||
| **Расписание** | ✅ | 2 раза в день (02:30 и 22:30) |
|
||
| **Retention** | ✅ | 14 последних, 7 ежедневных, 4 недельных, 4 месячных |
|
||
| **Режим** | ✅ | Snapshot (без остановки сервисов) |
|
||
| **Уведомления** | ✅ | Telegram-бот через прокси (XRay + Redsocks) |
|
||
|
||
### 📊 Архитектура
|
||
|
||
```mermaid
|
||
graph TD
|
||
A["Proxmox VE Host<br/>Olimp 192.168.1.200"] --> B["LXC Containers<br/>201-211"]
|
||
A --> C["Virtual Machines<br/>205, 213"]
|
||
A -->|Network| D["Proxmox Backup Server<br/>Olimpbs 192.168.1.199"]
|
||
D --> E["Datastore: olimpbkp<br/>1 TB LVM"]
|
||
E --> F["/var/lib/proxmox-backup/olimpbkp"]
|
||
D -->|HTTPS via proxy| G["Telegram API<br/>api.telegram.org"]
|
||
```
|
||
|
||
---
|
||
|
||
## Что бэкапится
|
||
|
||
### LXC Контейнеры (10 шт.)
|
||
|
||
| ID | Имя | Тип данных | Примерный размер |
|
||
|----|-----|------------|------------------|
|
||
| **201** | gateway | Конфигурации, прокси | ~5 GB |
|
||
| **202** | data | Базы данных (BookStack, Mealie) | ~10 GB |
|
||
| **203** | media | Конфигурации Jellyfin, метаданные | ~60 GB* |
|
||
| **204** | photo | Immich конфиги | ~20 GB |
|
||
| **206** | talk | Matrix, XMPP конфиги | ~10 GB |
|
||
| **207** | games | Конфигурации игровых серверов | ~20 GB |
|
||
| **208** | manage | Grafana, Loki, мониторинг | ~15 GB |
|
||
| **209** | git | GitLab репозитории | ~30 GB |
|
||
| **210** | ansible | Ansible конфиги, vault | ~5 GB |
|
||
| **211** | torrent | qBittorrent настройки | ~5 GB |
|
||
|
||
*Без медиафайлов (исключены из бэкапа)
|
||
|
||
### Виртуальные машины (2 шт.)
|
||
|
||
| ID | Имя | Тип данных | Примерный размер |
|
||
|----|-----|------------|------------------|
|
||
| **205** | Nextcloud | Файлы, БД PostgreSQL | ~10 GB |
|
||
| **213** | VPN | OpenVPN/WireGuard конфиги | ~12 GB |
|
||
|
||
### Исключено из бэкапа
|
||
|
||
- **Большие диски** (16 TB RAID6): медиафайлы, игры, загрузки
|
||
- **Причина**: занимают слишком много места, можно восстановить из источников
|
||
- **Что включено**: только конфигурации и метаданные (через `backup=1`)
|
||
|
||
---
|
||
|
||
## Расписание бэкапов
|
||
|
||
### Автоматическое расписание
|
||
|
||
**Два раза в день**:
|
||
- **02:30** — ночной бэкап
|
||
- **22:30** — вечерний бэкап
|
||
|
||
**Proxmox Backup Server автоматически определяет**:
|
||
- Первый бэкап — **полный**
|
||
- Последующие — **инкрементальные** (только изменения)
|
||
- Периодически — **полный** для целостности данных
|
||
|
||
### Retention политика
|
||
|
||
| Тип хранения | Количество | Фактический срок |
|
||
|--------------|------------|------------------|
|
||
| **Keep Last** | 14 | Минимум 14 последних бэкапов |
|
||
| **Keep Daily** | 7 | 7 ежедневных копий |
|
||
| **Keep Weekly** | 4 | 4 недельных копии |
|
||
| **Keep Monthly** | 4 | 4 месячных копии |
|
||
|
||
**Итого**: ~1-2 месяца истории бэкапов
|
||
|
||
---
|
||
|
||
## Текущая конфигурация
|
||
|
||
### На Proxmox VE (Olimp)
|
||
|
||
**Хранилище подключено**:
|
||
- **ID**: `olimpbkp`
|
||
- **Тип**: Proxmox Backup Server
|
||
- **Server**: 192.168.1.199:8007
|
||
- **Datastore**: `olimpbkp`
|
||
- **Content**: Backup
|
||
- **Status**: ✅ Enabled
|
||
|
||
**Backup Job**:
|
||
```text
|
||
Node: Olimp
|
||
Storage: olimpbkp
|
||
Schedule: 02:30, 22:30 (daily)
|
||
Mode: Snapshot
|
||
Compression: ZSTD
|
||
VM/CT: 201-211, 205, 213
|
||
Retention: keep-last=14, keep-daily=7, keep-weekly=4, keep-monthly=4
|
||
```
|
||
|
||
### На PBS (Olimpbs)
|
||
|
||
**Datastore**:
|
||
```text
|
||
Name: olimpbkp
|
||
Path: /var/lib/proxmox-backup/olimpbkp
|
||
Size: 1 TB (912 GB available)
|
||
GC Schedule: daily
|
||
Prune Schedule: daily
|
||
```
|
||
|
||
**Пользователи**:
|
||
- `root@pam` — администратор (веб-интерфейс)
|
||
- `pve@pbs` — для Proxmox VE (API токен)
|
||
|
||
---
|
||
|
||
## Управление бэкапами
|
||
|
||
### Просмотр бэкапов
|
||
|
||
**Через веб-интерфейс PBS**:
|
||
```text
|
||
https://192.168.1.199:8007
|
||
→ Datastore → olimpbkp → Content
|
||
```
|
||
|
||
**Через Proxmox VE**:
|
||
```text
|
||
https://192.168.1.220:8006
|
||
→ Datacenter → Storage → olimpbkp → Content
|
||
```
|
||
|
||
**Через CLI**:
|
||
```bash
|
||
# Список бэкапов
|
||
proxmox-backup-client list --repository root@pam@192.168.1.199:8007:olimpbkp
|
||
|
||
# Детальная информация
|
||
proxmox-backup-client show-backup --repository root@pam@192.168.1.199:8007:olimpbkp ct/201
|
||
|
||
# Статистика datastore
|
||
proxmox-backup-client datastore stats olimpbkp
|
||
```
|
||
|
||
### Ручной запуск бэкапа
|
||
|
||
**Один контейнер/ВМ**:
|
||
```bash
|
||
vzdump 201 --storage olimpbkp --mode snapshot --compress zstd
|
||
```
|
||
|
||
**Все бэкапируемые системы**:
|
||
```bash
|
||
for id in 201 202 203 204 205 206 207 208 209 210 211 213; do
|
||
vzdump $id --storage olimpbkp --mode snapshot --compress zstd &
|
||
done
|
||
wait
|
||
```
|
||
|
||
### Восстановление из бэкапа
|
||
|
||
**Через веб-интерфейс Proxmox VE**:
|
||
1. Datacenter → Storage → olimpbkp → Content
|
||
2. Выбрать бэкап → Restore
|
||
3. Указать:
|
||
- **Storage**: целевое хранилище (local-lvm, vmsystem)
|
||
- **VMID**: новый ID (или существующий для перезаписи)
|
||
4. Start
|
||
|
||
**Через CLI**:
|
||
```bash
|
||
# LXC контейнер
|
||
pct restore 216 olimpbkp:backup/ct/201/2026-04-11T13:19:03Z --storage local-lvm
|
||
|
||
# Виртуальная машина
|
||
qm restore 216 olimpbkp:backup/vm/205/2026-04-11T13:19:03Z --storage vmsystem
|
||
```
|
||
|
||
### Восстановление отдельных файлов
|
||
|
||
**Через File Restore**:
|
||
1. PBS веб-интерфейс → Datastore → Content
|
||
2. Выбрать бэкап → **File Restore**
|
||
3. Смонтировать бэкап
|
||
4. Скачать нужные файлы
|
||
5. Unmount
|
||
|
||
**Через CLI**:
|
||
```bash
|
||
# Смонтировать бэкап
|
||
mkdir -p /mnt/restore
|
||
proxmox-backup-client mount ct/201/2026-04-11T13:19:03Z /mnt/restore \
|
||
--repository root@pam@192.168.1.199:8007:olimpbkp
|
||
|
||
# Скопировать файлы
|
||
cp /mnt/restore/root.pxar/etc/config/file.conf /tmp/
|
||
|
||
# Размонтировать
|
||
proxmox-backup-client unmount /mnt/restore
|
||
```
|
||
|
||
---
|
||
|
||
## Мониторинг и обслуживание
|
||
|
||
### Проверка статуса бэкапов
|
||
|
||
**Ежедневная проверка**:
|
||
```bash
|
||
# Последние задачи
|
||
pvesm status olimpbkp
|
||
|
||
# Логи бэкапов
|
||
tail -f /var/log/pve/tasks/*
|
||
|
||
# Проверка места на PBS
|
||
ssh root@192.168.1.199 "df -h /var/lib/proxmox-backup/olimpbkp"
|
||
```
|
||
|
||
### Garbage Collection (очистка)
|
||
|
||
PBS автоматически запускает GC ежедневно. Вручную:
|
||
```bash
|
||
# На PBS сервере
|
||
proxmox-backup-manager garbage-collection start olimpbkp
|
||
|
||
# Проверка статуса
|
||
proxmox-backup-manager garbage-collection status olimpbkp
|
||
```
|
||
|
||
### Prune (удаление старых бэкапов)
|
||
|
||
Автоматически по retention политике. Вручную:
|
||
```bash
|
||
proxmox-backup-manager prune-job run <job-id>
|
||
```
|
||
|
||
### Проверка целостности (Verify)
|
||
|
||
```bash
|
||
# Проверить все бэкапы
|
||
proxmox-backup-manager verify olimpbkp
|
||
|
||
# Проверить конкретный бэкап
|
||
proxmox-backup-client verify ct/201/2026-04-11T13:19:03Z \
|
||
--repository root@pam@192.168.1.199:8007:olimpbkp
|
||
```
|
||
|
||
---
|
||
|
||
## 📦 Уведомления в Telegram
|
||
|
||
### 🎯 Цель
|
||
Получать уведомления в Telegram об успешных/неудачных бэкапах с Proxmox Backup Server, без изменений на гипервизоре.
|
||
В системе настроено **два независимых канала** уведомлений в Telegram:
|
||
|
||
| Тип | Источник | Сервер | Порт реле | Что уведомляет |
|
||
|-----|----------|--------|-----------|----------------|
|
||
| **PBS Backup Notifications** | Proxmox Backup Server | 192.168.1.199 | 8085 | ✅ Успешные/❌ неудачные бэкапы |
|
||
| **Grafana Alerts** | VictoriaMetrics + Grafana | 192.168.1.208 | 8085 | ⚠️ Бэкапы старше 24 часов |
|
||
|
||
### 📋 Предварительные требования
|
||
- PBS сервер (192.168.1.199) с доступом в интернет через прокси
|
||
- Telegram бот: токен и chat_id
|
||
- Прокси: VLESS+Reality на `2.27.50.20:2054`
|
||
|
||
### 🔧 Настройка прокси (XRay + Redsocks + iptables)
|
||
|
||
**1. Установка XRay клиента**
|
||
```bash
|
||
# Установка зависимостей
|
||
apt update && apt install unzip curl iptables -y
|
||
|
||
# Скачиваем XRay
|
||
cd /tmp
|
||
curl -L -o xray.zip https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip
|
||
unzip xray.zip
|
||
chmod +x xray
|
||
mv xray geoip.dat geosite.dat /usr/local/bin/
|
||
|
||
# Проверка
|
||
xray version
|
||
```
|
||
|
||
**2. Конфигурация XRay**
|
||
```bash
|
||
mkdir -p /usr/local/etc/xray
|
||
|
||
cat > /usr/local/etc/xray/config.json << 'EOF'
|
||
{
|
||
"log": {"loglevel": "warning"},
|
||
"inbounds": [{"port": 1080, "listen": "127.0.0.1", "protocol": "socks", "settings": {"auth": "noauth", "udp": true}}],
|
||
"outbounds": [
|
||
{
|
||
"protocol": "vless",
|
||
"settings": {
|
||
"vnext": [{
|
||
"address": "2.27.50.20",
|
||
"port": 2054,
|
||
"users": [{"id": "68f44a38-396d-48da-b832-79b5dc5716ab", "encryption": "none", "level": 8}]
|
||
}]
|
||
},
|
||
"streamSettings": {
|
||
"network": "xhttp",
|
||
"security": "reality",
|
||
"realitySettings": {
|
||
"serverName": "cloud.zailon.ru",
|
||
"publicKey": "TOyddQCTdSpycmO20uiLOqMABuKabpwVhw57tWmvJws",
|
||
"shortId": "174fc0",
|
||
"spiderX": "/"
|
||
},
|
||
"xhttpSettings": {
|
||
"host": "",
|
||
"path": "/remote.php/dav/upload",
|
||
"mode": "stream-one"
|
||
}
|
||
},
|
||
"tag": "proxy"
|
||
},
|
||
{"protocol": "freedom", "tag": "direct"}
|
||
],
|
||
"routing": {
|
||
"rules": [
|
||
{"type": "field", "ip": ["geoip:private"], "outboundTag": "direct"},
|
||
{"type": "field", "domain": ["api.telegram.org"], "outboundTag": "proxy"}
|
||
]
|
||
}
|
||
}
|
||
EOF
|
||
```
|
||
|
||
**3. Запуск XRay как сервис**
|
||
```bash
|
||
cat > /etc/systemd/system/xray-client.service << 'EOF'
|
||
[Unit]
|
||
Description=XRay Client
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/config.json
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable --now xray-client
|
||
systemctl status xray-client
|
||
```
|
||
|
||
**4. Установка и настройка Redsocks**
|
||
```bash
|
||
# Установка
|
||
apt install redsocks -y
|
||
|
||
# Конфигурация
|
||
cat > /etc/redsocks.conf << 'EOF'
|
||
base {
|
||
log_debug = off;
|
||
log_info = off;
|
||
log = stderr;
|
||
daemon = on;
|
||
redirector = iptables;
|
||
}
|
||
|
||
redsocks {
|
||
bind = "0.0.0.0:12345";
|
||
relay = "127.0.0.1:1080";
|
||
type = socks5;
|
||
autoproxy = 0;
|
||
timeout = 10;
|
||
}
|
||
EOF
|
||
|
||
# Запуск
|
||
systemctl enable --now redsocks
|
||
```
|
||
|
||
**5. Настройка iptables для Telegram**
|
||
```bash
|
||
cat > /usr/local/bin/telegram-proxy.sh << 'EOF'
|
||
#!/bin/bash
|
||
iptables -t nat -F TELEGRAM_PROXY 2>/dev/null
|
||
iptables -t nat -X TELEGRAM_PROXY 2>/dev/null
|
||
iptables -t nat -N TELEGRAM_PROXY
|
||
|
||
# Telegram IP ranges
|
||
iptables -t nat -A TELEGRAM_PROXY -p tcp -d 149.154.160.0/20 -j REDIRECT --to-ports 12345
|
||
iptables -t nat -A TELEGRAM_PROXY -p tcp -d 91.108.4.0/22 -j REDIRECT --to-ports 12345
|
||
|
||
iptables -t nat -A OUTPUT -p tcp --dport 443 -j TELEGRAM_PROXY
|
||
EOF
|
||
|
||
chmod +x /usr/local/bin/telegram-proxy.sh
|
||
/usr/local/bin/telegram-proxy.sh
|
||
```
|
||
|
||
**6. Проверка соединения**
|
||
```bash
|
||
curl -s "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getMe"
|
||
```
|
||
*Ожидаемый результат*: JSON с информацией о боте.
|
||
|
||
### 🔧 Скрипт уведомлений
|
||
|
||
**7. Создание скрипта**
|
||
```bash
|
||
cat > /usr/local/bin/pbs-backup-notify.sh << 'EOF'
|
||
#!/bin/bash
|
||
|
||
TOKEN="YOUR_BOT_TOKEN"
|
||
CHAT="YOUR_CHAT_ID"
|
||
ARCHIVE="/var/log/proxmox-backup/tasks/archive"
|
||
STATE="/var/run/pbs-notify-lastline"
|
||
|
||
# Получаем последнюю обработанную строку
|
||
LAST_LINE=$(cat $STATE 2>/dev/null || echo 0)
|
||
CURR_LINE=$(wc -l < $ARCHIVE)
|
||
|
||
# Если файл уменьшился - начинаем сначала
|
||
if [ "$CURR_LINE" -lt "$LAST_LINE" ]; then
|
||
LAST_LINE=0
|
||
fi
|
||
|
||
# Читаем новые строки
|
||
if [ "$CURR_LINE" -gt "$LAST_LINE" ]; then
|
||
tail -n +$((LAST_LINE + 1)) $ARCHIVE | while IFS= read -r line; do
|
||
# Только backup задачи
|
||
if echo "$line" | grep -q ":backup:"; then
|
||
# Извлекаем имя бэкапа (конвертируем \x3a в :)
|
||
BACKUP=$(echo "$line" | grep -oP 'backup:\K[^:]+' | sed 's/\\x3a/:/g')
|
||
STATUS=$(echo "$line" | grep -oP 'OK|FAILED|ERROR')
|
||
TIME=$(date '+%Y-%m-%d %H:%M:%S')
|
||
|
||
if [ "$STATUS" = "OK" ]; then
|
||
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"chat_id\":\"$CHAT\",\"text\":\"✅ *Backup Success*\\n📦 $BACKUP\\n⏰ $TIME\",\"parse_mode\":\"Markdown\"}" > /dev/null
|
||
else
|
||
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"chat_id\":\"$CHAT\",\"text\":\"❌ *Backup Failed*\\n📦 $BACKUP\\n⏰ $TIME\\n📝 Status: $STATUS\",\"parse_mode\":\"Markdown\"}" > /dev/null
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Сохраняем позицию
|
||
echo $CURR_LINE > $STATE
|
||
EOF
|
||
|
||
chmod +x /usr/local/bin/pbs-backup-notify.sh
|
||
```
|
||
|
||
**8. Настройка Cron (каждые 5 минут)**
|
||
```bash
|
||
echo "*/5 * * * * root /usr/local/bin/pbs-backup-notify.sh" >> /etc/cron.d/pbs-notify
|
||
```
|
||
|
||
**9. Инициализация и тест**
|
||
```bash
|
||
# Сброс состояния для теста
|
||
echo "0" > /var/run/pbs-notify-lastline
|
||
|
||
# Запуск вручную
|
||
/usr/local/bin/pbs-backup-notify.sh
|
||
|
||
# Проверка состояния
|
||
cat /var/run/pbs-notify-lastline
|
||
wc -l < /var/log/proxmox-backup/tasks/archive
|
||
```
|
||
|
||
|
||
### 📝 Переменные для замены
|
||
|
||
| Переменная | Описание | Пример |
|
||
|------------|----------|--------|
|
||
| `YOUR_BOT_TOKEN` | Токен Telegram бота | `7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4` |
|
||
| `YOUR_CHAT_ID` | ID чата для уведомлений | `292909723` |
|
||
| `address` | Адрес прокси-сервера | `2.27.50.20` |
|
||
| `port` | Порт прокси | `2054` |
|
||
| `id` | UUID пользователя VLESS | `68f44a38-396d-48da-b832-79b5dc5716ab` |
|
||
| `publicKey` | Публичный ключ Reality | `TOyddQCTdSpycmO20uiLOqMABuKabpwVhw57tWmvJws` |
|
||
| `serverName` | Домен для Reality | `cloud.zailon.ru` |
|
||
|
||
### 📁 Файлы конфигурации
|
||
|
||
| Файл | Назначение |
|
||
|------|------------|
|
||
| `/usr/local/etc/xray/config.json` | Конфиг XRay прокси |
|
||
| `/etc/redsocks.conf` | Конфиг Redsocks |
|
||
| `/usr/local/bin/telegram-proxy.sh` | Правила iptables для Telegram |
|
||
| `/usr/local/bin/pbs-backup-notify.sh` | Скрипт уведомлений |
|
||
| `/var/run/pbs-notify-lastline` | Позиция последнего прочитанного лога |
|
||
| `/etc/cron.d/pbs-notify` | Cron задача |
|
||
|
||
## 📦 Настройка уведомлений из Grafana в Telegram
|
||
|
||
### 🎯 Цель
|
||
Настроить отправку алертов из Grafana в Telegram с красивым форматированием, эмодзи и автоматическим преобразованием времени.
|
||
|
||
### 📋 Архитектура
|
||
|
||
```mermaid
|
||
graph LR
|
||
A["Grafana<br/>192.168.1.208"] -->|HTTP POST| B["Python Relay<br/>localhost:8085"]
|
||
B -->|SOCKS5| C["XRay Client<br/>127.0.0.1:1080"]
|
||
C -->|HTTPS| D["Telegram API<br/>api.telegram.org"]
|
||
```
|
||
|
||
---
|
||
### 🔧 Шаг 0: Установка XRay (если не установлен)
|
||
|
||
**XRay нужен для проксирования запросов в Telegram.**
|
||
|
||
**1. Установи зависимости:**
|
||
|
||
```bash
|
||
apt update && apt install unzip curl iptables -y
|
||
```
|
||
|
||
**2. Скачай и установи XRay:**
|
||
|
||
```bash
|
||
cd /tmp
|
||
curl -L -o xray.zip https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip
|
||
unzip xray.zip
|
||
chmod +x xray
|
||
mv xray geoip.dat geosite.dat /usr/local/bin/
|
||
|
||
# Проверка
|
||
xray version
|
||
```
|
||
|
||
**3. Создай конфигурацию:**
|
||
|
||
```bash
|
||
mkdir -p /usr/local/etc/xray
|
||
|
||
cat > /usr/local/etc/xray/config.json << 'EOF'
|
||
{
|
||
"log": {"loglevel": "warning"},
|
||
"inbounds": [
|
||
{
|
||
"port": 1080,
|
||
"listen": "127.0.0.1",
|
||
"protocol": "socks",
|
||
"settings": {"auth": "noauth", "udp": true}
|
||
}
|
||
],
|
||
"outbounds": [
|
||
{
|
||
"protocol": "vless",
|
||
"settings": {
|
||
"vnext": [{
|
||
"address": "2.27.50.20",
|
||
"port": 2054,
|
||
"users": [{"id": "68f44a38-396d-48da-b832-79b5dc5716ab", "encryption": "none", "level": 8}]
|
||
}]
|
||
},
|
||
"streamSettings": {
|
||
"network": "xhttp",
|
||
"security": "reality",
|
||
"realitySettings": {
|
||
"serverName": "cloud.zailon.ru",
|
||
"publicKey": "TOyddQCTdSpycmO20uiLOqMABuKabpwVhw57tWmvJws",
|
||
"shortId": "174fc0",
|
||
"spiderX": "/"
|
||
},
|
||
"xhttpSettings": {
|
||
"host": "",
|
||
"path": "/remote.php/dav/upload",
|
||
"mode": "stream-one"
|
||
}
|
||
},
|
||
"tag": "proxy"
|
||
},
|
||
{"protocol": "freedom", "tag": "direct"}
|
||
],
|
||
"routing": {
|
||
"rules": [
|
||
{"type": "field", "ip": ["geoip:private"], "outboundTag": "direct"},
|
||
{"type": "field", "domain": ["api.telegram.org"], "outboundTag": "proxy"}
|
||
]
|
||
}
|
||
}
|
||
EOF
|
||
```
|
||
|
||
**4. Создай systemd-сервис:**
|
||
|
||
```bash
|
||
cat > /etc/systemd/system/xray-client.service << 'EOF'
|
||
[Unit]
|
||
Description=XRay Client
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/config.json
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable --now xray-client
|
||
```
|
||
|
||
**5. Проверь работу:**
|
||
|
||
```bash
|
||
systemctl status xray-client
|
||
|
||
# Тест соединения
|
||
curl -sx socks5://127.0.0.1:1080 \
|
||
https://api.telegram.org/bot7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4/getMe
|
||
```
|
||
|
||
Должен вернуться JSON с информацией о боте.
|
||
|
||
---
|
||
|
||
### 🔧 Шаг 2: Установка Python-реле
|
||
|
||
**1. Создай скрипт реле:**
|
||
|
||
```bash
|
||
cat > /usr/local/bin/telegram-relay.py << 'EOF'
|
||
#!/usr/bin/env python3
|
||
import socket
|
||
import subprocess
|
||
import sys
|
||
import re
|
||
|
||
PORT = 8085
|
||
BOT_TOKEN = "7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4"
|
||
CHAT_ID = "292909723"
|
||
PROXY = "socks5://127.0.0.1:1080"
|
||
|
||
def format_duration(seconds):
|
||
try:
|
||
sec = float(seconds)
|
||
if sec < 60:
|
||
return f"{int(sec)} сек"
|
||
elif sec < 3600:
|
||
return f"{int(sec // 60)} мин"
|
||
elif sec < 86400:
|
||
hours = int(sec // 3600)
|
||
mins = int((sec % 3600) // 60)
|
||
return f"{hours} ч {mins} мин" if mins > 0 else f"{hours} ч"
|
||
else:
|
||
return f"{int(sec // 86400)} дн"
|
||
except:
|
||
return str(seconds)
|
||
|
||
def get_icon(backup):
|
||
return "💻" if backup.startswith('vm-') else "📦" if backup.startswith('ct-') else "🗄️"
|
||
|
||
def send_telegram(text):
|
||
try:
|
||
cmd = [
|
||
'curl', '-sx', PROXY, '-s',
|
||
f'https://api.telegram.org/bot{BOT_TOKEN}/sendMessage',
|
||
'-H', 'Content-Type: application/json',
|
||
'-d', f'{{"chat_id":"{CHAT_ID}","text":"{text}","parse_mode":"Markdown"}}'
|
||
]
|
||
subprocess.run(cmd, timeout=10, capture_output=True)
|
||
return True
|
||
except:
|
||
return False
|
||
|
||
def handle_client(client_socket, addr):
|
||
try:
|
||
data = b""
|
||
client_socket.settimeout(2)
|
||
while True:
|
||
try:
|
||
chunk = client_socket.recv(4096)
|
||
if not chunk:
|
||
break
|
||
data += chunk
|
||
except socket.timeout:
|
||
break
|
||
|
||
data = data.decode('utf-8', errors='ignore')
|
||
body = data.split('\r\n\r\n', 1)[1] if '\r\n\r\n' in data else data
|
||
|
||
# Парсим JSON от Grafana
|
||
backups = re.findall(r'"backup"\s*:\s*"([^"]+)"', body)
|
||
statuses = re.findall(r'"status"\s*:\s*"([^"]+)"', body)
|
||
values = re.findall(r'value=([\d.]+)', body)
|
||
|
||
# Если нашли бэкапы — форматируем как алерт
|
||
if backups:
|
||
CHUNK = 5
|
||
for i in range(0, len(backups), CHUNK):
|
||
chunk_backups = backups[i:i+CHUNK]
|
||
text = "🚨 *Backup Alert*\n\n"
|
||
|
||
for j, backup in enumerate(chunk_backups):
|
||
idx = i + j
|
||
status = statuses[idx] if idx < len(statuses) else "unknown"
|
||
icon = get_icon(backup)
|
||
|
||
if len(values) >= (idx * 2 + 1):
|
||
seconds = float(values[idx * 2])
|
||
human_time = format_duration(seconds)
|
||
desc = f"Последний бэкап: {human_time} назад"
|
||
else:
|
||
desc = "Нет данных"
|
||
|
||
status_emoji = "🔥" if status == "firing" else "✅"
|
||
text += f"{icon} *{backup}*\n⏱ {desc}\nStatus: {status_emoji} *{status}*\n\n"
|
||
|
||
send_telegram(text)
|
||
else:
|
||
# Если не JSON от Grafana — отправляем как есть (для тестов)
|
||
# Очищаем от служебных символов
|
||
clean_text = body.strip().replace('\r', '').replace('\n', ' ')
|
||
if clean_text and len(clean_text) < 4000:
|
||
send_telegram(clean_text)
|
||
|
||
client_socket.send(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nOK")
|
||
|
||
except Exception as e:
|
||
print(f"Error: {e}", file=sys.stderr)
|
||
try:
|
||
client_socket.send(b"HTTP/1.1 500 Error\r\n\r\n")
|
||
except:
|
||
pass
|
||
finally:
|
||
try:
|
||
client_socket.close()
|
||
except:
|
||
pass
|
||
|
||
# Запуск сервера
|
||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||
server.bind(('0.0.0.0', PORT))
|
||
server.listen(10)
|
||
print(f"▶ Telegram Relay on {PORT}", file=sys.stderr)
|
||
|
||
while True:
|
||
client, addr = server.accept()
|
||
print(f"← {addr}", file=sys.stderr)
|
||
handle_client(client, addr)
|
||
EOF
|
||
chmod +x /usr/local/bin/telegram-relay.py
|
||
```
|
||
|
||
**2. Создай systemd-сервис:**
|
||
|
||
```bash
|
||
cat > /etc/systemd/system/telegram-relay.service << 'EOF'
|
||
[Unit]
|
||
Description=Telegram Relay via Xray (Python+curl)
|
||
After=network.target xray-client.service
|
||
Requires=xray-client.service
|
||
|
||
[Service]
|
||
Type=simple
|
||
ExecStart=/usr/bin/python3 /usr/local/bin/telegram-relay.py
|
||
Restart=always
|
||
RestartSec=3
|
||
User=root
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable --now telegram-relay
|
||
```
|
||
|
||
**3. Проверь работу:**
|
||
|
||
```bash
|
||
# Проверка статуса
|
||
systemctl status telegram-relay
|
||
|
||
# Проверка порта
|
||
ss -tlnp | grep 8085
|
||
|
||
# Тест
|
||
curl -X POST http://127.0.0.1:8085 -d "🎉 Test message"
|
||
```
|
||
|
||
Должно прийти сообщение в Telegram!
|
||
|
||
---
|
||
|
||
### 🔧 Шаг 2: Настройка Contact Point в Grafana
|
||
|
||
**1. Открой Grafana:**
|
||
- Перейди: **Alerting** → **Contact points**
|
||
- Нажми **+ Add contact point**
|
||
|
||
**2. Заполни поля:**
|
||
|
||
| Поле | Значение |
|
||
|------|----------|
|
||
| **Name** | `Telegram Relay` |
|
||
| **Integration** | `Webhook` |
|
||
| **URL** | `http://127.0.0.1:8085` |
|
||
| **HTTP Method** | `POST` |
|
||
| **Max Alerts** | `0` (без лимита) |
|
||
|
||
**3. Сообщение (Message):**
|
||
|
||
```text
|
||
{{ range .Alerts }}
|
||
📦 {{ .Labels.backup }}
|
||
⏱ {{ .Annotations.description }}
|
||
Status: {{ if eq .Status "firing" }}🔥{{ else }}✅{{ end }} {{ .Status }}
|
||
---
|
||
{{ end }}
|
||
```
|
||
|
||
**4. Сохрани:**
|
||
- Нажми **Save contact point**
|
||
- Нажми **Test** — должно прийти сообщение
|
||
|
||
---
|
||
|
||
### 🔧 Шаг 3: Создание Alert Rule
|
||
|
||
**1. Перейди:** **Alerting** → **Alert rules** → **+ New alert rule**
|
||
|
||
**2. Query и условие:**
|
||
|
||
| Поле | Значение |
|
||
|------|----------|
|
||
| **Query** | `time() - last_over_time(pbs_backup_timestamp[1h])/1000` |
|
||
| **Alert condition** | `A is above 86400` |
|
||
| **Evaluation group** | `Backup alerts` (создать новый) |
|
||
| **Evaluation interval** | `1m` |
|
||
| **Pending period** | `1m` |
|
||
|
||
**3. Annotations → Description:**
|
||
|
||
```text
|
||
{{ .Value }}
|
||
```
|
||
|
||
*(Python-скрипт сам превратит секунды в "11 ч 35 мин")*
|
||
|
||
**4. Notifications:**
|
||
- Выбери Contact Point: `Telegram Relay`
|
||
|
||
**5. Сохрани правило**
|
||
|
||
---
|
||
|
||
### 🔧 Шаг 4: Проверка
|
||
|
||
**1. Проверка реле:**
|
||
|
||
```bash
|
||
# Логи реле
|
||
journalctl -u telegram-relay -f
|
||
|
||
# Тестовый запрос
|
||
curl -X POST http://127.0.0.1:8085 \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"text":"✅ Grafana relay test"}'
|
||
```
|
||
|
||
**2. Проверка XRay:**
|
||
|
||
```bash
|
||
# Статус XRay
|
||
systemctl status xray-client
|
||
|
||
# Тест соединения с Telegram
|
||
curl -sx socks5://127.0.0.1:1080 \
|
||
https://api.telegram.org/bot7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4/getMe
|
||
```
|
||
|
||
**3. Тест алерта:**
|
||
- В Grafana: Contact Point → **Test**
|
||
- Должно прийти сообщение в Telegram
|
||
|
||
---
|
||
|
||
### 📝 Переменные для замены
|
||
|
||
| Переменная | Описание | Значение |
|
||
|------------|----------|----------|
|
||
| `BOT_TOKEN` | Токен Telegram бота | `7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4` |
|
||
| `CHAT_ID` | ID чата для уведомлений | `292909723` |
|
||
| `PROXY` | SOCKS5 прокси | `socks5://127.0.0.1:1080` |
|
||
| `PORT` | Порт реле | `8085` |
|
||
|
||
---
|
||
|
||
### 📁 Файлы конфигурации
|
||
|
||
| Файл | Назначение |
|
||
|------|------------|
|
||
| `/usr/local/bin/telegram-relay.py` | Python-скрипт реле |
|
||
| `/etc/systemd/system/telegram-relay.service` | systemd-юнит |
|
||
| `/etc/grafana/provisioning/alerting/` | Конфиги алертов Grafana |
|
||
|
||
---
|
||
|
||
### 🔍 Troubleshooting
|
||
|
||
| Проблема | Решение |
|
||
|----------|---------|
|
||
| Не приходит сообщение | `journalctl -u telegram-relay -n 20` |
|
||
| Ошибка 400 от Telegram | Убери `parse_mode` или экранируй спецсимволы |
|
||
| Реле не слушает порт | `systemctl restart telegram-relay` |
|
||
| XRay не подключается | `systemctl status xray-client` |
|
||
| Сообщение обрезается | Скрипт разбивает на части по 5 бэкапов — это норма |
|
||
| Grafana не шлёт | Проверь что Alert Rule в состоянии "Firing" |
|
||
|
||
---
|
||
|
||
### 🎨 Пример уведомления
|
||
|
||
```text
|
||
🚨 Backup Alert
|
||
|
||
📦 ct-201
|
||
⏱ Последний бэкап: 11 ч 35 мин назад
|
||
Status: 🔥 firing
|
||
|
||
📦 ct-202
|
||
⏱ Последний бэкап: 11 ч 34 мин назад
|
||
Status: 🔥 firing
|
||
|
||
💻 vm-205
|
||
⏱ Последний бэкап: 11 ч 30 мин назад
|
||
Status: ✅ resolved
|
||
```
|
||
|
||
**Эмодзи:**
|
||
- 💻 — виртуальные машины (vm-*)
|
||
- 📦 — LXC контейнеры (ct-*)
|
||
- 🔥 — статус firing (проблема)
|
||
- ✅ — статус resolved (всё ок)
|
||
|
||
---
|
||
|
||
### 🔄 Обновление скрипта
|
||
|
||
```bash
|
||
# Останови сервис
|
||
systemctl stop telegram-relay
|
||
|
||
# Отредактируй скрипт
|
||
nano /usr/local/bin/telegram-relay.py
|
||
|
||
# Запусти снова
|
||
systemctl start telegram-relay
|
||
|
||
# Проверь статус
|
||
systemctl status telegram-relay
|
||
```
|
||
---
|
||
|
||
## Безопасность
|
||
|
||
### Аутентификация
|
||
- **PBS пользователи**: `root@pam`, `pve@pbs`
|
||
- **API токены**: для автоматизации (без паролей)
|
||
- **Fingerprint**: SHA-256 отпечаток сертификата
|
||
|
||
### Сетевая безопасность
|
||
- **Порт**: 8007 (TCP)
|
||
- **Шифрование**: TLS 1.3
|
||
- **Доступ**: только из локальной сети (192.168.1.0/24)
|
||
|
||
### Рекомендации
|
||
- ✅ Включить **2FA** для `root@pam`
|
||
- ✅ Создать **отдельного пользователя** для бэкапов (не root)
|
||
- ✅ Настроить **firewall** на PBS сервере
|
||
- ✅ Регулярно **обновлять** систему (unattended-upgrades)
|
||
|
||
---
|
||
|
||
## Планы на будущее
|
||
|
||
### 🔵 Краткосрочные (1-2 месяца)
|
||
- [x] Настроить **уведомления** (Telegram) о статусе бэкапов
|
||
- [ ] Включить **2FA** для доступа к PBS
|
||
- [ ] Настроить **репликацию** в облако (Backblaze B2 / Wasabi)
|
||
- [ ] Провести **тестовое восстановление** всех критичных ВМ
|
||
|
||
### 🔵 Долгосрочные (3-6 месяцев)
|
||
- [ ] Добавить **второй PBS** для репликации
|
||
- [ ] Настроить **оффсайт-копии** (внешний диск + облако)
|
||
- [ ] Внедрить **шифрование** бэкапов на стороне клиента
|
||
- [ ] Автоматизировать **тестирование восстановления**
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Бэкап не запускается
|
||
```bash
|
||
# Проверить хранилище
|
||
pvesm status | grep olimpbkp
|
||
|
||
# Проверить подключение к PBS
|
||
ping 192.168.1.199
|
||
nc -zv 192.168.1.199 8007
|
||
|
||
# Переподключить хранилище
|
||
pvesm remove olimpbkp
|
||
pvesm add proxmox-backup olimpbkp \
|
||
--server 192.168.1.199 \
|
||
--datastore olimpbkp \
|
||
--username pve@pbs
|
||
```
|
||
|
||
### Закончилось место на PBS
|
||
```bash
|
||
# Проверить занятое место
|
||
proxmox-backup-client datastore stats olimpbkp
|
||
|
||
# Запустить GC
|
||
proxmox-backup-manager garbage-collection start olimpbkp
|
||
|
||
# Удалить старые бэкапы вручную
|
||
proxmox-backup-client prune \
|
||
--repository root@pam@192.168.1.199:8007:olimpbkp \
|
||
--max-daily 7 --max-weekly 2
|
||
```
|
||
|
||
### Ошибка аутентификации PBS
|
||
```bash
|
||
# Сгенерировать новый токен
|
||
proxmox-backup-manager user generate-token pve@pbs backup-token
|
||
|
||
# Обновить в Proxmox VE
|
||
pvesm update olimpbkp --password <новый_токен>
|
||
```
|
||
|
||
### Проблемы с уведомлениями в Telegram
|
||
| Проблема | Решение |
|
||
|----------|---------|
|
||
| Не приходят уведомления | Проверить `curl -s https://api.telegram.org/bot<TOKEN>/getMe` |
|
||
| Ошибка iptables | Перезапустить `/usr/local/bin/telegram-proxy.sh` |
|
||
| XRay не запускается | `journalctl -u xray-client -n 20 --no-pager` |
|
||
| Дубликат уведомлений | Удалить `/var/run/pbs-notify-lastline` и пересоздать |
|
||
|
||
---
|
||
|
||
## Полезные команды
|
||
|
||
### На Proxmox VE
|
||
```bash
|
||
# Список хранилищ
|
||
pvesm list
|
||
|
||
# Статус бэкап хранилища
|
||
pvesm status olimpbkp
|
||
|
||
# Запустить бэкап
|
||
vzdump <vmid> --storage olimpbkp --mode snapshot
|
||
|
||
# Восстановить
|
||
pct restore <new-vmid> olimpbkp:backup/ct/<ctid>/<timestamp>
|
||
qm restore <new-vmid> olimpbkp:backup/vm/<vmid>/<timestamp>
|
||
```
|
||
|
||
### На PBS
|
||
```bash
|
||
# Список datastore
|
||
proxmox-backup-manager datastore list
|
||
|
||
# Статистика
|
||
proxmox-backup-client datastore stats olimpbkp
|
||
|
||
# Список бэкапов
|
||
proxmox-backup-client list --repository root@pam@192.168.1.199:8007:olimpbkp
|
||
|
||
# Информация о бэкапе
|
||
proxmox-backup-client show-backup ct/201 --repository root@pam@192.168.1.199:8007:olimpbkp
|
||
|
||
# Проверка целостности
|
||
proxmox-backup-manager verify olimpbkp
|
||
|
||
# Garbage collection
|
||
proxmox-backup-manager garbage-collection start olimpbkp
|
||
```
|
||
|
||
---
|
||
|
||
## Метрики и отчётность
|
||
|
||
### Ежедневная проверка
|
||
```bash
|
||
# Размер бэкапов за сегодня
|
||
du -sh /var/lib/proxmox-backup/olimpbkp/data/*
|
||
|
||
# Количество бэкапов
|
||
proxmox-backup-client list --repository root@pam@192.168.1.199:8007:olimpbkp | wc -l
|
||
|
||
# Свободное место
|
||
df -h /var/lib/proxmox-backup/olimpbkp
|
||
```
|
||
|
||
### Еженедельный отчёт
|
||
```bash
|
||
# Дедупликация
|
||
proxmox-backup-client datastore stats olimpbkp | grep "Deduplication"
|
||
|
||
# Прирост за неделю
|
||
# Сравнить размер datastore с прошлой неделей
|
||
```
|
||
|
||
---
|
||
|
||
**Связанные разделы:**
|
||
- [Гипервизор Proxmox](02-hypervisor.md)
|
||
- [Виртуальные машины и LXC](03-vms-lxcs.md)
|
||
- [Безопасность](10-security.md)
|
||
- [Мониторинг и логирование](08-monitoring.md) |