服务器自动备份方案:用 rsync + cron 实现异地增量备份

服务器自动备份方案:用 rsync + cron 实现异地增量备份

服务器数据丢失是小概率但高代价的事故。硬盘故障、误操作 rm -rf、系统被黑客清空......这些情况听起来离你很远,但真发生了就是几个月甚至几年的工作付之一炬。备份不是"有了更好",是"没有迟早后悔"。

这篇文章介绍一套实用的自动备份方案:用 rsync 做增量同步,用 cron 定时触发,把数据异地备份到另一台服务器。整个方案不依赖第三方服务,成本低,可控性强。

备份策略设计

先想清楚要备份什么、备份多久、保留多少份:

备份类型 频率 保留时长
数据库 每天一次 保留 30 天
应用配置 每天一次 保留 30 天
用户上传文件 每天一次 保留 7 天
完整系统快照 每周一次 保留 4 周

最低成本的异地备份方案是再准备一台小服务器专门用来存备份。我在 雨云服务器 rainyun.com 开了一台 1 核 1G、50GB 硬盘的机器专门放备份数据,按月付,价格很低。雨云的性价比在这种场景下特别合适------备份机不需要高性能,只需要存储空间大、网络稳定。新用户注册填优惠码 2026off 可以领 5 折优惠券,一台备份机的月费低到可以忽略不计,但能给你的主服务器数据提供有效保障。

准备工作

主服务器(被备份的那台)

创建专用备份用户:

bash 复制代码
adduser backup --disabled-password --gecos ""

备份服务器

同样创建对应用户:

bash 复制代码
adduser backup --disabled-password --gecos ""
mkdir -p /backup/server-main
chown backup:backup /backup/server-main

配置 SSH 免密登录

在主服务器上,以 backup 用户身份生成 SSH 密钥:

bash 复制代码
su - backup
ssh-keygen -t ed25519 -C "backup@main-server" -N "" -f ~/.ssh/id_backup

把公钥复制到备份服务器:

bash 复制代码
ssh-copy-id -i ~/.ssh/id_backup.pub backup@备份服务器IP

测试连接:

bash 复制代码
ssh -i ~/.ssh/id_backup backup@备份服务器IP echo "连接成功"

编写备份脚本

创建数据库备份脚本:

bash 复制代码
sudo mkdir -p /opt/backup/scripts
sudo tee /opt/backup/scripts/backup-mysql.sh << 'EOF'
#!/bin/bash

DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/tmp/db_backup"
REMOTE_HOST="备份服务器IP"
REMOTE_DIR="/backup/server-main/databases"
KEEP_DAYS=30

mkdir -p $BACKUP_DIR

# 备份所有数据库(排除系统库)
DATABASES=$(mysql -u root -p"$MYSQL_PASS" -e "SHOW DATABASES;" 2>/dev/null | grep -Ev "(Database|information_schema|performance_schema|sys|mysql)")

for DB in $DATABASES; do
    echo "备份数据库: $DB"
    mysqldump -u root -p"$MYSQL_PASS" \
        --single-transaction \
        --routines \
        --triggers \
        "$DB" 2>/dev/null | gzip > "$BACKUP_DIR/${DB}_${DATE}.sql.gz"
done

# rsync 到备份服务器
rsync -avz \
    -e "ssh -i /home/backup/.ssh/id_backup -o StrictHostKeyChecking=no" \
    "$BACKUP_DIR/" \
    "backup@${REMOTE_HOST}:${REMOTE_DIR}/"

# 清理本地临时文件
rm -rf $BACKUP_DIR

# 删除备份服务器上 30 天前的文件
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
    "find ${REMOTE_DIR}/ -name '*.sql.gz' -mtime +${KEEP_DAYS} -delete"

echo "数据库备份完成: $DATE"
EOF

sudo chmod +x /opt/backup/scripts/backup-mysql.sh

创建文件备份脚本:

bash 复制代码
sudo tee /opt/backup/scripts/backup-files.sh << 'EOF'
#!/bin/bash

REMOTE_HOST="备份服务器IP"
REMOTE_DIR="/backup/server-main/files"
LOG_FILE="/var/log/backup-files.log"

echo "=== 备份开始: $(date) ===" >> $LOG_FILE

# 需要备份的目录列表
BACKUP_DIRS=(
    "/etc"
    "/opt/myapp"
    "/var/www"
    "/home"
)

for DIR in "${BACKUP_DIRS[@]}"; do
    if [ -d "$DIR" ]; then
        echo "同步目录: $DIR" >> $LOG_FILE
        rsync -avz \
            --delete \
            --exclude="*.log" \
            --exclude="*.tmp" \
            --exclude="node_modules" \
            --exclude=".git" \
            -e "ssh -i /home/backup/.ssh/id_backup -o StrictHostKeyChecking=no" \
            "$DIR" \
            "backup@${REMOTE_HOST}:${REMOTE_DIR}/" \
            >> $LOG_FILE 2>&1
    fi
done

echo "=== 备份结束: $(date) ===" >> $LOG_FILE
EOF

sudo chmod +x /opt/backup/scripts/backup-files.sh

配置 cron 定时任务

以 root 身份编辑 crontab:

bash 复制代码
sudo crontab -e

添加以下任务:

bash 复制代码
# 每天凌晨 2 点备份数据库
0 2 * * * MYSQL_PASS="你的MySQL密码" /opt/backup/scripts/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1

# 每天凌晨 3 点备份文件
0 3 * * * /opt/backup/scripts/backup-files.sh

# 每周日凌晨 4 点做一次完整打包(可选)
0 4 * * 0 /opt/backup/scripts/backup-weekly.sh

验证备份效果

手动运行一次脚本测试:

bash 复制代码
sudo MYSQL_PASS="你的密码" /opt/backup/scripts/backup-mysql.sh
sudo /opt/backup/scripts/backup-files.sh

登录备份服务器确认文件是否到位:

bash 复制代码
ssh backup@备份服务器IP ls -lh /backup/server-main/databases/

检查日志:

bash 复制代码
tail -50 /var/log/backup-mysql.log
tail -50 /var/log/backup-files.log

进阶:带保留策略的增量备份

rsync 加上 --link-dest 参数可以实现"快照式"备份------每次都是完整备份的样子,但实际上用硬链接节省空间,没变化的文件不占额外存储。

bash 复制代码
#!/bin/bash

DATE=$(date +%Y%m%d)
REMOTE_HOST="备份服务器IP"
SNAPSHOT_DIR="/backup/server-main/snapshots"
LATEST_LINK="${SNAPSHOT_DIR}/latest"

ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
    "mkdir -p ${SNAPSHOT_DIR}/${DATE}"

rsync -avz \
    --delete \
    --link-dest="${LATEST_LINK}" \
    -e "ssh -i /home/backup/.ssh/id_backup" \
    /opt/myapp/ \
    "backup@${REMOTE_HOST}:${SNAPSHOT_DIR}/${DATE}/"

# 更新 latest 软链接
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
    "ln -sfn ${SNAPSHOT_DIR}/${DATE} ${LATEST_LINK}"

# 删除 7 天前的快照
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
    "find ${SNAPSHOT_DIR} -maxdepth 1 -name '20*' -mtime +7 -exec rm -rf {} \;"

这样每天的快照独立存在,可以回滚到任意一天的状态,实际占用空间只比单次备份多一点点。

备份监控(可选)

建议加一个简单的备份成功通知。最简单的方式是在脚本末尾发送邮件:

bash 复制代码
# 安装发件工具
sudo apt install -y msmtp msmtp-mta

# 在脚本末尾加
echo "备份完成 $(date)" | mail -s "服务器备份报告" 你的邮箱@example.com

或者用 curl 发 webhook 到你的通知渠道(Bark、钉钉、企业微信等):

bash 复制代码
curl -s "https://api.day.app/你的Key/备份成功/$(date +%Y%m%d)"

异地备份搭起来之后基本是"无感"运行的。定期检查一下日志和备份服务器的文件大小,确认备份在正常进行。真正到需要用的那天,有备份和没备份的差别就是能不能快速恢复。

相关推荐
Avan_菜菜9 小时前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB1 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
两个人的幸福3 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
zzzzzz3103 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode3 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220703 天前
如何搭建本地yum源(上)
运维
BingoGo5 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack5 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户3074596982076 天前
PHP 扩展——从入门到理解
php
鹏仔先生6 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php