一、环境变量系统配置
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"
}