我把下载站迁到OBS后连踩三坑
迁移当晚和 403 排查
8 月 12 日 20:03 我在值班群里发了“开始切流”,把补丁下载页从 ECS 挪到 OBS 静态网站托管。老机器是华为云 s6.large.2,2 vCPU / 4 GiB,40GB 高 I/O 系统盘,EIP 固定带宽 5Mbps,站点是 Vue 3.4.27 打包产物,dist 一共 24MB、187 个文件。过去三个月 PV 在 20 万到 23 万之间,7 月 ECS 账单 207.6 元,带宽单项 96 元,我就是想把固定支出压到更低,不然活动周我得盯着余额曲线看半天。
构建跑在 c6.large.2,EulerOS 2.0 SP11,Node.js 18.19.1,pnpm 8.15.6,obsutil 5.5.12。预演全流程 6 分 42 秒,真切流拖到 43 分钟。20:08 到 20:34 我一直怀疑工具链,先查 obsutil 版本兼容,再看 lockfileVersion=9 会不会和 pnpm 版本冲突,还把同一 commit 重打包两次。两次产物的 `sha256sum dist/assets/index-9f3a7.js` 完全一样,这时我才承认自己方向错了。
obsutil version
# obsutil version: 5.5.12
node -v # v18.19.1
pnpm -v # 8.15.6
obsutil mb obs://team-portal-prod -location=cn-north-4 -storageClass=STANDARD
obsutil chattri obs://team-portal-prod -website=index.html,error.html
obsutil sync ./dist obs://team-portal-prod/ -delete=false -f -j=8 -p=8
DNS 在 20:17 切到 `team-portal-prod.obs-website.cn-north-4.myhuaweicloud.com` 后,首页直接 403。我用公司出口、手机 4G、家宽都复现,排除了缓存。`curl -i` 的返回很干脆,`x-obs-request-id` 每次都变,但错误码稳定是 AccessDenied。
curl -i http://team-portal-prod.obs-website.cn-north-4.myhuaweicloud.com/
HTTP/1.1 403 Forbidden
x-obs-request-id: 00000191A7C57D6D7C1A2B8E7D3F11A0
Content-Type: application/xml
<Error>
<Code>AccessDenied</Code>
<Message>Access denied</Message>
</Error>
这个 403 完全是我自己写出来的。
桶 ACL 我设成 private 本来没问题,坑在策略 Action 写成了 `GetObjectAcl`,匿名请求自然读不到对象。我当时还做了个失败尝试,把 ACL 临时改成 `public-read`,安全同事 30 秒内就在群里让我改回去,这个来回多耗了 11 分钟。真正需要改的只有一行,把 Action 调整为 `GetObject`,其余配置不动。20:54 再次 `curl` 返回 200,页面 3 分钟内恢复。
# 错误策略
{
"Version": "1.1",
"Statement": [{
"Sid": "WrongAction",
"Effect": "Allow",
"Principal": {"ID":["*"]},
"Action": ["GetObjectAcl"],
"Resource": ["obs:team-portal-prod/*"]
}]
}
# 修正后
{
"Version": "1.1",
"Statement": [{
"Sid": "AllowPublicReadOnly",
"Effect": "Allow",
"Principal": {"ID":["*"]},
"Action": ["GetObject"],
"Resource": ["obs:team-portal-prod/*"]
}]
}
我那 26 分钟都在怀疑 npm 和 obsutil,回头看全是误判。
生命周期误删、跨区回填和脚本返工
第 8 天 09:43 前端同事在群里贴白屏,我打开控制台就看到 `GET https://static.example.com/assets/index-9f3a7.js 404`。我先查 CodeArts 任务 #842,状态 success,耗时 4m19s,上传步骤记录 `Total file count: 187`,构建链路看起来正常。再看桶事件,09:00 到 09:06 出现 417 条 `DeleteObject`,操作者是 `LifecycleService`,这时定位就很明确了,是生命周期规则删了线上对象。
GET https://static.example.com/assets/index-9f3a7.js 404 (Not Found)
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<RequestId>00000191B2A34E6CBB55A9A3D96D7783</RequestId>
</Error>
EventName=DeleteObject
UserIdentity=LifecycleService
Count(09:00-09:06)=417
这次真的是手抖。
我原本只想清理 `history/` 下 7 天前的旧包,结果 Prefix 留空,命中了全桶当前版本对象。那时没开版本控制,删掉后桶里没有可回滚版本。我先做了个无效补救,用本地最新 dist 回传:
obsutil sync ./dist obs://team-portal-prod/ -f -j=8 -p=8
# Succeed count: 187
# Failed count: 0
首页恢复了,历史补丁链接还是 404,因为 `history/20240805`、`history/20240809` 这些目录本地没有。可用方案是去 CodeArts 制品库回捞,我下载了 8 月 5 日、9 日、12 日三次归档包,每份约 280MB,下载 18 分钟,再用 `obsutil cp -u` 补传,避免覆盖同名新文件。恢复窗口是 09:51 到 12:07,总计 2 小时 16 分钟,客服侧收到 34 条下载失败工单。
obsutil cp ./artifacts/20240805/ obs://team-portal-prod/ -r -f -u -j=8 -p=8
obsutil cp ./artifacts/20240809/ obs://team-portal-prod/ -r -f -u -j=8 -p=8
obsutil cp ./artifacts/20240812/ obs://team-portal-prod/ -r -f -u -j=8 -p=8
同一天我把版本控制打开,把生命周期规则改成只匹配 `history/`,规则 JSON 收进仓库。现在改这个文件必须提 PR,CI 用 ajv 校验 schema,字段错一个就合不进去,我也把控制台手改权限从团队角色里收掉了。
CRR 也踩了坑。主桶在 cn-north-4,灾备桶在 cn-east-3,9 月演练时灾备桶少 312 个对象,缺的全是复制规则生效前的老文件。我还试过把复制规则 disable 再 enable,盼着触发一次全量回填,14 分钟过去对象数没变化,只能手工跑 backfill,11 分钟结束,`backfill.log` 里 success 312、failed 0,额外产生 1.74 元跨区复制流量费。
obsutil ls obs://team-portal-prod -s
# Total size of bucket is: 8.7 GB
# Total number is: 1342
obsutil ls obs://team-portal-dr -s
# Total size of bucket is: 6.9 GB
# Total number is: 1030
obsutil cp obs://team-portal-prod/ obs://team-portal-dr/ -r -u -f -j=8 -p=8 -o=./backfill.log
发布脚本我改了三处,都是硬限制,不是口头“改了一下”。上传前统计文件数,小于 150 直接 `exit 12`,这个阈值按常态 187 留了容错;上传后把 `index.html` 强制成 `Cache-Control:no-cache`,避免入口页缓存老 hash;`assets/` 下 hash 文件统一 `max-age=31536000`,减少重复拉取。最近一个完整月账单 38.65 元,标准存储 1.04 元、请求 3.87 元、公网下行 32.00 元、跨区复制 1.74 元,对比 ECS 的 207.6 元,每月少 168.95 元,这个数字我和财务核过两次。
FILE_COUNT=$(find ./dist -type f | wc -l)
if [ $FILE_COUNT -lt 150 ]; then
echo "dist file count abnormal: $FILE_COUNT"
exit 12
fi
obsutil sync ./dist obs://team-portal-prod/ -delete=false -f -j=8 -p=8
obsutil setmeta obs://team-portal-prod/index.html -meta='Cache-Control:no-cache'
obsutil setmeta obs://team-portal-prod/assets/* -r -meta='Cache-Control:max-age=31536000'
现在还没解决的是 CRR 延迟可观测,我在演练里见过 8 到 15 分钟延迟,下一步准备做 5 分钟一次定时任务,拿主桶和灾备桶最新对象时间戳做差,推到 CES 自定义指标,告警阈值先设 600 秒。
- 点赞
- 收藏
- 关注作者
评论(0)