trx
Published on 2024-08-31 / 68 Visits
0

Restic 全场景备份操作手册

一、环境变量系统配置

1.1 通用环境变量配置

# ~/.restic/env.conf - 通用配置
# ============================================

# RESTIC 核心配置

export RESTIC_PASSWORD_FILE="$HOME/.restic/password"

export RESTIC_CACHE_DIR="$HOME/.cache/restic"

export RESTIC_PROGRESS_FPS=0.1

export RESTIC_COMPRESSION="auto"  # auto/max/off

# 日志和调试

export RESTIC_VERBOSE=1

export RESTIC_QUIET=0

export RESTIC_LOG_FILE="/var/log/restic/$(date +%Y%m%d).log"

export RESTIC_DEBUG=0

# 网络配置

export RESTIC_CONNECT_TIMEOUT=30

export RESTIC_READ_TIMEOUT=300

export RESTIC_RETRY_LOCK=30m

export RESTIC_TLS_CLIENT_CERT=""

export RESTIC_TLS_CLIENT_KEY=""

# 缓存优化

export RESTIC_CACHE_MAX_AGE=30

export RESTIC_PACK_SIZE=16

1.2 密码管理方案

# 方案1:密码文件(推荐)
echo "your-strong-password" > ~/.restic/password

chmod 600 ~/.restic/password

export RESTIC_PASSWORD_FILE="$HOME/.restic/password"

# 方案2:密码管理器集成

# 使用 pass (https://www.passwordstore.org/)

export RESTIC_PASSWORD_COMMAND="pass restic/main"

# 方案3:使用 gpg 加密的密码文件

gpg -c -o ~/.restic/password.gpg

export RESTIC_PASSWORD_COMMAND="gpg --decrypt ~/.restic/password.gpg"

# 方案4:使用系统密钥环

# macOS Keychain

export RESTIC_PASSWORD_COMMAND="security find-generic-password -a ${USER} -s restic-repo -w"

# Linux Secret Service (GNOME Keyring)

export RESTIC_PASSWORD_COMMAND="secret-tool lookup application restic repository main"

二、各类远程存储库配置

2.1 S3 兼容存储(AWS S3, MinIO, Wasabi, Backblaze B2, etc)

# ============================================
# AWS S3 配置

# ============================================

export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"

export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

export AWS_DEFAULT_REGION="us-east-1"

export RESTIC_REPOSITORY="s3:s3.amazonaws.com/bucket-name"

# 使用路径格式

export RESTIC_REPOSITORY="s3:s3.amazonaws.com/bucket-name/restic"

# ============================================

# MinIO 配置(自建 S3 兼容)

# ============================================

export AWS_ACCESS_KEY_ID="r8QTNLagfwLXuvUvIDfT"

export AWS_SECRET_ACCESS_KEY="N079sUCscGm6GYQa3BOM3t3XkSIxCu2E7w6KcJG6"

export AWS_ENDPOINT="http://www.trxgrwz.cn:9000"

export AWS_ENDPOINT_URL="http://www.trxgrwz.cn:9000"

export AWS_REGION="us-east-1"  # MinIO 默认区域

export RESTIC_REPOSITORY="s3:http://www.trxgrwz.cn:9000/restic_test"

# 高级 MinIO 配置

export AWS_MAX_RETRIES=10

export AWS_TIMEOUT=300

export RESTIC_REPOSITORY="s3:http://www.trxgrwz.cn:9000/bucket-name/restic?prefix=backups"

# ============================================

# Backblaze B2 配置

# ============================================

export B2_ACCOUNT_ID="00123456789abcdef"

export B2_ACCOUNT_KEY="K00123456789abcdefghijklmnopqrstu"

export RESTIC_REPOSITORY="b2:bucket-name:restic"

# 或使用应用密钥

export B2_APPLICATION_KEY_ID="00123456789abcdef"

export B2_APPLICATION_KEY="K00123456789abcdefghijklmnopqrstu"

# ============================================

# Wasabi 配置

# ============================================

export AWS_ACCESS_KEY_ID="YOURACCESSKEY"

export AWS_SECRET_ACCESS_KEY="YOURSECRETKEY"

export AWS_DEFAULT_REGION="us-east-1"

export AWS_ENDPOINT="https://s3.wasabisys.com"

export RESTIC_REPOSITORY="s3:s3.wasabisys.com/bucket-name/restic"

# ============================================

# DigitalOcean Spaces 配置

# ============================================

export AWS_ACCESS_KEY_ID="DOABCDEFGHIJKLMNOPQR"

export AWS_SECRET_ACCESS_KEY="abcdefghijklmnopqrstuvwxyz0123456789ABCD"

export AWS_DEFAULT_REGION="nyc3"

export AWS_ENDPOINT="https://nyc3.digitaloceanspaces.com"

export RESTIC_REPOSITORY="s3:nyc3.digitaloceanspaces.com/bucket-name/restic"

# ============================================

# 阿里云 OSS 配置

# ============================================

export AWS_ACCESS_KEY_ID="LTAI5tABCDEFGHIJKLMNOP"

export AWS_SECRET_ACCESS_KEY="abcdefghijklmnopqrstuvwxyz0123456789"

export AWS_DEFAULT_REGION="oss-cn-hangzhou"

export AWS_ENDPOINT="https://oss-cn-hangzhou.aliyuncs.com"

export RESTIC_REPOSITORY="s3:https://oss-cn-hangzhou.aliyuncs.com/bucket-name/restic"

# ============================================

# 腾讯云 COS 配置

# ============================================

export AWS_ACCESS_KEY_ID="AKIDABCDEFGHIJKLMNOPQR"

export AWS_SECRET_ACCESS_KEY="abcdefghijklmnopqrstuvwxyz0123456789"

export AWS_DEFAULT_REGION="ap-beijing"

export AWS_ENDPOINT="https://cos.ap-beijing.myqcloud.com"

export RESTIC_REPOSITORY="s3:https://cos.ap-beijing.myqcloud.com/bucket-name/restic"

# ============================================

# Google Cloud Storage (GCS)

# ============================================

# 方法1:使用服务账号密钥文件

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"

export RESTIC_REPOSITORY="gs:bucket-name:/restic"

# 方法2:使用访问令牌

export GOOGLE_ACCESS_TOKEN="ya29.c.abcdefghijklmnopqrstuvwxyz"

export RESTIC_REPOSITORY="gs:bucket-name:/restic"

# 方法3:使用默认应用凭证

gcloud auth application-default login

export RESTIC_REPOSITORY="gs:bucket-name:/restic"

# ============================================

# Azure Blob Storage

# ============================================

# 方法1:使用连接字符串

export AZURE_ACCOUNT_NAME="accountname"

export AZURE_ACCOUNT_KEY="accountkey"

export RESTIC_REPOSITORY="azure:container-name:/restic"

# 方法2:使用 SAS 令牌

export AZURE_ACCOUNT_NAME="accountname"

export AZURE_ACCOUNT_SAS_TOKEN="?sv=2019-12-12&ss=bfqt&srt=sco&sp=rwdlacupx&se=..."

export RESTIC_REPOSITORY="azure:container-name:/restic"

# 方法3:使用服务主体

export AZURE_ACCOUNT_NAME="accountname"

export AZURE_CLIENT_ID="clientid"

export AZURE_CLIENT_SECRET="clientsecret"

export AZURE_TENANT_ID="tenantid"

export RESTIC_REPOSITORY="azure:container-name:/restic"

# ============================================

# SFTP/SSH 服务器

# ============================================

# 方法1:使用密码认证

export RESTIC_REPOSITORY="sftp:user@host:/path/to/restic"

# 然后在命令行输入密码,或使用 SSH_ASKPASS

# 方法2:使用密钥认证

export RESTIC_REPOSITORY="sftp:user@host:/path/to/restic"

export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"

# 或指定密钥文件

export RESTIC_REPOSITORY="sftp:user@host:/path/to/restic?ssh_command=ssh -i /path/to/key"

# 方法3:高级 SSH 选项

export RESTIC_REPOSITORY="sftp:user@host:22/path/to/restic?ssh_command=ssh -o ConnectTimeout=30 -o ServerAliveInterval=60"

# ============================================

# REST 服务器

# ============================================

# 使用 rest-server (https://github.com/restic/rest-server)

export RESTIC_REPOSITORY="rest:http://user:pass@host:8000/restic"

export RESTIC_REPOSITORY="rest:https://host:8000/restic"

# 带认证的 REST 服务器

export RESTIC_REPOSITORY="rest:https://user:pass@host:8000/restic"

export RESTIC_REPOSITORY="rest:https://host:8000/restic?tls_client_cert=/path/cert.pem&tls_client_key=/path/key.pem"

# ============================================

# rclone 远程(支持 40+ 云服务)

# ============================================

# 首先配置 rclone: rclone config

export RESTIC_REPOSITORY="rclone:remote:bucket/restic"

# 高级 rclone 选项

export RESTIC_REPOSITORY="rclone:remote:bucket/restic?rclone_args=--buffer-size 32M --transfers 4"

# ============================================

# 本地文件系统(跨平台)

# ============================================

# Unix/Linux/macOS

export RESTIC_REPOSITORY="/mnt/backup/restic"

export RESTIC_REPOSITORY="/Volumes/Backup/restic"

# Windows

export RESTIC_REPOSITORY="C:\\Backup\\restic"

export RESTIC_REPOSITORY="\\\\server\\share\\restic"

# ============================================

# WebDAV

# ============================================

# 基础认证

export RESTIC_REPOSITORY="webdav:https://user:pass@host/webdav/path"

# 使用证书

export RESTIC_REPOSITORY="webdav:https://host/webdav/path?tls_client_cert=/path/cert.pem&tls_client_key=/path/key.pem"

# ============================================

# OpenStack Swift

# ============================================

export OS_AUTH_URL="https://auth.cloud.ovh.net/v3"

export OS_USERNAME="username"

export OS_PASSWORD="password"

export OS_PROJECT_NAME="project"

export OS_REGION_NAME="region"

export RESTIC_REPOSITORY="swift:container:/restic"

2.2 存储库初始化模板

#!/bin/bash
# init-repos.sh - 多存储库初始化

init_s3_repo() {

    local bucket=$1

    local region=${2:-us-east-1}

    

    echo "初始化 S3 存储库: $bucket"

    export AWS_DEFAULT_REGION="$region"

    export RESTIC_REPOSITORY="s3:s3.amazonaws.com/$bucket/restic"

    

    restic init

}

init_minio_repo() {

    local endpoint=$1

    local bucket=$2

    

    echo "初始化 MinIO 存储库: $endpoint/$bucket"

    export AWS_ENDPOINT="$endpoint"

    export RESTIC_REPOSITORY="s3:$endpoint/$bucket/restic"

    

    restic init

}

init_sftp_repo() {

    local user=$1

    local host=$2

    local path=$3

    

    echo "初始化 SFTP 存储库: $user@$host:$path"

    export RESTIC_REPOSITORY="sftp:$user@$host:$path"

    

    restic init

}

# 初始化多个存储库(冗余备份)

init_multi_repos() {

    echo "=== 初始化主存储库 (S3) ==="

    init_s3_repo "my-backup-bucket" "us-east-1"

    

    echo -e "\n=== 初始化次存储库 (MinIO) ==="

    init_minio_repo "http://minio.example.com:9000" "backup-bucket"

    

    echo -e "\n=== 初始化本地副本 (SFTP) ==="

    init_sftp_repo "backup" "nas.example.com" "/backup/restic"

}

三、TLS/SSL 安全认证配置

3.1 证书配置

# ============================================
# 使用自定义 CA 证书

# ============================================

export RESTIC_TLS_CA_CERT="/etc/ssl/certs/ca-custom.pem"

export RESTIC_REPOSITORY="s3:https://minio.example.com:9000/bucket/restic"

# 或通过环境变量传递证书

export SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"

export SSL_CERT_DIR="/etc/ssl/certs"

# ============================================

# 客户端证书认证 (mTLS)

# ============================================

# 方法1:环境变量

export RESTIC_TLS_CLIENT_CERT="/path/to/client-cert.pem"

export RESTIC_TLS_CLIENT_KEY="/path/to/client-key.pem"

export RESTIC_REPOSITORY="rest:https://server:8000/restic"

# 方法2:URL 参数

export RESTIC_REPOSITORY="rest:https://server:8000/restic?tls_client_cert=/path/cert.pem&tls_client_key=/path/key.pem"

# ============================================

# 自签名证书配置

# ============================================

# 跳过证书验证(不推荐生产环境)

export RESTIC_TLS_SKIP_VERIFY="true"

export RESTIC_REPOSITORY="s3:https://selfsigned.example.com:9000/bucket/restic"

# 添加自签名证书到信任库

cp selfsigned.crt /usr/local/share/ca-certificates/

update-ca-certificates

# ============================================

# Let's Encrypt 自动证书

# ============================================

# 使用 certbot 获取证书

certbot certonly --standalone -d backup.example.com

export RESTIC_TLS_CERT="/etc/letsencrypt/live/backup.example.com/fullchain.pem"

export RESTIC_TLS_KEY="/etc/letsencrypt/live/backup.example.com/privkey.pem"

# ============================================

# 证书自动续期脚本

# ============================================

#!/bin/bash

# renew-certs.sh

CERT_DIR="/etc/letsencrypt/live/backup.example.com"

RESTIC_CMD="systemctl restart restic-backup.service"

# 续期证书

certbot renew --quiet --post-hook "$RESTIC_CMD"

# 更新证书文件权限

chmod 600 $CERT_DIR/privkey.pem

chmod 644 $CERT_DIR/fullchain.pem

```

### 3.2 TLS 安全加固配置

```bash

# ============================================

# OpenSSL 安全配置

# ============================================

export SSL_CIPHERS="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"

export SSL_MIN_VERSION="TLSv1.2"

# ============================================

# 创建自签名证书

# ============================================

#!/bin/bash

# create-self-signed-cert.sh

DOMAIN="backup.example.com"

DAYS=3650

# 生成 CA

openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem -out ca-cert.pem \

  -days $DAYS -nodes -subj "/CN=Backup CA"

# 生成服务器证书

openssl req -newkey rsa:4096 -keyout server-key.pem -out server-req.pem \

  -nodes -subj "/CN=$DOMAIN"

openssl x509 -req -in server-req.pem -days $DAYS \

  -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \

  -out server-cert.pem -extfile <(printf "subjectAltName=DNS:$DOMAIN")

# 生成客户端证书

openssl req -newkey rsa:4096 -keyout client-key.pem -out client-req.pem \

  -nodes -subj "/CN=restic-client"

openssl x509 -req -in client-req.pem -days $DAYS \

  -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \

  -out client-cert.pem

# 创建证书包

cat server-cert.pem ca-cert.pem > server-fullchain.pem

cat client-cert.pem ca-cert.pem > client-fullchain.pem

四、备份策略和快照管理

4.1 完整的快照保留策略

# ============================================
# 基础保留策略(黄金法则)

# ============================================

restic forget \

  --keep-last 10 \          # 保留最后10个快照

  --keep-hourly 24 \        # 保留24小时内每小时1个

  --keep-daily 7 \          # 保留7天内每天1个

  --keep-weekly 4 \         # 保留4周内每周1个

  --keep-monthly 12 \       # 保留12个月内每月1个

  --keep-yearly 10 \        # 保留10年内每年1个

  --prune

# ============================================

# 多维度标签策略

# ============================================

restic forget \

  --group-by "host,tags,paths" \

  --keep-within-hourly 24h \

  --keep-within-daily 7d \

  --keep-within-weekly 30d \

  --keep-within-monthly 365d \

  --keep-tag "critical" \

  --keep-tag "quarterly" \

  --prune

# ============================================

# 分级备份策略

# ============================================

#!/bin/bash

# retention-policy.sh

# 关键数据 - 长期保留

restic forget --path "/etc" --tag "critical" \

  --keep-daily 14 \

  --keep-weekly 8 \

  --keep-monthly 24 \

  --keep-yearly 10 \

  --prune

# 应用数据 - 中期保留

restic forget --path "/var/lib" --tag "appdata" \

  --keep-daily 7 \

  --keep-weekly 4 \

  --keep-monthly 12 \

  --prune

# 临时数据 - 短期保留

restic forget --path "/tmp" --tag "temporary" \

  --keep-hourly 24 \

  --keep-daily 3 \

  --prune

# 用户数据 - 灵活保留

restic forget --path "/home" --tag "userdata" \

  --keep-last 100 \

  --keep-within 30d \

  --prune

# ============================================

# 智能时间窗口策略

# ============================================

restic forget \

  --keep-within 2h \        # 2小时内的所有快照

  --keep-within-daily 7d \  # 7天内的每日快照

  --keep-within-weekly 1m \ # 1月内的每周快照

  --keep-within-monthly 1y \# 1年内的每月快照

  --keep-within-yearly 10y \# 10年内的每年快照

  --prune

# ============================================

# 基于空间的保留策略

# ============================================

#!/bin/bash

# space-based-retention.sh

MAX_SIZE="500GB"  # 最大存储空间

CURRENT_SIZE=$(restic stats | grep "Total Size" | awk '{print $3$4}')

if [[ "$CURRENT_SIZE" > "$MAX_SIZE" ]]; then

    echo "存储空间超过 $MAX_SIZE,执行清理..."

    

    # 逐步删除最旧的快照直到满足空间要求

    while [[ "$CURRENT_SIZE" > "$MAX_SIZE" ]]; do

        OLDEST=$(restic snapshots --json | jq -r 'sort_by(.time) | first | .short_id')

        if [[ -n "$OLDEST" ]]; then

            restic forget "$OLDEST" --prune

            CURRENT_SIZE=$(restic stats | grep "Total Size" | awk '{print $3$4}')

        else

            break

        fi

    done

fi

# ============================================

# 策略组合模板

# ============================================

#!/bin/bash

# apply-retention-policies.sh

apply_policy() {

    local name=$1

    local tags=$2

    local keep_daily=$3

    local keep_weekly=$4

    local keep_monthly=$5

    local keep_yearly=$6

    

    echo "应用策略: $name"

    restic forget \

      --tag "$tags" \

      --keep-daily "$keep_daily" \

      --keep-weekly "$keep_weekly" \

      --keep-monthly "$keep_monthly" \

      --keep-yearly "$keep_yearly" \

      --prune

}

# 应用不同策略

apply_policy "数据库备份" "database" 30 12 36 10

apply_policy "配置文件" "config" 7 4 12 5

apply_policy "日志文件" "logs" 3 0 0 0

apply_policy "完整备份" "full" 7 8 24 10

4.2 自动化备份策略脚本

#!/bin/bash
# automated-backup-system.sh

set -euo pipefail

# ============================================

# 配置部分

# ============================================

CONFIG_DIR="$HOME/.restic"

LOG_DIR="/var/log/restic"

LOCK_FILE="/tmp/restic-backup.lock"

BACKUP_SOURCES=(

    "/etc"

    "/home"

    "/var/lib"

    "/opt"

)

# ============================================

# 初始化函数

# ============================================

init() {

    mkdir -p "$CONFIG_DIR" "$LOG_DIR"

    

    # 加载环境配置

    if [[ -f "$CONFIG_DIR/env.conf" ]]; then

        source "$CONFIG_DIR/env.conf"

    else

        echo "错误: 配置文件不存在" >&2

        exit 1

    fi

    

    # 检查依赖

    command -v restic >/dev/null 2>&1 || {

        echo "错误: restic 未安装" >&2

        exit 1

    }

}

# ============================================

# 锁管理

# ============================================

acquire_lock() {

    exec 200>"$LOCK_FILE"

    flock -n 200 || {

        echo "另一个备份任务正在运行" >&2

        exit 1

    }

    echo $$ >&200

}

release_lock() {

    flock -u 200

    rm -f "$LOCK_FILE"

}

# ============================================

# 备份执行函数

# ============================================

perform_backup() {

    local backup_type=$1

    local timestamp=$(date +%Y%m%d-%H%M%S)

    local log_file="$LOG_DIR/backup-${backup_type}-${timestamp}.log"

    

    echo "开始 ${backup_type} 备份: $(date)" | tee -a "$log_file"

    

    # 根据备份类型设置标签

    local tags="auto,${backup_type},$(hostname)"

    

    # 执行备份

    restic backup "${BACKUP_SOURCES[@]}" \

        --tag "$tags" \

        --host "$(hostname)" \

        --time "$(date +%Y-%m-%dT%H:%M:%S)" \

        --exclude-file="$CONFIG_DIR/excludes" \

        --one-file-system \

        --verbose 2>&1 | tee -a "$log_file"

    

    local backup_status=${PIPESTATUS[0]}

    

    if [[ $backup_status -eq 0 ]]; then

        echo "备份成功完成" | tee -a "$log_file"

        return 0

    else

        echo "备份失败,退出码: $backup_status" | tee -a "$log_file"

        return $backup_status

    fi

}

# ============================================

# 保留策略函数

# ============================================

apply_retention() {

    local backup_type=$1

    local log_file="$LOG_DIR/retention-$(date +%Y%m%d-%H%M%S).log"

    

    echo "应用保留策略: $(date)" | tee -a "$log_file"

    

    case $backup_type in

        hourly)

            restic forget \

                --tag "auto,hourly" \

                --keep-within 2h \

                --keep-hourly 24 \

                --prune 2>&1 | tee -a "$log_file"

            ;;

        daily)

            restic forget \

                --tag "auto,daily" \

                --keep-within 24h \

                --keep-daily 7 \

                --keep-weekly 4 \

                --prune 2>&1 | tee -a "$log_file"

            ;;

        weekly)

            restic forget \

                --tag "auto,weekly" \

                --keep-within 7d \

                --keep-weekly 8 \

                --keep-monthly 12 \

                --prune 2>&1 | tee -a "$log_file"

            ;;

        monthly)

            restic forget \

                --tag "auto,monthly" \

                --keep-within 30d \

                --keep-monthly 24 \

                --keep-yearly 10 \

                --prune 2>&1 | tee -a "$log_file"

            ;;

        *)

            echo "未知的备份类型: $backup_type" | tee -a "$log_file"

            return 1

            ;;

    esac

}

# ============================================

# 健康检查函数

# ============================================

health_check() {

    local log_file="$LOG_DIR/health-$(date +%Y%m%d).log"

    

    echo "执行健康检查: $(date)" | tee -a "$log_file"

    

    # 检查存储库完整性

    restic check --read-data --quiet 2>&1 | tee -a "$log_file"

    local check_status=${PIPESTATUS[0]}

    

    # 检查备份统计

    restic stats --verbose 2>&1 | tee -a "$log_file"

    

    # 检查最近的备份

    echo -e "\n最近5个快照:" | tee -a "$log_file"

    restic snapshots --latest 5 2>&1 | tee -a "$log_file"

    

    if [[ $check_status -eq 0 ]]; then

        echo "健康检查通过" | tee -a "$log_file"

        return 0

    else

        echo "健康检查失败" | tee -a "$log_file"

        send_alert "Restic 健康检查失败"

        return 1

    fi

}

# ============================================

# 通知函数

# ============================================

send_alert() {

    local message=$1

    

    # 发送邮件通知

    if command -v mail >/dev/null 2>&1; then

        echo "$message" | mail -s "Restic 备份警报" admin@example.com

    fi

    

    # 发送 Slack 通知

    if [[ -n "${SLACK_WEBHOOK_URL:-}" ]]; then

        curl -X POST -H 'Content-type: application/json' \

            --data "{\"text\":\"$message\"}" \

            "$SLACK_WEBHOOK_URL" >/dev/null 2>&1

    fi

    

    # 发送 Telegram 通知

    if [[ -n "${TELEGRAM_BOT_TOKEN:-}" && -n "${TELEGRAM_CHAT_ID:-}" ]]; then

        curl -s -X POST \

            "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \

            -d "chat_id=${TELEGRAM_CHAT_ID}&text=$message" >/dev/null 2>&1

    fi

}

# ============================================

# 主函数

# ============================================

main() {

    local action=${1:-daily}

    

    # 获取锁

    acquire_lock

    

    # 初始化

    init

    

    # 执行备份

    case $action in

        hourly)

            perform_backup "hourly"

            apply_retention "hourly"

            ;;

        daily)

            perform_backup "daily"

            apply_retention "daily"

            ;;

        weekly)

            perform_backup "weekly"

            apply_retention "weekly"

            # 每周执行健康检查

            health_check

            ;;

        monthly)

            perform_backup "monthly"

            apply_retention "monthly"

            # 每月执行完整检查

            restic check --with-cache --read-data

            ;;

        health)

            health_check

            ;;

        stats)

            restic stats --mode raw-data

            restic stats --mode blobs-per-file

            ;;

        list)

            restic snapshots --group-by host,tags,paths

            ;;

        *)

            echo "用法: $0 {hourly|daily|weekly|monthly|health|stats|list}"

            exit 1

            ;;

    esac

    

    local exit_status=$?

    

    # 释放锁

    release_lock

    

    exit $exit_status

}

# 异常处理

trap 'release_lock; echo "脚本被中断" >&2; exit 1' INT TERM

# 运行主函数

main "$@"

```

### 4.3 crontab 定时任务配置

```bash

# /etc/crontab - Restic 定时任务

# ============================================

# 每小时增量备份

0 * * * * root /usr/local/bin/restic-backup.sh hourly

# 每日完整备份(凌晨2点)

0 2 * * * root /usr/local/bin/restic-backup.sh daily

# 每周备份(周日凌晨3点)

0 3 * * 0 root /usr/local/bin/restic-backup.sh weekly

# 每月备份(每月1号凌晨4点)

0 4 1 * * root /usr/local/bin/restic-backup.sh monthly

# 每日健康检查(凌晨1点)

0 1 * * * root /usr/local/bin/restic-backup.sh health

# 清理旧日志(每月1号凌晨5点)

0 5 1 *  root find /var/log/restic -name ".log" -mtime +90 -delete

# 系统性能监控备份

# 低负载时段执行备份

*/15 * * * * root [ $(uptime | awk '{print $10}' | cut -d. -f1) -lt 5 ] && /usr/local/bin/restic-backup.sh hourly

# 电池供电时不执行备份(笔记本)

@reboot root echo 'if on_ac_power; then /usr/local/bin/restic-backup.sh daily; fi' > /etc/cron.d/restic-on-ac

五、监控和报警系统

5.1 Prometheus 监控

# restic-exporter.yml

global:

  scrape_interval: 60s

scrape_configs:

  - job_name: 'restic'

    static_configs:

      - targets: ['localhost:9987']

    metrics_path: /metrics

```

```bash

#!/bin/bash

# restic-metrics-exporter.sh

export METRICS_PORT=9987

export METRICS_FILE="/tmp/restic-metrics.prom"

generate_metrics() {

    # 快照数量

    local snapshot_count=$(restic snapshots --json | jq length)

    echo "restic_snapshots_total $snapshot_count" > $METRICS_FILE

    

    # 存储库大小

    local repo_size=$(restic stats --json | jq -r '.total_size')

    echo "restic_repository_size_bytes $repo_size" >> $METRICS_FILE

    

    # 最后备份时间

    local last_backup=$(restic snapshots --json --last 1 | jq -r '.[0].time')

    local last_backup_ts=$(date -d "$last_backup" +%s)

    echo "restic_last_backup_timestamp $last_backup_ts" >> $METRICS_FILE

    

    # 备份成功率

    local total_backups=$(restic snapshots --json | jq length)

    local failed_backups=0  # 需要从日志中解析

    echo "restic_backup_success_ratio $(echo "1 - $failed_backups/$total_backups" | bc)" >> $METRICS_FILE

}

# 启动 HTTP 服务器提供 metrics

while true; do

    generate_metrics

    nc -l -p $METRICS_PORT -c "cat $METRICS_FILE"

    sleep 60

done

5.2 日志分析和报告

#!/bin/bash
# backup-report-generator.sh

generate_report() {

    local period=${1:-daily}

    local report_file="/var/log/restic/report-$(date +%Y%m%d).html"

    

    cat > "$report_file" << EOF

<!DOCTYPE html>

<html>

<head>

    <title>Restic 备份报告 - $(date)</title>

    <style>

        body { font-family: Arial, sans-serif; margin: 20px; }

        .success { color: green; }

        .warning { color: orange; }

        .error { color: red; }

        table { border-collapse: collapse; width: 100%; }

        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }

        th { background-color: #f2f2f2; }

    </style>

</head>

<body>

    <h1>Restic 备份报告</h1>

    <p>生成时间: $(date)</p>

    <p>周期: $period</p>

    

    <h2>备份概览</h2>

    <pre>

$(restic snapshots --latest 10)

    </pre>

    

    <h2>存储库统计</h2>

    <pre>

$(restic stats)

    </pre>

    

    <h2>系统状态</h2>

    <pre>

磁盘使用:

$(df -h / /home /var)

内存使用:

$(free -h)

    </pre>

</body>

</html>

EOF

    

    # 发送报告

    if [[ -n "${REPORT_EMAIL:-}" ]]; then

        cat "$report_file" | mail -s "Restic 备份报告 $(date)" -a "$report_file" "$REPORT_EMAIL"

    fi

}

六、灾备和恢复演练

6.1 恢复测试脚本

#!/bin/bash
# disaster-recovery-test.sh

test_recovery() {

    local test_id="dr-test-$(date +%Y%m%d-%H%M%S)"

    local restore_dir="/tmp/restic-recovery-test-$test_id"

    

    echo "开始灾备恢复测试: $test_id"

    

    # 1. 选择最新的快照

    local latest_snapshot=$(restic snapshots --latest 1 --json | jq -r '.[0].short_id')

    

    if [[ -z "$latest_snapshot" ]]; then

        echo "错误: 未找到快照"

        return 1

    fi

    

    # 2. 恢复测试

    mkdir -p "$restore_dir"

    echo "恢复快照 $latest_snapshot 到 $restore_dir"

    

    restic restore "$latest_snapshot" \

        --target "$restore_dir" \

        --verify \

        --verbose

    

    local restore_status=$?

    

    # 3. 验证恢复的文件

    if [[ $restore_status -eq 0 ]]; then

        echo "恢复测试成功"

        

        # 检查关键文件

        local critical_files=(

            "$restore_dir/etc/passwd"

            "$restore_dir/etc/hosts"

            "$restore_dir/etc/fstab"

        )

        

        for file in "${critical_files[@]}"; do

            if [[ -f "$file" ]]; then

                echo "✓ 找到关键文件: $file"

            else

                echo "✗ 缺失关键文件: $file"

            fi

        done

        

        # 计算恢复的文件数量

        local file_count=$(find "$restore_dir" -type f | wc -l)

        echo "恢复文件总数: $file_count"

        

    else

        echo "恢复测试失败"

    fi

    

    # 4. 清理

    rm -rf "$restore_dir"

    

    # 5. 记录测试结果

    local log_entry="$(date),$test_id,$latest_snapshot,$restore_status,$file_count"

    echo "$log_entry" >> /var/log/restic/recovery-tests.log

    

    return $restore_status

}

# 定期执行恢复测试

schedule_recovery_tests() {

    # 每月执行完整恢复测试

    0 4 1 * * /usr/local/bin/disaster-recovery-test.sh

    

    # 每季度执行跨区域恢复测试

    0 5 1 /3  /usr/local/bin/cross-region-recovery-test.sh

}

七、多存储库同步和复制

#!/bin/bash
# multi-repo-sync.sh

# 主存储库配置

PRIMARY_REPO="s3:primary-bucket/restic"

PRIMARY_PASSWORD="primary-pass"

# 次存储库配置

SECONDARY_REPOS=(

    "s3:secondary-bucket/restic"

    "b2:backblaze-bucket:restic"

    "sftp:nas@192.168.1.100:/backup/restic"

)

sync_to_secondary() {

    local snapshot_id=$1

    

    for repo in "${SECONDARY_REPOS[@]}"; do

        echo "同步到: $repo"

        

        # 导出快照

        restic -r "$PRIMARY_REPO" dump "$snapshot_id" | \

        restic -r "$repo" backup --stdin --stdin-filename "snapshot.tar"

        

        if [[ $? -eq 0 ]]; then

            echo "✓ 同步成功: $repo"

        else

            echo "✗ 同步失败: $repo"

        fi

    done

}

# 或者使用 restic copy 命令(需要 restic 0.12.0+)

copy_repository() {

    local source_repo=$1

    local dest_repo=$2

    

    export RESTIC_REPOSITORY="$source_repo"

    export RESTIC_PASSWORD="$PRIMARY_PASSWORD"

    

    restic copy --repo2 "$dest_repo"

}

# 增量同步

incremental_sync() {

    # 获取源存储库最新快照

    local latest_snapshot=$(restic -r "$PRIMARY_REPO" snapshots --latest 1 --json | jq -r '.[0].id')

    

    # 检查目标存储库是否已有该快照

    for dest_repo in "${SECONDARY_REPOS[@]}"; do

        if ! restic -r "$dest_repo" snapshots | grep -q "$latest_snapshot"; then

            echo "检测到新快照: $latest_snapshot,开始同步到 $dest_repo"

            sync_to_secondary "$latest_snapshot"

        fi

    done

}

八、性能和优化指南

8.1 性能调优参数

#!/bin/bash
# restic-optimize.sh

optimize_backup() {

    # 调整文件读取缓冲区

    export RESTIC_BUFFER_SIZE="32M"

    

    # 调整并发上传数量

    export RESTIC_UPLOAD_CONCURRENCY=5

    

    # 调整重试策略

    export RESTIC_MAX_RETRIES=10

    export RESTIC_BACKOFF_MAX=300

    

    # 使用压缩(根据CPU和网络权衡)

    export RESTIC_COMPRESSION="auto"

    

    # 调整包大小

    export RESTIC_PACK_SIZE=32

    

    # 缓存优化

    export RESTIC_CACHE_DIR="/fast/ssd/cache/restic"

    export RESTIC_CACHE_MAX_AGE=720  # 小时

    

    # 网络优化

    export RESTIC_CONNECT_TIMEOUT=60

    export RESTIC_READ_TIMEOUT=600

    export RESTIC_HTTP_PROXY="http://proxy:3128"

    

    # 排除大量小文件提升性能

    export RESTIC_EXCLUDE="*.pyc,*.swp,*.tmp,Thumbs.db"

}

# 针对不同场景的优化配置

case $BACKUP_SCENARIO in

    "high-latency")

        export RESTIC_UPLOAD_CONCURRENCY=2

        export RESTIC_PACK_SIZE=64

        ;;

    "low-bandwidth")

        export RESTIC_COMPRESSION="max"

        export RESTIC_UPLOAD_CONCURRENCY=1

        ;;

    "high-performance")

        export RESTIC_UPLOAD_CONCURRENCY=10

        export RESTIC_BUFFER_SIZE="128M"

        export RESTIC_CACHE_DIR="/dev/shm/restic"

        ;;

esac

8.2 基准测试脚本

#!/bin/bash

# restic-benchmark.sh

run_benchmark() {

local test_data="/tmp/restic-benchmark-data"

local results_file="/var/log/restic/benchmark-$(date +%Y%m%d).json"



# 创建测试数据

mkdir -p "$test_data"

dd if=/dev/urandom of="$test_data/testfile-1G.bin" bs=1M count=1024

mkdir -p "$test_data/smallfiles"

for i in {1..1000}; do

dd if=/dev/urandom of="$test_data/smallfiles/file-$i.bin" bs=1K count=1

done



# 执行基准测试

echo "开始基准测试..."



# 测试1: 大文件备份速度

echo "测试大文件备份..."

time restic backup "$test_data/testfile-1G.bin" --tag benchmark



# 测试2: 大量小文件备份

echo "测试小文件备份..."

time restic backup "$test_data/smallfiles" --tag benchmark



# 测试3: 增量备份

echo "测试增量备份..."

touch "$test_data/testfile-1G.bin"

time restic backup "$test_data" --tag benchmark



# 测试4: 恢复速度

echo "测试恢复速度..."

local snapshot_id=$(restic snapshots --tag benchmark --latest 1 --json | jq -r '.[0].short_id')

time restic restore "$snapshot_id" --target /tmp/restore-test



# 清理

rm -rf "$test_data" "/tmp/restore-test"



echo "基准测试完成,结果保存到: $results_file"

}