💾 备份恢复
数据库自动备份、对象存储归档、灾难恢复演练——没有测试过的备份等于没有备份
📐 3-2-1 备份原则
这是备份行业的黄金法则,无论你是个人还是企业都适用:
| 规则 | 含义 | 为什么 |
| 3 | 至少 3 份数据副本 | 1 份原始数据 + 2 份备份 |
| 2 | 至少 2 种不同存储介质 | 同一硬盘的 2 份不算——硬盘坏了全丢 |
| 1 | 至少 1 份在异地 | 机房着火/地震不会全丢 |
独立开发者的 3-2-1 实现方案
3 份副本:
① 生产数据库 (VPS)
② 同 VPS 本地备份 (/backup/)
③ 对象存储 (S3/DO Spaces/Backblaze)
2 种介质:
① VPS 本地 SSD
② 对象存储(不同物理设备)
1 份异地:
③ 对象存储(不同数据中心/区域)
成本估算:
- DO Spaces: $5/月 (250GB)
- Backblaze B2: $0.005/GB/月
- AWS S3 Glacier: $0.004/GB/月(冷存储)
🗄️ PostgreSQL 自动备份
#!/bin/bash
# /usr/local/bin/backup-postgres.sh
set -e
# 配置
DB_NAME="myapp"
DB_USER="postgres"
BACKUP_DIR="/backup/postgres"
S3_BUCKET="s3://my-backups/postgres"
RETENTION_LOCAL=7 # 本地保留 7 天
RETENTION_S3=30 # S3 保留 30 天
DATE=$(date +%Y-%m-%d_%H%M)
SLACK_WEBHOOK="" # 可选:通知
# 创建备份目录
mkdir -p $BACKUP_DIR
# 1. pg_dump 备份
echo "📦 开始备份 $DB_NAME..."
docker exec postgres pg_dump -U $DB_USER -F c $DB_NAME \
> "$BACKUP_DIR/${DB_NAME}_${DATE}.dump"
# 2. 压缩
gzip "$BACKUP_DIR/${DB_NAME}_${DATE}.dump"
# 3. 上传到 S3
echo "☁️ 上传到 S3..."
aws s3 cp "$BACKUP_DIR/${DB_NAME}_${DATE}.dump.gz" \
"$S3_BUCKET/${DB_NAME}_${DATE}.dump.gz"
# 4. 清理本地旧备份
find $BACKUP_DIR -name "*.dump.gz" -mtime +$RETENTION_LOCAL -delete
# 5. 清理 S3 旧备份
aws s3 ls $S3_BUCKET/ | \
awk '{print $4}' | \
while read -r file; do
file_date=$(echo $file | grep -oP '\d{4}-\d{2}-\d{2}')
if [[ $(date -d "$file_date" +%s) -lt $(date -d "$RETENTION_S3 days ago" +%s) ]]; then
aws s3 rm "$S3_BUCKET/$file"
fi
done
# 6. 验证备份
echo "🔍 验证备份..."
docker exec postgres pg_restore --list \
"$BACKUP_DIR/${DB_NAME}_${DATE}.dump.gz" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ 备份成功: ${DB_NAME}_${DATE}.dump.gz"
# 通知(可选)
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{\"text\":\"✅ PostgreSQL 备份成功: ${DATE}\"}"
else
echo "❌ 备份验证失败!"
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{\"text\":\"❌ PostgreSQL 备份验证失败: ${DATE}\"}"
fi
# Cron 定时任务
# crontab -e
# 0 2 * * * /usr/local/bin/backup-postgres.sh >> /var/log/backup.log 2>&1
📂 文件备份
#!/bin/bash
# /usr/local/bin/backup-files.sh
set -e
DATE=$(date +%Y-%m-%d)
S3_BUCKET="s3://my-backups/files"
# 备份 Docker Compose 配置
tar czf /tmp/compose-config_${DATE}.tar.gz \
/home/deploy/app/docker-compose.yml \
/home/deploy/app/.env \
/home/deploy/app/traefik/
# 备份应用数据(Uploads 等)
tar czf /tmp/app-data_${DATE}.tar.gz \
/home/deploy/app/uploads/
# 上传到 S3
aws s3 cp /tmp/compose-config_${DATE}.tar.gz $S3_BUCKET/config/
aws s3 cp /tmp/app-data_${DATE}.tar.gz $S3_BUCKET/data/
# 清理临时文件
rm /tmp/compose-config_${DATE}.tar.gz /tmp/app-data_${DATE}.tar.gz
echo "✅ 文件备份完成: $DATE"
🧪 灾难恢复演练
没有测试过的备份等于没有备份。定期(每月)做一次灾难恢复演练:
#!/bin/bash
# /usr/local/bin/test-restore.sh — 灾难恢复测试
set -e
echo "🧪 开始灾难恢复测试..."
# 1. 创建测试数据库
docker exec postgres psql -U postgres -c "CREATE DATABASE test_restore;"
# 2. 从 S3 下载最新备份
LATEST=$(aws s3 ls s3://my-backups/postgres/ | tail -1 | awk '{print $4}')
aws s3 cp "s3://my-backups/postgres/$LATEST" /tmp/latest_backup.dump.gz
# 3. 解压
gunzip /tmp/latest_backup.dump.gz
# 4. 恢复
docker exec -i postgres pg_restore -U postgres -d test_restore \
< /tmp/latest_backup.dump
# 5. 验证数据完整性
TABLES=$(docker exec postgres psql -U postgres -d test_restore \
-t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';")
echo "📊 恢复的表数量: $TABLES"
# 6. 随机数据验证
docker exec postgres psql -U postgres -d test_restore \
-c "SELECT count(*) FROM users;"
# 7. 清理
docker exec postgres psql -U postgres -c "DROP DATABASE test_restore;"
rm /tmp/latest_backup.dump
echo "✅ 灾难恢复测试完成!"
# 建议的恢复演练频率
# - 每月一次完整恢复测试
# - 每季度一次异地恢复测试(在另一台 VPS 上恢复)
# - 每半年一次全流程演练(从零搭建 + 恢复)
📊 备份监控
# 使用 Uptime Kuma 监控备份状态
# 方法:备份脚本写一个健康检查文件
# 在备份成功后
echo "$(date +%s)" > /backup/last-successful-backup
# Nginx 暴露健康端点
location /backup-status {
alias /backup/last-successful-backup;
default_type text/plain;
}
# Uptime Kuma 配置
# Type: HTTP(s)
# URL: https://your-server/backup-status
# Expected: 正则匹配时间戳
# Interval: 3600 (每小时检查)
# Alert: 如果文件超过 26 小时未更新 → 报警
💡 备份的隐性成本
数据库备份不只是数据——还包括:① 环境变量(.env 文件)② Docker Compose 配置 ③ SSL 证书(虽然可以重新获取,但 DNS 验证可能卡住)④ Cron 任务 ⑤ SSH 密钥和授权。建议把所有配置文件放在 Git 仓库(敏感信息用 SOPS 加密),这样恢复时 git clone + terraform apply + ansible-playbook + 数据库恢复 = 完整恢复。
🔗 相关专题
🐳 Docker Compose · 🛡️ 服务器加固 · 🏗️ Terraform · 🏗️ 返回首页