Автоматизация резервного копирования и восстановления: готовые скрипты Python и Bash для DevOps | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

Автоматизация резервного копирования и восстановления: готовые скрипты Python и Bash для DevOps

05 мая 2026 12 мин. чтения
Содержание статьи

Автоматизация резервного копирования и восстановления данных сокращает рутинные задачи и минимизирует риск человеческой ошибки. В этом руководстве вы получите готовые к использованию скрипты на 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 связаны с путями и правами.

  1. Через веб-интерфейс TrueNAS: Перейдите в Tasks → Cron Jobs. Добавьте новую задачу.
    • Command: python3 /mnt/tank/scripts/fs_monitor.py
    • User: root
    • Schedule: */15 * * * *
  2. Через 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, с единым интерфейсом и оплатой в рублях.

Поделиться:
Сохранить гайд? В закладки браузера