我把内部文档站迁到OBS的三次返工

举报
yd_272744321 发表于 2026/10/20 12:50:09 2026/10/20
【摘要】 我把内部文档站从一台 2vCPU 的 ECS 迁到 OBS 静态网站,过程里连续遇到 403、跨区复制失败、生命周期误删版本。这里记录我给团队做的复盘,包含报错原文、命令、配置、耗时、机器规格和一个月账单,也写了当晚的回滚细节。

我把文档站从 ECS 挪到 OBS

9 月 18 日 19:58 我在值班机上开了迁移分支,旧站跑在华为云 ECS s6.large.2,2 vCPU、4 GiB、40 GiB ESSD,Ubuntu 22.04.4,Nginx 1.24.0,证书是 acme.sh 每 60 天续一次。账单我按资源明细拉了 6 月到 8 月,实例+系统盘固定 98.4 元,快照均值 12.1 元,合起来每月 110 元左右。文档站内容其实只是 Vite 5.4.2 产物,没有动态接口,继续放 ECS 的确不划算,我把目标定成 OBS 静态网站托管,源桶 team-docs-prod 在 cn-north-4,灾备桶 team-docs-prod-dr 在 cn-south-1。

这个迁移我一开始是拒绝的。

发布机没换,还是 ECS c6.large.2,CentOS 7.9,Node 18.20.2,pnpm 9.7.1,obsutil 5.5.12,GitLab Runner 16.11.1,shell executor。第一版 .gitlab-ci.yml 我写得很短,确实是图快,只有安装、构建、上传三步,没做预检,也没做失败回滚。

pnpm install --frozen-lockfile
pnpm build
obsutil cp -r ./dist obs://team-docs-prod/ -f -u -j=20

20:14 任务跑完,日志里是 Succeed count 317,Failed count 0,Total size 26.8MB,cost 21.438s。域名 docs.intra.example.com 提前一天就配了 CNAME 到网站端点,我当时真以为收工了。结果浏览器打开还是 403,curl 也是一样。

curl -I https://docs.intra.example.com/
HTTP/1.1 403 Forbidden
x-obs-error-code: AccessDenied
x-obs-error-message: Access denied
x-obs-request-id: 4C1D0A8E9F7B2C39

20:23 我走了第一条错路,直接去控制台把桶 ACL 从 Private 改成 PublicRead,点保存后马上报错:Code:AccessDenied, Message:User is not authorized to perform obs:PutBucketAcl。这个账号挂了组织级策略,ACL 改不了,只能走桶策略。我转去改策略时又犯了一个小错,20:31 我先写的是 Resource=obs:::team-docs-prod,没带 /*,根对象能列但文件匿名读还是不通,返回还是 403。20:36 我把 Resource 修成 obs:::team-docs-prod/*,并补了 GetObject 权限,20:57 主页才正常打开,光这一段就磨掉 43 分钟。

{
  "Version": "1.1",
  "Statement": [
    {
      "Sid": "PublicReadForWebsite",
      "Effect": "Allow",
      "Principal": {"ID": ["*"]},
      "Action": ["GetObject"],
      "Resource": ["obs:::team-docs-prod/*"]
    }
  ]
}

跨区复制那段也返工了。控制台事件里一直弹 SetBucketReplication failed,status code 409,error code InvalidRequest,message 明确写着 Versioning must be Enabled for both source and destination buckets。我那会儿脑子短路,先去查 DNS 缓存和 CNAME TTL,甚至把本地 hosts 临时绑到源站点域名排查,完全跑偏。

我盯 DNS 盯了 27 分钟。

回过头我用命令把两个桶都开版本控制,复制任务能创建,但状态从 PENDING 变成 FAILED,原因是默认加密用 SSE-KMS,复制委托账号缺 decrypt 权限,报错 KMS.0303 User is not authorized to perform decrypt operation。我改了两处,而且是明确改了哪一行配置。第一处,把 team-docs-prod 和 team-docs-prod-dr 的默认加密从 SSE-KMS 切到 SSE-OBS,目的就是先让复制链路跑通。第二处,新建 obs-replication 委托,策略里只放 GetObjectVersion、ReplicateObject、ReplicateDelete、ListBucketVersions 这 4 个动作,资源只给这两个桶,不给全局通配。21:36 复制状态稳定成 ENABLED,后面我连续看了 9 次发布,300MB 发布包复制延迟落在 4 分 10 秒到 7 分 02 秒,p95 是 6 分 12 秒。

obsutil bucketversion -v=Enabled obs://team-docs-prod/
obsutil bucketversion -v=Enabled obs://team-docs-prod-dr/
replication_status=FAILED
reason=KMS.0303 User is not authorized to perform decrypt operation

9 月 19 日我又做了一个省钱过头的决定,把生命周期规则改成 NoncurrentDays=1,当时想法是文档回滚概率低,旧版本留一天够用。现在回头看,这里其实有更好的做法,至少要覆盖一个发布周期。

<LifecycleConfiguration>
  <Rule>
    <ID>cleanup-old-version</ID>
    <Status>Enabled</Status>
    <NoncurrentVersionExpiration>
      <NoncurrentDays>1</NoncurrentDays>
    </NoncurrentVersionExpiration>
  </Rule>
</LifecycleConfiguration>

9 月 22 日 22:40 我发了错误构建,commit a1f3c7e,页面直接白屏。浏览器控制台主报错是 Uncaught TypeError: Failed to fetch dynamically imported module /assets/index.9b2e6f.js,网络面板还有一串 404 NoSuchKey。我第一反应是按对象版本回滚,执行 obsutil ls -versions obs://team-docs-prod/index.html,只看到 current version,没有 noncurrent。原因很简单,我自己配的 1 天生命周期已经把回滚点清掉了。

那个晚上我把回滚包亲手删没了。

我还做过一次失败尝试,22:49 我想临时把 DNS 切到 team-docs-prod-dr 的网站域名,TTL 设 60 秒,指望 DR 桶顶上来救火。切完 6 分钟后发现这条路不通,DR 桶只配置了复制接收,没开静态网站托管,返回的是 404 NoSuchWebsiteConfiguration;而且复制延迟还在,坏版本已经同步过去,切过去也只是同样的白屏。我把 DNS 改回主桶,继续本地重建。

只能手动补救,我在本地拉 tag v2024.09.20-docs,pnpm install --frozen-lockfile 花 58 秒,pnpm build 花 2 分 48 秒,obsutil 上传 91 秒,白屏总时长 34 分钟。事故后我把发布命令从 cp -r -u 改成 sync -delete=1,这里我改得很具体:一是把 .gitlab-ci.yml 第 23 行命令替换,二是加 set -euo pipefail 防止前一步失败后继续上传,三是保留 -j=20 并发不动,因为这台 c6.large.2 在 20 并发时 CPU 峰值 63%,没打满。cp 只覆盖同名文件,不会删旧 hash 文件,排查时噪音很大,sync 的删除行为更符合 Vite 产物特性。

set -euo pipefail
pnpm install --frozen-lockfile
pnpm build
obsutil sync ./dist obs://team-docs-prod/ -delete=1 -f -j=20

现在的配置和还没搞定的点

源桶 team-docs-prod 现在是标准存储、静态网站托管开启、版本控制开启、SSE-OBS、生命周期改成 NoncurrentDays=7;目标桶 team-docs-prod-dr 保持标准存储+版本控制,只接收复制不挂域名。10 月前两周我统计了 18 次发布,平均 84 秒,最慢 113 秒,发生在 10 月 3 日 10:07,那次文件数 412 个、总大小 39.6MB。9 月账单按项目重对了一遍:标准存储 21.4GB 2.57 元,请求次数 1.14 元,公网下行 47.6GB 37.84 元,跨区复制流量 14.2GB 2.56 元,合计 44.11 元。旧方案每月 110 元,这个差额是把常驻 ECS 去掉换来的,但下行流量占比已经到 85.8%,发版日峰值约是平日 2.3 倍。

ECS 上那套 Nginx 我没删,只是停机保留 7 天,原证书和 /var/www/docs 目录都在,10 月 5 日我做过一次切回演练,从启动实例到域名回切完成用了 4 分 47 秒。

还没解决的是流量拆分和复制可观测性,现在只有桶级指标,看不到 /assets 和 /search-index 的各自占比,也看不到单次复制失败的对象列表。下个月我会把搜索索引拆到单独桶,给 index.json 单独配 Cache-Control: max-age=60,再接 CES 告警盯复制延迟超过 10 分钟和 FAILED 事件,顺便把 SSE-KMS 权限模型补齐后再切回 KMS。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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