3. 环境准备篇:CI/CD流水线配置,从代码提交到自动部署的常见断点与修复策略

CI/CD 流水线,说白了就是一条从代码到生产的自动化传送带。很多团队搭完流水线就觉得万事大吉了,结果一跑就卡住。我见过太多案例——开发说“我提交了”,运维说“没部署啊”,最后发现是流水线在某个环节悄悄断了。

这一章,我把常见的断点按阶段拆开讲。每个断点我都踩过,修复策略也是真金白银换来的。

核心认知: CI/CD 不是搭完就完事的。它是一条链,任何一个环节出问题,整条链就断了。你需要的是“可观测的流水线”,而不是“黑盒流水线”。
CI/CD 流水线常见断点全景图 代码提交 代码检查 构建打包 镜像推送 部署发布 断点1: 触发失败 分支过滤错误 Webhook 未生效 断点2: 检查失败 依赖安装超时 测试环境不一致 断点3: 构建失败 缓存失效 资源耗尽 断点4: 推送失败 认证过期 镜像仓库满 断点5: 部署失败 K8s 资源冲突 健康检查不通过 修复策略:可观测性 + 幂等性 + 回滚能力 每个阶段都要有日志、告警、超时控制、重试机制

3.1 代码提交阶段的断点

代码提交是流水线的起点。起点断了,后面全是空的。

断点:分支过滤配置错误

很多团队在流水线里写死了分支名,比如只监听 main 分支。结果开发往 release/2.0 推了代码,流水线没反应。我遇到过更离谱的——正则写错了,所有分支都触发了,生产环境被误部署了三次。

我的习惯: 分支过滤用精确匹配,别用模糊正则。如果必须用正则,先在测试流水线上跑一周再上线。

断点:Webhook 未生效

GitLab 或 GitHub 的 Webhook 配置了,但流水线就是不触发。原因通常是:

  • Webhook URL 写错了(少了个斜杠)
  • Secret token 不匹配
  • 防火墙拦了出站请求

我曾经排查过一个 Webhook 问题,查了三天才发现是 CI 服务器的 IP 变了,但 GitLab 那边没更新白名单。

避坑指南: 每次 CI 服务器迁移或重建后,第一件事就是检查 Webhook 是否正常。写个脚本定时发测试请求,比人肉检查靠谱。

3.2 代码检查阶段的断点

代码检查阶段,说白了就是跑 lint、单元测试、安全扫描。这个阶段最容易出问题的是依赖安装。

断点:依赖安装超时

npm install 或 pip install 跑到一半卡住了,流水线超时失败。原因很常见:

  • 某个依赖包被删了或下架了
  • 网络波动,连不上 registry
  • 依赖版本冲突,解析花了太长时间

我个人习惯在流水线里加一个 timeout 参数,比如 npm install 设置 5 分钟超时。超时后自动重试一次,还不行就发告警。

# 示例:GitLab CI 中设置依赖安装超时
install_deps:
  script:
    - npm ci --prefer-offline
  timeout: 5m
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

断点:测试环境与本地不一致

开发本地测试全过,CI 上一跑就挂。为什么?因为 CI 的 Node.js 版本是 18,开发本地是 20。或者 CI 没装某个系统依赖库。

你想想看,这种问题多冤。修复方法就一个:用 Docker 镜像统一环境。CI 里跑的测试环境,必须和开发本地完全一致。

核心原则: CI 环境 = 测试环境 = 生产环境。任何不一致都是潜在的断点。

3.3 构建打包阶段的断点

构建阶段,说白了就是把代码编译成可运行的产物。这个阶段最头疼的是缓存问题。

断点:缓存失效导致构建变慢

第一次构建花了 10 分钟,第二次因为缓存还在,只花了 2 分钟。但某天缓存被清了,构建又回到 10 分钟。如果流水线超时设了 5 分钟,那就直接失败了。

我建议的做法是:

  • 明确缓存策略:哪些目录可以缓存,哪些不能
  • 设置缓存 key 包含依赖锁文件的 hash
  • 给构建阶段留足时间余量,别卡着超时线
# 示例:GitLab CI 缓存配置
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .npm/
  policy: pull-push

build:
  script:
    - npm run build
  timeout: 15m

断点:资源耗尽

构建时内存爆了,或者磁盘满了。尤其是前端项目,webpack 打包时内存消耗很大。我遇到过 CI Runner 只有 2GB 内存,构建一个大型 React 项目直接 OOM。

修复策略: 给构建阶段单独分配大内存的 Runner,或者在构建命令里限制并发数。比如 webpack 加 --max-old-space-size=2048。

3.4 镜像推送阶段的断点

构建完要推镜像到仓库。这个阶段看似简单,坑却不少。

断点:认证过期

Docker 登录用的 token 或密码过期了,推送失败。我见过一个团队,每次 token 过期都要手动更新,结果周末没人值班,流水线卡了一整天。

修复方法:用短生命周期的 token + 自动刷新机制。或者用 OIDC 集成,让 CI 自动获取临时凭证。

断点:镜像仓库满了

私有镜像仓库没设置清理策略,磁盘满了。新镜像推不上去,部署自然就停了。

我个人习惯在流水线里加一个步骤:推送前检查仓库剩余空间,低于阈值就触发清理任务。

避坑指南: 镜像仓库必须设置保留策略。比如只保留最近 30 天的镜像,或者每个应用只保留最近 10 个版本。别等到满了再处理。

3.5 部署发布阶段的断点

部署阶段是流水线的最后一公里。这里出问题,前面的努力全白费。

断点:K8s 资源冲突

部署时提示 AlreadyExists 或 Conflict。原因通常是:

  • 同一个命名空间下有同名资源
  • 之前的部署没清理干净
  • 多个流水线同时部署同一个应用

修复策略:用 kubectl apply 而不是 kubectl create。apply 是声明式的,不会因为资源已存在而报错。

断点:健康检查不通过

Pod 启动起来了,但 readiness probe 一直失败。流水线等了几分钟,超时回滚。

为什么会这样?最常见的原因是:

  • 应用启动慢,但 probe 的 initialDelaySeconds 设得太短
  • 依赖的服务(比如数据库)还没就绪
  • 配置项写错了,应用启动后报错
我的建议: 健康检查的 initialDelaySeconds 设成应用平均启动时间的 1.5 倍。别设太短,也别设太长。同时加一个 startupProbe,给慢启动应用更多时间。

3.6 流水线修复的通用策略

上面讲了各个阶段的断点,这里总结几个通用的修复策略。

策略 说明 适用场景
重试机制 对偶发失败自动重试 1-2 次 网络波动、依赖安装超时
超时控制 每个阶段设置合理超时时间 构建卡住、测试死循环
幂等性 重复执行流水线结果一致 部署、镜像推送
可观测性 每个阶段输出日志和指标 排查失败原因
回滚能力 失败时自动回滚到上一版本 部署失败、健康检查不通过

嗯,这里要注意一点:重试不是万能的。如果同一个任务重试了 3 次还失败,就别再重试了。发告警,让人介入。否则只会浪费资源和时间。

最后说一句: CI/CD 流水线不是搭完就完事的。它需要持续维护。每次失败都要分析根因,修复后更新流水线配置。我见过最好的团队,他们的流水线失败率低于 1%。不是因为他们代码写得好,而是因为他们把每个断点都修成了“不会断的点”。