我在OBS做异地备份踩过的三个坑
3月第二周我把生产 MySQL 备份从 ECS 数据盘迁到 OBS。机器是华北-北京四的 ECS s7n.xlarge.2,4 vCPU、8 GiB 内存,Ubuntu 22.04.4 LTS,MySQL 8.0.32,xtrabackup 8.0.35,obsutil 5.5.12。系统盘 40 GiB,数据盘 300 GiB。业务白天写入平稳,日增量 6 到 9 GiB,周日全量在 38 GiB 左右。预算卡在每月 120 元,超过就 webhook 发企业微信群。
备份脚本上线当天到三次返工
上线时我只配了一条 cron,1:10 开跑,全量压缩后直接上云。审计给我的话是“备份完成即上云”,我照做了,本地留存没有做。
10 1 * * * /usr/local/bin/mysql_backup_to_obs.sh >> /var/log/mysql_backup.log 2>&1
# /usr/local/bin/mysql_backup_to_obs.sh
DATE=$(date +%F)
BASE=/data/backup
xtrabackup --backup --target-dir=${BASE}/full-${DATE} \
--user=backup --password='***' --parallel=4 --compress=0
tar -I 'pigz -4' -cf ${BASE}/full-${DATE}.tar.gz ${BASE}/full-${DATE}
obsutil cp ${BASE}/full-${DATE}.tar.gz obs://db-bak-prod/MySQL/prod/ \
-p=8 --threshold=104857600 --sse-kms=true
第一天完整跑完用了 58 分钟,xtrabackup 31 分钟,压缩 12 分钟,上传 14 分钟。日志里这几行我后来才意识到是 KMS 权限检查前奏。
[2026-03-10 01:41:03] xtrabackup: Transaction log of lsn (32513422744) to (32513422744) was copied.
[2026-03-10 01:53:22] Start to upload /data/backup/full-2026-03-10.tar.gz with SSE-KMS
这个方案我一开始是拒绝的。
第一处返工在 IAM。3 月 11 日 01:32 开始刷 403,日志连续 219 行同类错误。我先怀疑是 AK 失效,跑 obsutil ls obs://db-bak-prod -limit=1 能列目录,说明凭证没问题,问题只在 SSE-KMS 上传。
[2026-03-11 01:32:18] Uploading file /data/backup/full-2026-03-11.tar.gz
Error: Put object failed, StatusCode=403, ErrorCode=AccessDenied
Error: kms encrypt failed, code=KMS.0303, message=User is not authorized to perform kms:GenerateDataKey
账号是 IAM 用户 backup-uploader,挂在 db-backup-group。原策略只有 obs:PutObject、obs:ListBucket、obs:AbortMultipartUpload。我补了 kms:Encrypt 和 kms:GenerateDataKey,Resource 先放成 *,先救火,再收口。
{
"Action": ["kms:Encrypt","kms:GenerateDataKey"],
"Effect": "Allow",
"Resource": "*"
}
定位加改策略一共 47 分钟,01:59 重试通过,38.4 GiB 全量包上传 13 分 22 秒,obsutil 统计平均 49.1 MiB/s。我还做过一次失败试探,把参数改成 --sse-kms=false 想先上传,命令确实成功,但对象加密方式变成 SSE-OBS,和审计要求冲突,我删掉对象后重传。
第二处返工是生命周期前缀写错。我在规则里填了 mysql/prod/,对象真实路径是 MySQL/prod/,OBS 前缀大小写敏感,16 天都没命中,标准存储涨到 1.74 TiB。我是用下面这条命令把问题看明白的。
obsutil ls obs://db-bak-prod -s | grep -E 'MySQL|mysql'
那天我盯着账单页面沉默了十分钟。
3 月账单里标准存储容量费 146.82 元、请求费 8.37 元、跨区复制流量费 31.44 元,总计 186.63 元,超预算 66.63 元。我原本想偷懒用 obsutil mv obs://db-bak-prod/MySQL/prod/ obs://db-bak-prod/mysql/prod/ -r 一步改名,直接报 Error: mv for prefix is not supported。这次尝试失败后只能 copy+delete。命令跑了 2 小时 6 分钟,搬了 5127 个对象,删除又花 19 分钟。我把脚本里前缀写成常量 OBS_PREFIX='mysql/prod/',不再手打路径。
第三处返工在跨区复制和 KMS 组合上。源桶在北京四,目标桶 db-bak-dr 在广州,两个桶都开了 SSE-KMS。控制台显示规则“已启用”,我以为结束了,第二天抽查新对象 replication status 是 FAILED,错误是 KMS.0303 User is not authorized to perform kms:Encrypt on destination key。根因是复制委托只拿到了源桶读权限,目标 key policy 没放行委托主体 replication-obs@iam。我在目标 KMS key policy 新增 kms:Encrypt 和 kms:GenerateDataKey,10 分钟后状态从 FAILED 变成 PENDING,再到 COMPLETED。积压对象 4863 个,补齐 3 小时 26 分钟。规则创建前上传的对象不会自动补复制,我又手工触发了一次“存量对象复制”,这一步等了 1 小时 12 分钟。
现在的配置、花费和还没收口的点
现在我把脚本拆成全量和增量两个 cron。周日 01:10 跑全量,周一到周六 01:10 跑增量。全量参数从 --parallel=4 改到 --parallel=6,因为机器 CPU 还有余量,备份阶段从 31 分钟降到 24 到 26 分钟;压缩从 pigz -4 调到 -3,包体积增加约 3.8%,但 CPU 峰值从 92% 降到 74%,凌晨 ETL 冲突少了。增量链路用 --incremental-basedir 指向上一份目录,我加了目录存在性检查,断链时直接退出并报警。
10 1 * * 0 /usr/local/bin/mysql_full_to_obs.sh
10 1 * * 1-6 /usr/local/bin/mysql_incr_to_obs.sh
xtrabackup --backup --target-dir=${BASE}/incr-${DATE} \
--incremental-basedir=${BASE}/${LAST_POINT} \
--parallel=6 --user=backup --password='***'
生命周期现在是第 8 天转低频,第 31 天转归档,前缀统一成 mysql/prod/。源桶和目标桶都开版本控制,复制规则也只匹配这个前缀,避免别的目录被复制。恢复演练还是每月两次,拉回 ECS c7.large.2(2 vCPU,4 GiB),最近四次是 29、34、41、33 分钟,慢的一次日志显示低频取回排队 6 分钟。
费用这两个月平均 92.4 元,4 月 89.7 元,5 月 95.1 元,波动主要在跨区复制流量,日增量高的那几天会多 1 到 2 元。归档对象恢复时延我只测过一次,6.2 GiB 文件取回用了 51 分钟,这个样本太少。我已经排了 10 次抽样,准备把取回耗时和业务 RTO 45 分钟放在同一张表里,再决定第 31 天转归档要不要改到第 45 天。
- 点赞
- 收藏
- 关注作者
评论(0)