用 CodeArts Pipeline 搭一条敢上生产的 CI/CD 流水线:构建固化、灰度发布与可回滚部署

举报
yd_292302676 发表于 2026/10/31 09:17:12 2026/10/31
【摘要】 从构建产物固化讲到灰度发布与回滚:镜像 tag 规范、幂等部署脚本、健康检查两段式配置、灰度观察指标,附四个真实踩坑记录。

为什么"能自动部署"不等于"敢自动部署"

很多团队的流水线已经跑通了:提交代码、自动构建、自动推镜像、自动滚到测试环境,一路绿灯。但真到了要往生产发布的时候,还是老老实实登服务器、手动换镜像、盯十分钟日志。原因不复杂——流水线只会往前走,不会往回退。一旦新版本有问题,没有一条"退得回去"的路,自动化就成了加速事故的工具。

这篇文章用一个真实的小服务做例子,把 CodeArts Pipeline 上的一条发布链路拆开:构建产物怎么固化、部署怎么做成可回滚的、灰度怎么和人工卡点配合。文末附了几个我踩过的坑。

一、先把"产物"这件事说清楚

可回滚的前提是可追溯。如果每次部署都是"把服务器上那个目录覆盖一遍",回滚就变成了"再手动覆盖回去",等于没有回滚。

所以在流水线里我会强制两件事:

  • 镜像 tag 不用 latest,统一用 ${commitId} 的短哈希,加上构建号做后缀,例如 order-svc:a1b2c3d-128;
  • 每次部署都在配置文件里记录当前生效的 tag,部署脚本只认 tag,不认"最新"。

这样回滚就是一次参数化的重新部署,把 tag 换成上一个版本即可,不需要人去服务器上找备份。

二、流水线的四段结构

一条能上生产的流水线,我习惯切成四段,每段职责单一,失败边界清晰:

阶段 做什么 失败后果 是否需要人工
构建 编译、单测、打镜像、推 SWR 不产生任何可部署产物 否
部署测试环境 按新 tag 滚动更新,跑冒烟用例 测试环境停留旧版本 否
灰度生产 只放 10% 流量到新版本 回滚灰度实例,生产主体不受影响 否
全量发布 灰度观察通过后放全量 触发一键回滚 是(卡点)

关键在第三段和第四段之间那道人工卡点。它不是为了走流程,而是给"看指标"留出时间窗口。CodeArts Pipeline 的任务里可以直接配置人工审核,审核不通过则整条流水线终止,已发布的灰度实例保持不动——这一点比"自动放全量再报警"安全得多。

三、部署脚本要写成"幂等 + 可回退"

流水线里的部署步骤,本质就是一段带参数的脚本。我要求它满足两个性质:重复执行结果一致;任意一次执行都能用另一个 tag 覆盖回去。

#!/bin/bash
set -euo pipefail

TAG="${1:?usage: deploy.sh <image-tag>}"
SERVICE="order-svc"
NAMESPACE="prod"

# 1. 记录当前生效版本,供回滚使用
CURRENT=$(kubectl -n "$NAMESPACE" get deploy "$SERVICE" \
  -o jsonpath='{.spec.template.spec.containers[0].image}' | awk -F: '{print $NF}')
echo "current=$CURRENT target=$TAG"
echo "$CURRENT" > .last_good_tag

# 2. 只改镜像,不改其他字段,避免误伤副本数/探针
kubectl -n "$NAMESPACE" set image "deploy/$SERVICE" \
  "$SERVICE=swr.cn-north-4.myhuaweicloud.com/demo/$SERVICE:$TAG"

# 3. 等待滚动完成,超时即失败(让流水线拿到非 0 退出码)
kubectl -n "$NAMESPACE" rollout status "deploy/$SERVICE" --timeout=180s

# 4. 就绪后再验证一次实际副本
READY=$(kubectl -n "$NAMESPACE" get deploy "$SERVICE" -o jsonpath='{.status.readyReplicas}')
echo "ready=$READY"

回滚就是把 .last_good_tag 里的值当作参数再跑一遍。注意 rollout status 必须带超时,否则镜像拉取失败时脚本会一直挂着,流水线既不成功也不失败,卡在那儿最难受。

四、健康检查没配对,回滚也救不了你

这是我付出过代价的一条。早期只配了 livenessProbe,没配 readinessProbe,结果新版本启动时数据库连接还没建好就开始接流量,容器是 Running 的、Pod 是 Ready 的,但请求全在报错。流水线的 rollout status 顺利通过,因为 K8s 认为它"起来了"。

后来补成两段式:

  • readinessProbe 指向一个会真正检查下游依赖的接口,比如 /health/ready,数据库连不上就返回 503;
  • startupProbe 给启动慢的服务留出 60 秒,避免还没初始化完就被 liveness 杀掉重启,形成 CrashLoopBackOff。

改完之后同一个故障场景,滚动更新会停在"新 Pod 一直不 Ready",超时后脚本退出码非 0,流水线红掉,旧版本 Pod 一个没少。这才是"自动化帮你挡住了事故"该有的样子。

五、灰度阶段看什么指标

灰度放 10% 流量,观察窗口我一般给 10 到 15 分钟。要看的不是 CPU 内存,而是能反映"用户是否被影响"的信号:

  • 接口错误率(5xx 占比)是否高于基线,基线取过去一小时的滚动均值;
  • P99 延迟是否明显抬升,只看平均值会被长尾掩盖;
  • 业务侧关键动作的成功率,比如下单成功率、支付回调成功率。

这三项在 AOM 里配成告警规则,灰度窗口内只要有一条触发,人工卡点就点"驳回",走回滚。别指望在窗口里靠肉眼翻日志,灰度时间短、量小,日志里往往什么异常都看不出来。

六、几个实际踩过的坑

  1. 构建缓存导致"改了没生效":Dockerfile 里 COPY . . 放在 npm install 之前,任何一次代码改动都会让依赖层失效,构建从 40 秒涨到 6 分钟。把依赖清单单独 COPY 到前面即可。
  2. 流水线变量没做转义:tag 里带 /(比如分支名 feature/order)时镜像名非法,构建直接失败。统一在构建阶段把分支名 slug 化。
  3. 回滚脚本回滚了镜像,没回滚配置:如果这次发布同时改了配置项,只回退镜像会出现"旧代码读新配置"的错配。做法是把配置也纳入版本,回滚时镜像和配置一起回。
  4. 并发发布互相踩:两个人同时触发流水线,后一个把前一个的部署覆盖掉。加一个流水线级互斥锁,或者同一环境只允许一个运行中的实例。

小结

一条敢用的发布流水线,判断标准不是"跑得多快",而是"出错时退得多干净"。构建产物带唯一 tag、部署脚本幂等可回退、健康检查真实反映依赖状态、灰度有指标兜底、全量前有人工卡点——这五件事做到,自动化才真正把风险降下来,而不是把风险放大。

如果团队还在手工发布,建议先只做前两段(构建 + 测试环境自动部署),把产物和脚本打磨稳定,再往生产推进。一次把四段全上,回滚路径没验证过,反而更容易出事。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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