OBS跨区日志备份的两次返工

举报
yd_272744321 发表于 2026/10/22 10:29:20 2026/10/22
【摘要】 我们把生产 ECS 的应用日志改成 OBS 备份并开启跨区复制,落地时连踩了 KMS 加密权限和目标桶策略两个坑。文里写了具体报错、脚本、生命周期规则、恢复耗时和账单变化,方便团队复盘这次返工。

触发点和环境

9 月 3 日 01:17,我被 CES 告警电话吵醒,指标是 disk_usedPercent,实例 ecs-order-prod-04 的 /var/log 到了 96.4%。这台机在华北-北京四,规格 s6.large.2,2 vCPU / 4 GiB,系统盘 40GB,数据盘 500GB,EulerOS 2.0,内核 4.19.90,JDK 是 17.0.8。01:28 我登录值班机跑 df -h,看到 /dev/vda1 40G 38G 1.1G 98% /,再跑 du -sh /var/log/* | sort -h,/var/log/order-api 单目录就 29G。01:39 开始出现业务报错,日志里最刺眼的一段是 java.io.FileNotFoundException: /var/log/order-api/gc.log (No space left on device),同一分钟还有 [error][logging] Error opening log file '/var/log/order-api/gc.log': No space left on device。journalctl -u order-api -n 50 里能看到 order-api.service: Scheduled restart job, restart counter is at 3,重启间隔 5 秒,第四次才稳住。

我把‘日志留在本机七天再手工清理’这个习惯废掉了。

故障单里我把改造边界写得很死,后来也没改字:日志每天备份到 OBS,保留 210 天,跨区留一份,抽样恢复 100GB 的耗时控制在 30 分钟内。210 天不是拍脑袋,是审计同事给的最小留存窗口。源桶 prod-order-log-cn-north-4,目标桶 prod-order-log-cn-east-3,工具版本 obsutil 5.5.12 linux_amd64。

上线脚本和初始配置

这个方案我一开始是拒绝的。

我先试了打包上传,命令是 tar -I 'zstd -3' -cf /tmp/order-log-$(date +%F).tar.zst /var/log/order-api,zstd 版本 1.5.5,压缩阶段 CPU 常驻 170% 到 190%,跑了 28 分钟,得到 126GB 单包。上传命令 obsutil cp /tmp/order-log-2024-09-03.tar.zst obs://prod-order-log-cn-north-4/raw/ -f -j=8 -p=8,41 分钟传完,看起来挺顺。演练恢复时问题全暴露了,我只想取 3GB 的 Nginx access 日志,却被迫下载整包,下载 18 分钟,解压 22 分钟,临时盘剩余空间从 34GB 掉到 6GB,差点把演练机也打满。

我还做过一次失败尝试,把单包拆成 8GB 分片:split -b 8G -d order-log.tar.zst part- 再并行上传 16 线程。这个办法没有解决随机恢复,查一段日志还是得把所有分片拉回来重组,恢复路径更长,脚本复杂度也上去了,当天 17:40 我把这套脚本删掉,回到按文件同步。

第一次可用脚本在 9 月 4 日 02:15 上线,第一次全量 812GB,耗时 3 小时 42 分,obsutil 统计平均 62.1MB/s,峰值带宽约 680Mbps。这里我返工了三处。第一处是目录日期,我原来写 DAY=$(date +%F),会把 02:10 后新切出来的当天目录混进去,跟 logrotate 节奏冲突;改成 date -d 'yesterday' +%F 后只同步前一天目录。第二处是定时,我把 crontab 从 02:00 改成 02:15,给 logrotate 留 5 分钟缓冲,避免切分没完成就上传。第三处是并发和分片阈值,我一开始用 -j=32 -p=32 --threshold=104857600,CPU 打到 95%,业务 RT 从 38ms 抬到 91ms,持续 7 分钟;回滚成 -j=16 -p=16,再把阈值改成 50MB,60 到 90MB 这批文件的重传次数从每晚 12 到 15 次降到 3 次以内。

#!/bin/bash
set -euo pipefail
DAY=$(date -d 'yesterday' +%F)
SRC=/var/log/order-api/${DAY}
DST=obs://prod-order-log-cn-north-4/dt=${DAY}
for i in 1 2 3; do
  /opt/obsutil/obsutil sync ${SRC} ${DST} -u -j=16 -p=16 --threshold=52428800 --timeOut=300 || RC=$?
  if [ ${RC:-0} -eq 0 ]; then break; fi
  sleep 60
  unset RC
done
/opt/obsutil/obsutil ls ${DST} -s
15 2 * * * nice -n 10 ionice -c2 -n7 /usr/local/bin/obs_backup.sh | logger -t obs-backup
10 2 * * * /usr/sbin/logrotate -f /etc/logrotate.d/order-api

桶上的配置我做了 SSE-KMS、版本控制、生命周期,规则是 7 天转低频,45 天转归档,210 天删除。拖我时间最多的是 KMS 权限,不是脚本。

两个权限坑

第一处是 KMS 授权。我给 ECS 实例委托只放了 obs:PutObject 和 obs:ListBucket,脚本立刻 403。obsutil 日志里原样是 Error [PutObject], status [403], code [KMS.0303], message [User is not authorized to perform kms:Encrypt], request id [7C2F1B6A1E6E0F7F3B1A9D29]。我当时误判成桶 ACL,拿着 obsutil stat obs://prod-order-log-cn-north-4 -v 和控制台策略来回改了 40 分钟,完全没效果。根因是 CMK 3c1d9f2a-6a2b-4f3e-9a1c-2d77f1bb8210 没放行实例委托,我补上 kms:Encrypt 和 kms:GenerateDataKey,3 分钟后生效,任务恢复。这个坑从发现到修完用了 2 小时 15 分。

Statement:
  - Effect: Allow
    Action:
      - obs:PutObject
      - obs:ListBucket
      - kms:Encrypt
      - kms:GenerateDataKey
    Resource:
      - obs:*:*:bucket:prod-order-log-cn-north-4
      - kms:*:*:key/3c1d9f2a-6a2b-4f3e-9a1c-2d77f1bb8210

第二处是跨区复制。控制台任务一直 FAILED,事件详情写的是 Task crr-8f27b1 failed, reason: AccessDenied, action obs:ReplicateObject denied by destination bucket policy, request id [0000018B3A5F0A1D4E95C8F2]。目标桶策略只给了 obs:PutObject,没给 obs:ReplicateObject 和 obs:ReplicateDelete。我补完策略后 10 分钟就看到新对象开始复制,延迟稳定在 6 到 14 分钟。旧对象补复制跑了 9 小时 28 分才追平,期间两次 503 SlowDown,日志分别是 11:23 和 14:07,靠脚本 60 秒重试顶过去。

生命周期和账单

账单我拿了改造前后两个月对比。改造前主要靠 EVS 留日志,500GB 通用型 SSD 月费用 268.00 元,峰值还加过 200GB 临时盘,当月合计 372.00 元。改造后 10 月账单拆开是 OBS 标准存储 640GB 89.6 元,低频 1.02TB 73.4 元,归档 430GB 25.1 元,跨区复制流量 296GB 44.4 元,请求次数 11.9 元,本地日志盘缩到 200GB 96.0 元,合计 340.4 元。省了 31.6 元,不夸张,但磁盘告警从每周 2 到 3 次降到 0 次,这个变化我每天都能感受到。

恢复测试我做了三轮,目标机是 c6.large.2,2 vCPU / 4 GiB,系统盘 80GB。拉取 120GB 命令是 obsutil cp obs://prod-order-log-cn-north-4/dt=2024-09-06 /data/restore -f -r -j=8 -p=8,三次耗时 26 分 41 秒、27 分 08 秒、27 分 33 秒,平均吞吐 75MB/s。归档层我用 obsutil restore obs://prod-order-log-cn-north-4/dt=2024-08-01 -f -r -days=3 -tier=Standard 触发解冻,同体量最短 2 小时 53 分,最长 3 小时 21 分,解冻请求费用多了 7.2 元。这个速度在故障排查窗口 1 小时的场景里还是不够。

还没做完的部分

现在回头看,脚本依然偏手工,失败重试和校验都不完整。我现在只做了每天 1% 文件的 md5 抽样,命令是 find /var/log/order-api/$(date -d 'yesterday' +%F) -type f | shuf -n 200 | xargs md5sum,再和 obsutil hash 对比;200 个样本平均要跑 4 分 30 秒,全量校验估算在 58 分钟左右,晚上跑会把 2 核 CPU 顶满,业务 RT 会抬高 12 到 18ms,所以我还没上。

我还没把归档解冻自动化。

下周我准备试一版 FunctionGraph 定时触发,cron 设成每天 01:40,提前解冻最近 14 天目录,保留 72 小时热状态,再跑一次 100GB 恢复演练。我也会把 -j 调到 12 做对照,看能不能把恢复时间压进 30 分钟,同时把业务 RT 波动控制在 5ms 以内。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。