繁华依在的小站

💾 自动化备份策略:别让你的数据一夜之间消失!

技术 作者:admin | 发布时间:2026-02-03 16:18 | 更新时间:2026-02-07 16:21 | 阅读:10 |
标签: 备份 数据安全 Shell脚本 自动化 灾难恢复

💾 自动化备份策略:别让你的数据一夜之间消失!

"我的数据不会丢!" —— 每个人都这么想,直到有一天... 💥

让我们看看如何建立一个可靠的备份系统,让你的数据安全无忧!🔒


😱 真实案例

案例 1:误删数据库

text
1
2
3
4
5
6
7
开发时想清空测试数据
    ↓
执行:DELETE FROM posts;
    ↓
等等...我连的是生产库!😱
    ↓
3 年的文章,全没了

案例 2:服务器挂了

text
1
2
3
4
5
硬盘故障,数据无法恢复
    ↓
没有备份,一切从头来
    ↓
损失:无法估量 💸

案例 3:被勒索病毒

text
1
2
3
4
5
服务器被加密
    ↓
黑客:付比特币就给你解密
    ↓
有备份?直接恢复!🎉

结论:备份不是可有可无,而是救命稻草


🎯 备份的 3-2-1 原则

text
1
2
3
4
5
3份数据副本
    ↓
2种不同介质
    ↓
1份异地备份

示例

text
1
2
3
4
5
生产数据(本地)
    ↓
备份1:本地磁盘(快)
备份2:云存储(安全)
备份3:异地服务器(防灾)

📦 第一步:需要备份什么?

数据库

bash
1
2
3
4
5
6
7
8
# SQLite
blog.db

# MySQL/MariaDB
mysqldump -u user -p database > backup.sql

# PostgreSQL
pg_dump database > backup.sql

用户上传的文件

bash
1
2
3
4
static/uploads/
    ├── images/
    ├── files/
    └── avatars/

配置文件

bash
1
2
3
4
.env                    # 环境变量
nginx.conf              # Nginx 配置
gunicorn_config.py      # Gunicorn 配置
frpc.toml              # FRP 配置

应用代码

bash
1
2
3
4
5
# 或者用 Git
git push origin master

# 或者打包
tar czf app-code.tar.gz app/ templates/ static/

🛠️ 第二步:备份脚本

完整备份脚本

backup.sh

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash

###############################################################################
# 博客自动备份脚本
###############################################################################

set -e  # 遇到错误立即退出

# 配置
PROJECT_DIR="/var/www/blog"
BACKUP_DIR="/var/backups/blog"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30  # 保留 30 天

# 颜色输出
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

echo -e "${GREEN}=== 博客备份开始 $(date) ===${NC}"

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 临时目录
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT

# 1. 备份数据库
echo -e "${GREEN}[1/5] 备份数据库...${NC}"
cp "$PROJECT_DIR/blog.db" "$TEMP_DIR/blog.db"

# 2. 备份上传文件
echo -e "${GREEN}[2/5] 备份上传文件...${NC}"
mkdir -p "$TEMP_DIR/uploads"
rsync -av "$PROJECT_DIR/static/uploads/" "$TEMP_DIR/uploads/"

# 3. 备份配置文件
echo -e "${GREEN}[3/5] 备份配置文件...${NC}"
mkdir -p "$TEMP_DIR/config"
cp "$PROJECT_DIR/.env" "$TEMP_DIR/config/" 2>/dev/null || true
cp "$PROJECT_DIR/gunicorn_config.py" "$TEMP_DIR/config/"
cp "$PROJECT_DIR/nginx/blog.conf" "$TEMP_DIR/config/" 2>/dev/null || true

# 4. 创建备份包
echo -e "${GREEN}[4/5] 打包备份...${NC}"
BACKUP_FILE="$BACKUP_DIR/blog_backup_$DATE.tar.gz"
tar czf "$BACKUP_FILE" -C "$TEMP_DIR" .
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo -e "${GREEN}  备份文件: $BACKUP_FILE ($BACKUP_SIZE)${NC}"

# 5. 清理旧备份
echo -e "${GREEN}[5/5] 清理旧备份...${NC}"
find "$BACKUP_DIR" -name "blog_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
OLD_COUNT=$(find "$BACKUP_DIR" -name "blog_backup_*.tar.gz" | wc -l)
echo -e "${GREEN}  保留备份: $OLD_COUNT${NC}"

# 完成
echo -e "${GREEN}=== 备份完成!$(date) ===${NC}"

# 可选:上传到云存储
# rclone copy "$BACKUP_FILE" remote:backups/

设置权限

bash
1
chmod +x backup.sh

增量备份(节省空间)

backup-incremental.sh

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

PROJECT_DIR="/var/www/blog"
BACKUP_DIR="/var/backups/blog"
DATE=$(date +%Y%m%d)

# 每周一次全备份
if [ $(date +%u) -eq 7 ]; then
    echo "执行全备份..."
    tar czf "$BACKUP_DIR/full_$DATE.tar.gz" \
        -C "$PROJECT_DIR" \
        blog.db static/uploads .env
else
    echo "执行增量备份..."
    # 使用 rsync 增量同步
    rsync -av --delete \
        "$PROJECT_DIR/blog.db" \
        "$PROJECT_DIR/static/uploads/" \
        "$BACKUP_DIR/incremental/"
fi

☁️ 第三步:云备份

Rclone 配置

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 安装 Rclone
curl https://rclone.org/install.sh | sudo bash

# 配置云存储
rclone config

# 支持的服务:
# - Google Drive
# - Dropbox
# - OneDrive
# - Amazon S3
# - 阿里云 OSS

上传到云存储

backup-cloud.sh

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash

BACKUP_FILE="$1"
REMOTE_NAME="googledrive"  # rclone 配置的远程名称
REMOTE_DIR="backups/blog"

# 上传
echo "上传到云存储..."
rclone copy "$BACKUP_FILE" "$REMOTE_NAME:$REMOTE_DIR/" \
    --progress \
    --transfers 4

echo "上传完成!"

# 验证
rclone check "$BACKUP_FILE" "$REMOTE_NAME:$REMOTE_DIR/$(basename $BACKUP_FILE)"

多云备份(更安全)

bash
1
2
3
4
# 备份到多个云
rclone copy "$BACKUP_FILE" "google:backups/"
rclone copy "$BACKUP_FILE" "dropbox:backups/"
rclone copy "$BACKUP_FILE" "s3:backups/"

⏰ 第四步:自动化定时任务

Cron 定时备份

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 编辑 crontab
crontab -e

# 每天凌晨 2 点备份
0 2 * * * /var/www/blog/scripts/backup.sh >> /var/log/blog_backup.log 2>&1

# 每周日凌晨 3 点上传到云
0 3 * * 0 /var/www/blog/scripts/backup-cloud.sh /var/backups/blog/blog_backup_$(date +\%Y\%m\%d)_*.tar.gz >> /var/log/blog_backup.log 2>&1

# 每小时检查磁盘空间
0 * * * * /var/www/blog/scripts/check-disk.sh

Systemd Timer(更可靠)

/etc/systemd/system/blog-backup.service

ini
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=Blog Backup Service
After=network.target

[Service]
Type=oneshot
User=root
WorkingDirectory=/var/www/blog
ExecStart=/var/www/blog/scripts/backup.sh

[Install]
WantedBy=multi-user.target

/etc/systemd/system/blog-backup.timer

ini
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[Unit]
Description=Blog Backup Timer
Requires=blog-backup.service

[Timer]
OnCalendar=daily
OnCalendar=02:00
Persistent=true

[Install]
WantedBy=timers.target

启动定时器

bash
1
2
3
4
5
6
sudo systemctl daemon-reload
sudo systemctl enable blog-backup.timer
sudo systemctl start blog-backup.timer

# 查看下次执行时间
systemctl list-timers

🧪 第五步:验证备份

定期恢复测试

test-restore.sh

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

BACKUP_FILE="$1"
TEST_DIR="/tmp/restore-test"

echo "测试恢复: $BACKUP_FILE"

# 创建测试目录
mkdir -p "$TEST_DIR"

# 解压备份
tar xzf "$BACKUP_FILE" -C "$TEST_DIR"

# 验证数据库
echo "验证数据库..."
sqlite3 "$TEST_DIR/blog.db" "SELECT COUNT(*) FROM posts;"

# 验证文件
echo "验证文件..."
ls -lh "$TEST_DIR/uploads/" | head -5

# 清理
rm -rf "$TEST_DIR"

echo "✅ 备份验证通过!"

自动验证脚本

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash

# 每周自动测试最新的备份
LATEST_BACKUP=$(ls -t /var/backups/blog/blog_backup_*.tar.gz | head -1)

if /var/www/blog/scripts/test-restore.sh "$LATEST_BACKUP"; then
    echo "✅ 备份正常" | mail -s "备份验证成功" admin@example.com
else
    echo "❌ 备份损坏" | mail -s "备份验证失败" admin@example.com
fi

📊 第六步:监控备份

备份状态检查

check-backup.sh

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

BACKUP_DIR="/var/backups/blog"
MAX_AGE=48  # 48 小时

# 查找最新备份
LATEST_BACKUP=$(find "$BACKUP_DIR" -name "blog_backup_*.tar.gz" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2)

if [ -z "$LATEST_BACKUP" ]; then
    echo "❌ 没有找到备份!"
    exit 1
fi

# 检查备份时间
BACKUP_AGE=$(( ($(date +%s) - $(stat -c %Y "$LATEST_BACKUP")) / 3600 ))

if [ $BACKUP_AGE -gt $MAX_AGE ]; then
    echo "❌ 最新备份已过期:$BACKUP_AGE 小时前"
    exit 1
else
    echo "✅ 最新备份:$(basename $LATEST_BACKUP) ($BACKUP_AGE 小时前)"
fi

# 检查备份大小
BACKUP_SIZE=$(du -h "$LATEST_BACKUP" | cut -f1)
echo "📦 备份大小: $BACKUP_SIZE"

Prometheus 监控指标

backup-exporter.py

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python3
import os
import time
from prometheus_client import start_http_server
from prometheus_client import Gauge

# 指标
backup_age = Gauge('backup_age_hours', 'Backup age in hours')
backup_size = Gauge('backup_size_bytes', 'Backup size in bytes')

def check_backup():
    backup_dir = '/var/backups/blog'
    latest = max(
        (os.path.getmtime(os.path.join(backup_dir, f)), f)
        for f in os.listdir(backup_dir)
        if f.endswith('.tar.gz')
    )

    age = (time.time() - latest[0]) / 3600
    size = os.path.getsize(os.path.join(backup_dir, latest[1]))

    backup_age.set(age)
    backup_size.set(size)

if __name__ == '__main__':
    start_http_server(9101)
    while True:
        check_backup()
        time.sleep(60)

🚨 第七步:灾难恢复

恢复流程

restore.sh

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/bin/bash

BACKUP_FILE="$1"
PROJECT_DIR="/var/www/blog"

if [ -z "$BACKUP_FILE" ]; then
    echo "用法: $0 <backup_file.tar.gz>"
    exit 1
fi

echo "⚠️  警告:这将覆盖现有数据!"
read -p "确定要恢复吗?(yes/no): " confirm

if [ "$confirm" != "yes" ]; then
    echo "取消恢复"
    exit 0
fi

# 1. 停止服务
echo "停止服务..."
sudo systemctl stop flask-blog

# 2. 备份当前数据(以防万一)
echo "备份当前数据..."
sudo mv "$PROJECT_DIR/blog.db" "$PROJECT_DIR/blog.db.backup" 2>/dev/null || true

# 3. 恢复数据库
echo "恢复数据库..."
tar xzf "$BACKUP_FILE" -C /tmp blog.db
sudo mv /tmp/blog.db "$PROJECT_DIR/blog.db"

# 4. 恢复上传文件
echo "恢复上传文件..."
sudo tar xzf "$BACKUP_FILE" -C /tmp uploads/
sudo cp -r /tmp/uploads/* "$PROJECT_DIR/static/uploads/"

# 5. 恢复配置
echo "恢复配置文件..."
sudo tar xzf "$BACKUP_FILE" -C /tmp config/.env
sudo cp /tmp/config/.env "$PROJECT_DIR/.env"

# 6. 重启服务
echo "重启服务..."
sudo systemctl start flask-blog

# 7. 验证
echo "验证恢复..."
sleep 3
if systemctl is-active --quiet flask-blog; then
    echo "✅ 恢复成功!"
else
    echo "❌ 恢复失败,请检查日志"
    exit 1
fi

📋 第八步:备份策略模板

小型博客

text
1
2
3
4
频率:每天
保留:30 天
位置:本地 + 云端
大小:< 100MB

中型博客

text
1
2
3
4
频率:每小时
保留:90 天
位置:本地 + 云端 + 异地
大小:< 1GB

大型博客

text
1
2
3
4
频率:实时(主从复制)+ 每天全备份
保留:1 年
位置:多地多副本
大小:> 1GB

🎯 最佳实践

1. 加密备份

bash
1
2
3
4
5
# 加密备份
tar czf - data/ | gpg --encrypt --recipient admin@example.com > backup.tar.gz.gpg

# 解密恢复
gpg --decrypt backup.tar.gz.gpg | tar xzf -

2. 备份元数据

bash
1
2
3
4
5
6
7
8
# 记录备份信息
cat > "$BACKUP_DIR/backup_info.txt" << EOF
备份时间: $(date)
备份文件: blog_backup_$DATE.tar.gz
备份大小: $BACKUP_SIZE
数据库版本: $(sqlite3 blog.db 'SELECT sqlite_version()')
文章数量: $(sqlite3 blog.db 'SELECT COUNT(*) FROM posts')
EOF

3. 备份通知

bash
1
2
3
4
5
6
7
# 备份成功通知
echo "博客备份成功:$BACKUP_FILE" | mail -s "✅ 备份成功" admin@example.com

# 备份失败通知
if ! ./backup.sh; then
    echo "博客备份失败!" | mail -s "❌ 备份失败" admin@example.com
fi

⚠️ 常见错误

1. 只备份不恢复测试

text
1
2
3
4
❌ 错误:只备份,不测试恢复
✅ 正确:定期恢复测试

"备份不存在 = 没有备份"

2. 备份文件和网站在同一服务器

text
1
2
❌ 危险:服务器挂了,备份也没了
✅ 正确:至少有一份异地备份

3. 忘记备份配置文件

text
1
2
❌ 问题:恢复时发现没配置文件
✅ 正确:配置文件也要备份

📊 备份检查清单

备份内容

  • [ ] 数据库
  • [ ] 上传文件
  • [ ] 配置文件
  • [ ] 应用代码(或 Git 仓库)

自动化

  • [ ] 定时任务已配置
  • [ ] 备份脚本正常执行
  • [ ] 错误通知已设置

可靠性

  • [ ] 本地备份
  • [ ] 云端备份
  • [ ] 定期恢复测试

监控

  • [ ] 备份状态监控
  • [ ] 磁盘空间监控
  • [ ] 备份大小异常告警

🎉 总结

备份的重要性:

"数据只有两份:一份在用的,一份丢失的。" —— Murphy's Law

建立备份系统,就是买保险:

  1. 平时看不见 → 但一定要有
  2. 出事救命 → 关键时刻能救命
  3. 定期检查 → 确保能正常恢复
  4. 多地存储 → 防止单点故障

记住:备份不是为了让你爽,而是为了让你在灾难面前不慌!😌


🔗 相关资源


系列完结! 🎊

你已经学会了: 1. ✅ Flask 博客部署 2. ✅ Gunicorn 性能优化 3. ✅ FRP 内网穿透 4. ✅ Nginx 优化 5. ✅ HTTPS 安全 6. ✅ 日志监控 7. ✅ 自动化备份

你的博客现在专业、稳定、安全! 🚀

评论 (0)

暂无评论,快来抢沙发吧~

发表评论