Автоматизация резервного копирования и восстановления данных сокращает рутинные задачи и минимизирует риск человеческой ошибки. В этом руководстве вы получите готовые к использованию скрипты на Python и Bash, которые мониторят целостность файловых систем, создают снапшоты ZFS и Btrfs, интегрируются с системами мониторинга и управления инцидентами. Ключевой элемент - автоматизированный тестовый стенд для регулярной проверки процедур восстановления, который подтверждает работоспособность вашего плана аварийного восстановления до реального инцидента.
Базовые скрипты для автоматизации: Python для мониторинга, Bash для снапшотов
Этот блок содержит два ключевых, полностью готовых скрипта с детальными комментариями. Акцент сделан на безопасность и возможность быстрой адаптации. Обратите внимание на типичные ошибки: проверяйте пути, права доступа и переменные среды перед запуском в production.
Python-скрипт для мониторинга целостности файловых систем и критических каталогов
Скрипт проверяет состояние смонтированных файловых систем (ZFS, Btrfs, ext4), отслеживает заполнение дисков, вычисляет хеши SHA-256 критических конфигурационных файлов. Логика оповещения срабатывает при превышении порога использования диска в 90% или при изменении хеша любого отслеживаемого файла, например /etc/fstab или /etc/crontab.
#!/usr/bin/env python3
"""
Мониторинг целостности файловых систем и критических файлов.
Отправляет предупреждение при высокой утилизации диска или изменении файлов.
"""
import os
import hashlib
import subprocess
import json
import sys
# Конфигурация
CRITICAL_PATHS = ['/etc/fstab', '/etc/ssh/sshd_config', '/etc/crontab']
DISK_USAGE_THRESHOLD = 90 # Процент
HASH_STORE = '/var/lib/fs-monitor/hashes.json'
def get_filesystem_usage():
"""Возвращает использование файловых систем в процентах."""
result = subprocess.run(['df', '-h', '--output=target,pcent'], capture_output=True, text=True)
usage = {}
for line in result.stdout.strip().split('\n')[1:]:
mount, pct = line.split()
usage[mount] = int(pct.rstrip('%'))
return usage
def calculate_file_hash(filepath):
"""Вычисляет SHA-256 хеш файла."""
sha256_hash = hashlib.sha256()
try:
with open(filepath, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
except FileNotFoundError:
return None
def load_stored_hashes():
"""Загружает ранее сохраненные хеши."""
try:
with open(HASH_STORE, 'r') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return {}
def save_hashes(hashes):
"""Сохраняет текущие хеши."""
os.makedirs(os.path.dirname(HASH_STORE), exist_ok=True)
with open(HASH_STORE, 'w') as f:
json.dump(hashes, f, indent=2)
def main():
alerts = []
# Проверка использования диска
fs_usage = get_filesystem_usage()
for mount, usage in fs_usage.items():
if usage > DISK_USAGE_THRESHOLD:
alerts.append(f"ALERT: Файловая система {mount} заполнена на {usage}%")
# Проверка целостности файлов
stored_hashes = load_stored_hashes()
current_hashes = {}
for path in CRITICAL_PATHS:
current_hash = calculate_file_hash(path)
if current_hash is None:
continue
current_hashes[path] = current_hash
if path in stored_hashes and stored_hashes[path] != current_hash:
alerts.append(f"ALERT: Изменен файл {path}")
# Сохраняем новые хеши при первом запуске или если файл новый
save_hashes(current_hashes)
# Вывод результатов для интеграции (например, в Zabbix или лог)
if alerts:
print("\n".join(alerts))
sys.exit(1) # Код ошибки для триггера алерта
else:
print("STATUS: OK")
sys.exit(0)
if __name__ == "__main__":
main()
Для интеграции добавьте вызов скрипта в cron: */15 * * * * /usr/local/bin/fs_monitor.py. При срабатывании алерта скрипт возвращает код 1, что можно использовать для отправки уведомления.
Bash-скрипт для автоматического создания снапшотов ZFS/Btrfs по расписанию и при изменениях
Скрипт принимает параметры: целевой ZFS pool или Btrfs subvolume и политику хранения. Он создает снапшоты с метками времени и включает дополнительную логику для активации через inotifywait при обнаружении изменений в целевых директориях.
#!/bin/bash
# Автоматическое создание и ротация снапшотов ZFS/Btrfs
set -euo pipefail
# Конфигурация (настройте под свою среду)
TARGET_POOL="tank" # Имя ZFS pool или путь к Btrfs subvolume
SNAPSHOT_PREFIX="auto"
RETENTION_DAILY=7 # Хранить ежедневные снапшоты 7 дней
RETENTION_WEEKLY=4 # Хранить еженедельные снапшоты 4 недели
MONITOR_DIR="/data" # Директория для мониторинга изменений (опционально)
# Функция создания снапшота ZFS
create_zfs_snapshot() {
local snapshot_name="${TARGET_POOL}@${SNAPSHOT_PREFIX}-$(date +%Y%m%d-%H%M%S)"
if zfs list "$snapshot_name" >/dev/null 2>&1; then
echo "Снапшот $snapshot_name уже существует."
return 1
fi
zfs snapshot "$snapshot_name"
echo "Создан ZFS снапшот: $snapshot_name"
log_snapshot "$snapshot_name"
}
# Функция создания снапшота Btrfs
create_btrfs_snapshot() {
local snapshot_dir="/snapshots/$(basename "$TARGET_POOL")"
local snapshot_name="${SNAPSHOT_PREFIX}-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$snapshot_dir"
btrfs subvolume snapshot -r "$TARGET_POOL" "${snapshot_dir}/${snapshot_name}"
echo "Создан Btrfs снапшот: ${snapshot_dir}/${snapshot_name}"
log_snapshot "${snapshot_dir}/${snapshot_name}"
}
# Функция ротации снапшотов ZFS
rotate_zfs_snapshots() {
# Удаляем ежедневные снапшоты старше RETENTION_DAILY дней
zfs list -t snapshot -o name -H | grep "@${SNAPSHOT_PREFIX}-" | while read -r snap; do
local snap_date
snap_date=$(echo "$snap" | grep -oE '[0-9]{8}' | head -1)
if [[ -n "$snap_date" ]]; then
local days_old
days_old=$(( ( $(date +%s) - $(date -d "$snap_date" +%s) ) / 86400 ))
if [[ $days_old -gt $RETENTION_DAILY ]]; then
zfs destroy "$snap"
echo "Удален устаревший снапшот: $snap"
fi
fi
done
}
# Логирование (пример)
log_snapshot() {
logger -t "auto-snapshot" "Создан снапшот: $1"
}
# Основная логика
case "${1:-schedule}" in
"schedule")
# Режим по расписанию (вызов из cron)
if [[ "$TARGET_POOL" == /* ]]; then
create_btrfs_snapshot
else
create_zfs_snapshot
rotate_zfs_snapshots
fi
;;
"watch")
# Режим мониторинга изменений (требует inotify-tools)
echo "Мониторинг изменений в $MONITOR_DIR..."
inotifywait -m -r -e modify,create,delete "$MONITOR_DIR" | while read -r path action file; do
echo "Изменение в $path: $action $file. Создание снапшота..."
if [[ "$TARGET_POOL" == /* ]]; then
create_btrfs_snapshot
else
create_zfs_snapshot
fi
done
;;
*)
echo "Использование: $0 [schedule|watch]"
exit 1
;;
esac
Для работы по расписанию добавьте в crontab: 0 2 * * * /usr/local/bin/auto_snapshot.sh schedule. Для мониторинга изменений в реальном времени запустите скрипт как службу: systemctl enable --now auto-snapshot-watch.service (требует установленного inotify-tools).
Интеграция с экосистемой: Prometheus, Zabbix, Jira и ServiceNow
Эти практические примеры показывают, как экспортировать метрики из скриптов в форматы, понятные Prometheus и Zabbix, а также создавать автоматические тикеты в Jira и ServiceNow через REST API при срабатывании алертов, например, при неудачном создании снапшота.
Экспорт метрик в Prometheus и настройка алертов в Alertmanager
Модифицируйте Python-скрипт мониторинга для запуска как HTTP-сервера на порту 8000, который отдает метрики в формате Prometheus.
#!/usr/bin/env python3
"""
Prometheus exporter для метрик резервного копирования.
Запуск: python3 backup_exporter.py
Метрики доступны по адресу http://localhost:8000/metrics
"""
from http.server import HTTPServer, BaseHTTPRequestHandler
import subprocess
import time
class MetricsHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/metrics':
# Пример сбора метрик
metrics = []
# Метрика времени последнего успешного снапшота (эмуляция)
metrics.append('backup_last_success_timestamp{type="zfs_snapshot"} 1746489600')
# Метрика использования файловой системы
try:
df_output = subprocess.check_output(['df', '/data', '--output=pcent'], text=True)
usage = int(df_output.split('\n')[1].rstrip('%'))
metrics.append(f'filesystem_usage_percent{{mount="/data"}} {usage}')
except subprocess.CalledProcessError:
metrics.append('filesystem_usage_percent{mount="/data"} -1')
# Метрика количества снапшотов (эмуляция)
metrics.append('snapshot_count{pool="tank"} 42')
self.send_response(200)
self.send_header('Content-type', 'text/plain; version=0.0.4')
self.end_headers()
self.wfile.write('\n'.join(metrics).encode())
else:
self.send_response(404)
self.end_headers()
if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', 8000), MetricsHandler)
print('Prometheus exporter запущен на порту 8000')
server.serve_forever()
Добавьте в конфигурацию Prometheus scrape-конфиг:
scrape_configs:
- job_name: 'backup_metrics'
static_configs:
- targets: ['localhost:8000']
Настройте правило в Alertmanager для отправки уведомления в Slack или Email при превышении порога использования диска в 95%:
groups:
- name: backup_alerts
rules:
- alert: HighFilesystemUsage
expr: filesystem_usage_percent > 95
for: 5m
annotations:
summary: "Файловая система {{ $labels.mount }} почти заполнена"
description: "Использование составляет {{ $value }}%"
Для комплексной автоматизации других рутинных задач, таких как мониторинг логов или обновление серверов, изучите готовые примеры в руководстве по автоматизации инфраструктуры для DevOps и сисадминв.
Создание автоматических тикетов в Jira при критических событиях
Этот фрагмент кода на Python использует библиотеку jira для создания тикета при критическом событии, например, при ошибке создания снапшота.
#!/usr/bin/env python3
"""
Создание тикета в Jira при критическом событии резервного копирования.
"""
from jira import JIRA
import os
import sys
# Конфигурация (используйте переменные окружения или vault для секретов)
JIRA_SERVER = os.getenv('JIRA_URL', 'https://your-domain.atlassian.net')
JIRA_API_TOKEN = os.getenv('JIRA_API_TOKEN') # Сгенерируйте в учетной записи Atlassian
JIRA_PROJECT = 'OPS'
def create_incident_ticket(summary, description, priority='High'):
"""Создает инцидент в Jira."""
try:
jira = JIRA(server=JIRA_SERVER, token_auth=JIRA_API_TOKEN)
issue_dict = {
'project': {'key': JIRA_PROJECT},
'summary': summary,
'description': description,
'issuetype': {'name': 'Incident'},
'priority': {'name': priority},
}
new_issue = jira.create_issue(fields=issue_dict)
print(f"Создан тикет: {new_issue.key} - {summary}")
return new_issue.key
except Exception as e:
print(f"Ошибка создания тикета в Jira: {e}", file=sys.stderr)
# Резервное логирование или отправка в syslog
sys.exit(1)
if __name__ == '__main__':
# Пример вызова при ошибке в скрипте снапшотов
# Передавайте аргументы из основного скрипта
ticket_summary = "Сбой автоматического создания снапшота ZFS"
ticket_description = """
Скрипт auto_snapshot.sh завершился с ошибкой.
* Пул: tank
* Время: $(date)
* Код ошибки: 1
* Вывод: Недостаточно места в пуле.
"""
create_incident_ticket(ticket_summary, ticket_description)
Установите библиотеку: pip install jira. Для безопасного хранения API-токена используйте HashiCorp Vault или аналогичное решение. Интегрируйте вызов этой функции в блок обработки ошибок вашего основного скрипта создания снапшотов.
Тестовый стенд для регулярной проверки восстановления: убедитесь, что backup работает
Это детальное руководство по развертыванию изолированной среды в Docker, которая имитирует production. Скрипты автоматически разворачивают тестовые данные, восстанавливают их из актуальных снапшотов и проводят валидацию через checksum и проверку доступности сервисов.
Архитектура стенда: Docker Compose для изоляции и повторяемости
Пример docker-compose.yml определяет сервисы: база данных PostgreSQL, простое веб-приложение на Python и S3-совместимое хранилище MinIO для эмуляции облачного бэкапа.
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD: testpassword123
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser"]
interval: 10s
timeout: 5s
retries: 5
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
volumes:
- minio_data:/data
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web Console
webapp:
build: ./webapp
ports:
- "8080:8080"
environment:
DATABASE_URL: "postgresql://appuser:testpassword123@postgres/appdb"
S3_ENDPOINT: "http://minio:9000"
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_started
volumes:
postgres_data:
minio_data:
Скрипт инициализации ./scripts/init-db.sql заполняет БД тестовыми данными:
-- Создание тестовой таблицы и данных
CREATE TABLE IF NOT EXISTS important_data (
id SERIAL PRIMARY KEY,
data TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO important_data (data) VALUES
('Тестовые данные для проверки восстановления #1'),
('Тестовые данные для проверки восстановления #2'),
('Тестовые данные для проверки восстановления #3');
-- Создание эталонного снапшота (логически)
COMMENT ON TABLE important_data IS 'Эталонные данные для теста восстановления от 2026-05-05';
Скрипт автоматического тестирования процедуры восстановления
Этот Bash-скрипт симулирует сбой, запускает процедуру восстановления из последнего валидного бэкапа и проверяет целостность данных. Его можно интегрировать в CI/CD, например, в GitLab CI.
#!/bin/bash
# Скрипт тестирования восстановления из резервной копии
set -euxo pipefail
LOG_FILE="/var/log/dr-test.log"
METRICS_FILE="/tmp/dr-test-metrics.prom"
# 1. Остановка сервисов и симуляция "сбоя"
echo "$(date) - Шаг 1: Симуляция сбоя" | tee -a "$LOG_FILE"
docker compose stop webapp postgres
docker compose exec -T postgres psql -U appuser -d appdb -c "DROP TABLE important_data;"
echo "Таблица удалена." | tee -a "$LOG_FILE"
# 2. Восстановление из бэкапа
# Предполагаем, что у нас есть актуальный дамп БД в MinIO/S3
echo "$(date) - Шаг 2: Восстановление данных" | tee -a "$LOG_FILE"
BACKUP_FILE="appdb-backup-$(date +%Y%m%d).sql"
# Эмуляция загрузки из MinIO (используем mc клиент)
docker compose exec -T minio mc cp local/backups/$BACKUP_FILE /tmp/restore.sql
# Восстановление в БД
docker compose exec -T postgres psql -U appuser -d appdb -f /tmp/restore.sql
# 3. Запуск сервисов
echo "$(date) - Шаг 3: Запуск восстановленных сервисов" | tee -a "$LOG_FILE"
docker compose start postgres
sleep 10 # Ожидаем готовности БД
docker compose start webapp
# 4. Валидация восстановления
echo "$(date) - Шаг 4: Валидация данных" | tee -a "$LOG_FILE"
ROW_COUNT=$(docker compose exec -T postgres psql -U appuser -d appdb -t -c "SELECT COUNT(*) FROM important_data;" | tr -d '[:space:]')
EXPECTED_COUNT=3
if [[ "$ROW_COUNT" -eq "$EXPECTED_COUNT" ]]; then
TEST_STATUS="success"
echo "ТЕСТ ПРОЙДЕН: Восстановлено $ROW_COUNT строк (ожидалось $EXPECTED_COUNT)." | tee -a "$LOG_FILE"
else
TEST_STATUS="failure"
echo "ТЕСТ НЕ ПРОЙДЕН: Восстановлено $ROW_COUNT строк, ожидалось $EXPECTED_COUNT." | tee -a "$LOG_FILE"
fi
# 5. Генерация отчета и метрик для Prometheus
echo "$(date) - Шаг 5: Генерация отчета" | tee -a "$LOG_FILE"
cat > "$METRICS_FILE" << EOF
# TYPE dr_test_last_run_timestamp gauge
dr_test_last_run_timestamp $(date +%s)
# TYPE dr_test_success gauge
dr_test_success{environment="test"} $( [[ $TEST_STATUS == "success" ]] && echo 1 || echo 0 )
EOF
# Отправка метрики (пример через pushgateway или curl)
if [[ -f "$METRICS_FILE" ]]; then
curl -X POST --data-binary @"$METRICS_FILE" http://prometheus:9091/metrics/job/dr_test/instance/test-stand
fi
# Выход с кодом результата
if [[ "$TEST_STATUS" == "success" ]]; then
exit 0
else
exit 1
fi
Настройте еженедельный запуск теста через cron: 0 3 * * 6 /usr/local/bin/test_disaster_recovery.sh. Результаты теста появятся в Prometheus как метрика dr_test_success.
Адаптация под вашу инфраструктуру: от домашнего TrueNAS до корпоративного кластера
Эта сравнительная таблица и конкретные примеры помогут модифицировать скрипты для разных сред: TrueNAS Scale на ZFS, сервера на Btrfs или Kubernetes-кластера. Основное внимание уделено безопасности: использование vault для хранения секретов и ограничение прав sudo для скриптов.
Настройка для TrueNAS Scale и других NAS-решений
В TrueNAS Scale скрипты можно запускать через задачи cron в веб-интерфейсе или через CLI. Основные особенности работы с ZFS пулами в TrueNAS связаны с путями и правами.
- Через веб-интерфейс TrueNAS: Перейдите в Tasks → Cron Jobs. Добавьте новую задачу.
- Command:
python3 /mnt/tank/scripts/fs_monitor.py - User:
root - Schedule:
*/15 * * * *
- Command:
- Через CLI (SSH): Отредактируйте crontab для root:
crontab -eи добавьте строку.
Пример модификации скрипта снапшотов для TrueNAS, где пул называется tank, а данные находятся в dataset tank/data:
# В скрипте auto_snapshot.sh измените переменную:
TARGET_POOL="tank/data"
# Для создания снапшота используйте:
zfs snapshot "${TARGET_POOL}@${SNAPSHOT_PREFIX}-$(date +%Y%m%d-%H%M%S)"
Для настройки репликации между экземплярами TrueNAS и отработки аварийного восстановления изучите детальное руководство по репликации данных в TrueNAS.
Безопасность и надежность: vault для секретов, ограничение прав, логирование
Паттерны работы с паролями и API-ключами минимизируют риски. Вместо хранения секретов в скриптах используйте HashiCorp Vault или ansible-vault.
Пример использования HashiCorp Vault для получения JIRA API токена:
#!/bin/bash
# Получение секрета из Vault перед запуском скрипта
JIRA_API_TOKEN=$(curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
https://vault.example.com/v1/secret/data/jira | jq -r '.data.data.api_token')
export JIRA_API_TOKEN
python3 /usr/local/bin/create_jira_ticket.py "$@"
Ограничение прав sudo в /etc/sudoers.d/backup-scripts:
# Разрешить пользователю backup выполнять только конкретные команды ZFS
backup ALL=(ALL) NOPASSWD: /usr/sbin/zfs snapshot tank/data@*, /usr/sbin/zfs destroy tank/data@*
# Запретить все остальное
backup ALL=(ALL) !ALL
Централизованное логирование в rsyslog: Добавьте в /etc/rsyslog.d/backup.conf:
# Перенаправление логов скриптов в отдельный файл
local0.* /var/log/backup-scripts.log
# И отправка на центральный лог-сервер (если есть)
*.* @logserver.example.com:514
В скриптах используйте logger для записи событий: logger -p local0.notice -t auto_snapshot "Создан снапшот $snapshot_name".
Для автоматизации других рутинных операций, таких как передача файлов, могут пригодиться готовые скрипты для передачи файлов по FTP, FTPS и SFTP.
Систематизация экспертизы и создание единой базы знаний для команды - следующий шаг после настройки автоматизации. Это сокращает время восстановления (MTTR) и ускоряет онбординг новых сотрудников. Подробный план внедрения изложен в статье о том, как систематизировать IT-экспертизу в 2026 году.
Для DevOps-команд, которые интегрируют безопасность в CI/CD, будет полезно руководство по аудиту безопасности для DevOps с готовыми скриптами и методиками.
Для экспериментов с интеграцией различных AI-моделей в ваши инструменты автоматизации рассмотрите сервис AiTunnel. Это агрегатор API для более 200 моделей, включая GPT, Gemini и Claude, с единым интерфейсом и оплатой в рублях.