自托管服务数据安全实战:Shell 脚本 + Crontab 定时备份 + 异地同步方案
前言
前面两篇文章分别分享了 ECS 的运维优化和 Docker + Nginx 的部署实践,服务跑起来之后,一个更关键的问题摆在面前:数据安全。
我在自己的服务器上托管了多个应用(如图书管理等),业务数据全部挂载在宿主机目录上。一旦出现以下任何一种情况,后果都不堪设想:
- 磁盘损坏或文件系统故障;
- 误操作删除了数据目录;
- 应用升级失败导致数据损坏;
- 服务器被入侵或遭遇勒索软件。
而容器本身是「易失」的——镜像可以随时重新拉取,配置可以重新编写,唯独数据丢了就是真丢了。所以这套备份方案的原则很朴素:自动化、有保留周期、异地容灾。
一、方案设计
整体思路如下:
数据目录 → tar 打包(带日期命名)→ 本地保留目录
↓
Crontab 每日定时调度 ↓
↓
自动清理 N 天前的旧备份 → rsync 异地同步
设计要点:
- 每日全量打包:个人应用数据量通常在几百 MB 到几 GB,每日全量备份简单可靠,不必引入增量备份的复杂度;
- 保留周期:本地保留 7 天,异地保留 30 天,自动清理,避免备份本身把磁盘撑爆;
- 备份期间不停服:对挂载目录直接打包,应用持续写入的理论风险用「低峰期执行」来规避(凌晨 3 点);
- 失败可感知:脚本记录日志,备份异常时输出到日志文件,便于排查。
二、备份脚本编写
创建脚本 /opt/scripts/backup.sh:
#!/bin/bash
# ===== 配置区 =====
BACKUP_SRC="/your/data/path" # 需要备份的数据目录
BACKUP_DST="/opt/backups" # 本地备份存放目录
KEEP_DAYS=7 # 本地保留天数
LOG_FILE="/opt/scripts/backup.log" # 日志文件
DATE=$(date +%Y-%m-%d_%H-%M-%S)
FILENAME="backup_${DATE}.tar.gz"
# ===== 开始备份 =====
echo "[$(date '+%F %T')] 备份开始: ${BACKUP_SRC}" >> ${LOG_FILE}
mkdir -p ${BACKUP_DST}
tar -czf ${BACKUP_DST}/${FILENAME} -C "$(dirname ${BACKUP_SRC})" "$(basename ${BACKUP_SRC})"
if [ $? -eq 0 ]; then
SIZE=$(du -h ${BACKUP_DST}/${FILENAME} | awk '{print $1}')
echo "[$(date '+%F %T')] 备份成功: ${FILENAME} (大小: ${SIZE})" >> ${LOG_FILE}
else
echo "[$(date '+%F %T')] 备份失败!" >> ${LOG_FILE}
exit 1
fi
# ===== 清理过期备份 =====
find ${BACKUP_DST} -name "backup_*.tar.gz" -mtime +${KEEP_DAYS} -delete
echo "[$(date '+%F %T')] 已清理 ${KEEP_DAYS} 天前的旧备份" >> ${LOG_FILE}
赋予执行权限并手动跑一次验证:
chmod +x /opt/scripts/backup.sh
/opt/scripts/backup.sh
cat /opt/scripts/backup.log
几个细节说明:
tar -C参数:先切换到父目录再打包,这样解压出来的是目录本身而不是一长串绝对路径,恢复时更干净;-mtime +7 -delete:find一行搞定过期清理,比手写循环靠谱;- 每步操作都写日志,出问题时可以快速定位是打包失败还是清理失败。
三、Crontab 定时调度
编辑 root 用户的定时任务:
crontab -e
添加一行,每天凌晨 3 点执行:
0 3 * * * /opt/scripts/backup.sh >> /opt/scripts/cron.log 2>&1
保存后确认任务已生效:
crontab -l
踩坑提醒:如果你在脚本里用了
docker命令(比如先docker stop再备份),crontab 环境下的 PATH 和登录 shell 不同,经常出现「手动能跑、定时就失败」的问题。解决办法是在 crontab 顶部补一行PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin,或者脚本内全部使用绝对路径。
四、rsync 异地同步
本地备份只能防误删,防不了整盘故障。rsync 把备份同步到另一台服务器(或同厂商不同可用区的机器),才算真正的容灾。
4.1 配置免密登录
ssh-keygen -t ed25519
ssh-copy-id user@备份服务器IP
4.2 同步脚本
在 backup.sh 末尾追加同步逻辑,或单独写一个 sync_backup.sh:
#!/bin/bash
REMOTE="user@备份服务器IP:/opt/remote_backups/"
LOCAL="/opt/backups/"
rsync -avz --delete ${LOCAL} ${REMOTE}
if [ $? -eq 0 ]; then
echo "[$(date '+%F %T')] 异地同步成功" >> /opt/scripts/backup.log
else
echo "[$(date '+%F %T')] 异地同步失败,请检查网络与凭据" >> /opt/scripts/backup.log
fi
-a:归档模式,保留权限、时间戳等属性;-z:传输时压缩,节省带宽;--delete:让远端与本地保持一致(本地已清理的旧备份远端也删掉,远端保留周期可按需在远端再配置一层)。
同样加入 crontab,错开几分钟执行:
10 3 * * * /opt/scripts/sync_backup.sh >> /opt/scripts/cron.log 2>&1
4.3 验证恢复能力
**备份不演练等于没备份。**找一次空闲时间,把备份包在临时目录解压,确认数据完整:
mkdir /tmp/restore_test
tar -xzf /opt/backups/backup_xxx.tar.gz -C /tmp/restore_test
diff -r /tmp/restore_test/your_data /your/data/path
能完整恢复,这套方案才算闭环。
五、进阶优化方向
这套方案够用之后,还可以按需演进:
- 对象存储归档:如果有华为云 OBS 等对象存储,可用
obsutil或 S3 兼容工具把备份再推一份到云端,进一步降低成本(冷存储价格很低); - 备份加密:数据敏感的话,打包后用
openssl enc -aes-256-cbc加密,密钥与备份分开存放; - 通知告警:脚本结尾接一个 webhook(如企业微信机器人),备份失败时主动推送,而不是被动翻日志;
- 数据库场景:如果应用用了 MySQL/PostgreSQL,不能直接 tar 数据目录,应改用
mysqldump/pg_dump逻辑导出后再打包。
六、总结
回顾整套方案:
- 一个脚本:Shell 打包 + 自动清理,几十行代码;
- 一个定时任务:Crontab 每日调度,全自动无人值守;
- 一次异地同步:rsync 到备用服务器,实现双重容灾。
自托管的最大风险不是服务挂了(重启就能恢复),而是数据没了。花半小时搭好这套备份体系,之后每天凌晨自动运行,是性价比最高的运维投入。
后续我还会分享自托管服务的监控告警和应用升级回滚实践,欢迎关注交流。
- 点赞
- 收藏
- 关注作者
评论(0)