华为云 ECS 上用 crontab + obsutil 做定时备份:从脚本到过期清理的完整实践
服务器上的数据只放在一块云硬盘里,总归不太踏实。误删文件、程序写坏数据、系统盘出问题,哪一个碰上都够折腾半天。云硬盘快照当然能用,但如果只想把网站目录和数据库导出文件每天存一份,用 crontab 加 OBS 对象存储就够了,成本低,恢复起来也直观。下面是我在一台 CentOS/EulerOS 类 ECS 上实际搭的流程,Ubuntu 上命令基本一样。
一、准备工作:桶和访问密钥
- 在 OBS 控制台创建一个桶,区域尽量和 ECS 保持一致。同区域走内网,速度快,也不产生公网流量费。存储类别选标准或低频访问都可以,备份文件读得少,低频访问更划算。
- 不建议直接用主账号的 AK/SK。在 IAM 里新建一个子用户,只授予这个桶的读写权限,再给它生成访问密钥。万一密钥泄露,影响范围也有限。
二、安装并配置 obsutil
obsutil 是华为云官方的 OBS 命令行工具,单个二进制文件,下载解压就能用:
cd /opt
wget https://obs-community.obs.cn-north-1.myhuaweicloud.com/obsutil/current/obsutil_linux_amd64.tar.gz
tar -xzvf obsutil_linux_amd64.tar.gz
cd obsutil_linux_amd64_*
chmod 755 obsutil
ln -s "$(pwd)/obsutil" /usr/local/bin/obsutil
配置密钥时推荐用交互模式,避免 AK/SK 留在 shell 历史里:
obsutil config -interactive
按提示填 AK、SK 和 Endpoint,例如华北-北京四是 obs.cn-north-4.myhuaweicloud.com。配置会写入当前用户的 ~/.obsutilconfig,记得 chmod 600 ~/.obsutilconfig。配好后先测一下:
obsutil ls obs://my-backup-bucket
能列出桶内容(空桶也会正常返回),说明连通性和权限都没问题。
三、写备份脚本
假设要备份 /var/www 网站目录和一个 MySQL 库。新建 /opt/backup/backup.sh:
#!/bin/bash
set -euo pipefail
DATE=$(date +%Y%m%d_%H%M)
WORKDIR=/data/backup
BUCKET=obs://my-backup-bucket/$(hostname)
OBSUTIL=/usr/local/bin/obsutil
mkdir -p "$WORKDIR"
cd "$WORKDIR"
# 1. 打包网站目录
tar -czf "www_${DATE}.tar.gz" -C /var www
# 2. 导出数据库(账号密码放在 /root/.my.cnf,权限 600)
mysqldump --single-transaction --routines mydb | gzip > "mydb_${DATE}.sql.gz"
# 3. 上传到 OBS
$OBSUTIL cp "www_${DATE}.tar.gz" "${BUCKET}/www/"
$OBSUTIL cp "mydb_${DATE}.sql.gz" "${BUCKET}/db/"
# 4. 清理 7 天前的本地文件
find "$WORKDIR" -name "*.gz" -mtime +7 -delete
echo "$(date '+%F %T') backup ok: ${DATE}"
几点说明:
set -euo pipefail让任何一步失败时脚本立刻退出,不会出现“打包失败了还在上传空文件”的情况。mysqldump --single-transaction对 InnoDB 表做一致性导出,不锁表,适合线上库。密码写在/root/.my.cnf的[client]段里,比直接写进脚本安全。- 本地只留 7 天,云上的保留策略交给 OBS 处理,见第五节。
加执行权限后先手动跑一遍:
chmod +x /opt/backup/backup.sh
/opt/backup/backup.sh
obsutil ls obs://my-backup-bucket/$(hostname)/ -limit=20
四、交给 crontab 定时执行
crontab -e 添加一行,每天凌晨 3 点 15 分执行:
15 3 * * * /usr/bin/flock -n /tmp/backup.lock /opt/backup/backup.sh >> /var/log/backup.log 2>&1
五个字段依次是:分、时、日、月、星期。这里用了 flock -n:上一次备份还没跑完时,新任务直接跳过,避免两个进程同时打包、互相抢磁盘。输出追加到 /var/log/backup.log,出问题时先看这个文件。
日志会越写越大,可以加一个 logrotate 配置 /etc/logrotate.d/backup:
/var/log/backup.log {
weekly
rotate 4
compress
missingok
notifempty
}
五、云上的过期清理:用生命周期规则
OBS 上的旧备份不需要自己写脚本删。在桶的“生命周期规则”里新建一条规则,前缀填对应的主机名目录,设置对象在 30 天后过期删除即可。如果对成本更敏感,还可以设置 N 天后转为归档存储,再过一段时间删除。规则由 OBS 后台执行,生效会有一定延迟,不用盯着当天的结果。
六、失败了怎么知道
定时任务最怕悄悄失败。备份脚本连续停了一个月,直到要恢复数据时才发现,这种事并不少见。比较省事的做法有两种:
一是在脚本末尾成功时写一个时间戳文件,再用另一个每天运行的小检查脚本判断这个文件是不是超过 26 小时没更新,超过就发告警。告警渠道可以用企业微信、钉钉机器人的 Webhook,或者服务器上已有的邮件工具。
二是定期查看桶里的最新对象时间。比如每周一抽空执行一次 obsutil ls,确认最近几天的文件都在、大小正常。数据库导出文件如果突然只有几 KB,通常说明导出出错了,只上传了一个空的压缩包。
不管用哪种方式,关键是让“没备份成功”这件事能被人看到,而不是只靠日志文件躺在磁盘上。
七、常见坑
- cron 环境变量很少:cron 里的 PATH 通常只有
/usr/bin:/bin,手动能跑、定时不跑,十有八九是这个原因。脚本里一律用绝对路径,或在开头显式设置 PATH。 %在 crontab 里是特殊字符:直接在 crontab 行里写date +%F会被截断,需要写成\%。把逻辑放进脚本里就能避开这个问题。- obsutil 配置文件找不到:配置跟着用户走。如果用 root 的 crontab,就要在 root 下执行
obsutil config,否则会报鉴权失败。 - 时区不对:部分镜像默认是 UTC,“凌晨 3 点”实际变成北京时间 11 点。用
timedatectl确认,必要时执行timedatectl set-timezone Asia/Shanghai,再重启 crond。 - 只备份、不恢复:建议每个月挑一份备份下载下来,实际解压、导入一次:
obsutil cp obs://my-backup-bucket/$(hostname)/db/mydb_xxx.sql.gz /tmp/
gunzip -c /tmp/mydb_xxx.sql.gz | mysql mydb_restore_test
能恢复出来的备份才算真正的备份。
小结
整套方案就是:脚本负责打包和上传,crontab 负责定时,flock 防止任务重叠,日志用来排查问题,OBS 生命周期规则负责清理旧备份。全部搭完大概半小时,之后基本不用再管。再配上每月一次的恢复演练,小型站点的数据安全就有了一个比较稳的底线。
- 点赞
- 收藏
- 关注作者
评论(0)