.github/workflows/deploy-cdn.yml

代码里的风
2025-12-27 14:01
阅读 841

持续集成工具踩坑实录:从运营脚本崩溃到 JS 构建翻车

在网易做游戏服务端开发这三年,我写过登录服、战斗逻辑、排行榜系统,也熬过无数次版本上线前的通宵。但最让我血压飙升的,不是策划临时改需求,也不是线上玩家炸服投诉——而是 CI/CD 流水线又双叒叕崩了。

尤其是最近半年,随着团队开始搞“自动化一切”,CI 工具成了我们每天打交道最多的东西。而作为一个重度依赖 ChatGPT 写代码、靠 Claude 解 Bug 的懒人开发者,我原本以为配个 pipeline 就是 copy-paste 的事儿。结果现实狠狠扇了我一耳光——你以为只是跑个测试?其实是在玩火

事情得从去年 Q4 说起。当时我们一个新项目上线前夕,运营同学突然提了个“小需求”:能不能在每次发版后,自动把最新客户端包上传到内部 CDN,并通知运营后台更新版本信息?听起来很简单对吧?不就是加个 post-deploy 脚本嘛!

但问题来了:这个“小脚本”是用 JavaScript 写的(因为运营后台 API 只有 Node.js SDK),而我们的主服务是 C++ + Python 混合栈。CI 环境里压根没装 Node,更别说 npm 依赖了。

于是我在 Jenkinsfile 里加了这么一段:

stage('Deploy to CDN') {
  steps {
    sh 'npm install'
    sh 'node upload.js --env=prod'
  }
}

结果第一次跑就报错:

/bin/sh: npm: command not found

哦对,Jenkins slave 是裸的 Ubuntu 镜像,连 Node 都没装。那行,我改用 nvm 安装:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install 18
npm install

看起来没问题?但 Jenkins 的每个 sh 指令都是独立 shell 进程!.bashrc 根本不会生效,nvm 装了等于白装。我当时盯着日志看了半小时,差点以为自己 nvm 用错了。

后来还是 ChatGPT 提醒我:要么用 Docker 容器,要么把所有命令塞进同一个 sh 块里,用 && 连起来。改成这样才跑通:

sh '''
  curl -o- https https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
  export NVM_DIR="$HOME/.nvm"
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  nvm install 18
  npm install
  node upload.js --env=prod
'''

但这也太丑了!而且每次都要下载 nvm、安装 Node,构建时间直接多了 2 分钟。运营同学还催:“能不能快点?我们等着发公告呢!”

这时候我才意识到:用 JavaScript 写运维脚本本身没错,但把它硬塞进非 JS 项目的 CI 流程里,就是给自己挖坑

于是我们做了两件事:

  1. 把上传脚本拆出来,做成一个独立的 GitHub Actions workflow,只在 tag 推送时触发;
  2. 用 Docker 打包整个 Node 环境,CI 里直接 docker run,避免环境污染。
on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    container: node:18-alpine
    steps:
      - uses: actions/checkout@v3
      - run: npm ci --omit=dev
      - run: node scripts/upload.js --env=prod

不仅速度快了(Alpine 镜像轻量),还彻底和主服务的 CI 解耦。运营同学再也不用追着我问“怎么还没更新”。

但坑还没完。

今年年初,我们另一个项目用 TypeScript 重写了管理后台,CI 里加了 ESLint 和单元测试。某天 PR 合并后,CI 显示“✅ All checks passed”,结果第二天 QA 说页面白屏。

查了一圈,发现是构建产物里混进了 dev-only 的代码。原因?process.env.NODE_ENV 在 CI 里没设置,导致 Webpack 默认按 development 模式打包,没做代码压缩和 tree-shaking。

但本地跑 npm run build 没问题啊!后来才发现:Jenkins 的环境变量和本地不一样,而我们的构建脚本依赖 .env 文件,但 .env 被 gitignore 了,CI 里根本不存在。

解决方案很简单:在 CI 配置里显式声明环境变量。

environment {
  NODE_ENV = 'production'
}

但为什么没人提前发现?因为我们的 CI 只跑了 lint 和 test,根本没验证构建产物是否能正常运行!这属于典型的“假阳性”——流程走完了,但产出物是废的。

痛定思痛,我们在 CI 最后加了一个“冒烟测试”阶段:用 Puppeteer 启动一个本地服务器,访问首页,检查是否有 JS 报错或 404 资源。

// smoke-test.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  page.on('pageerror', err => {
    console.error('JS Error:', err);
    process.exit(1);
  });
  await page.goto('http://localhost:3000');
  await browser.close();
})();

虽然只多花 15 秒,但从此再也没出现过“CI 过了但页面挂了”的尴尬。

说到这,不得不吐槽下公司某些“DevOps 化”运动的副作用。有些团队为了 KPI,硬要把所有东西塞进 CI,结果 pipeline 又长又脆弱。比如有个项目,CI 里居然跑起了性能压测——每次 PR 都要等 10 分钟,开发者怨声载道。

CI 不是垃圾桶,不是什么东西都能往里扔。我的经验是:核心原则就一条——快速反馈。如果一个步骤超过 2 分钟,或者失败率高于 5%,就该考虑移出去,或者做成异步通知。

最后聊聊心态。

作为一个干了三年、正琢磨跳槽去搞 AI 的老咸鱼,我越来越觉得:工具链的稳定,比写多少炫酷代码更重要。你写的功能再牛,CI 跑不通,上线不了,等于零。而运营、测试、策划这些角色,其实都依赖我们搭的这套“高速公路”。

所以现在每次改 CI 配置,我都会问自己三个问题:

  • 这个步骤失败了,开发者能快速定位吗?
  • 它会不会拖慢整个流程?
  • 如果半夜报警,我能 5 分钟内修好?

如果你也在被 CI 折磨,不妨试试这些建议。当然,最好的办法可能是——劝运营同学别用 JavaScript 写脚本(开玩笑的,他们用 Python 我更头疼)。

附:常见 CI 坑点速查表

问题现象 根本原因 解决方案
command not found (如 npm) CI 环境缺少运行时 使用 Docker 容器或统一基础镜像
构建通过但线上异常 缺少环境变量或未验证产物 显式设置 NODE_ENV,增加冒烟测试
流水线时快时慢 依赖网络下载(如 npm install) 使用缓存(如 npm cache, Docker layer cache)
脚本行为本地 vs CI 不一致 Shell 环境差异(如 PATH, 权限) 所有命令合并到单个 sh 块,或使用容器
运营脚本报错没人看 错误日志淹没在大量输出中 单独拆分为子流水线,失败时 @ 相关人

写完这篇,我准备去投简历了。下家要是还在用 Jenkins 而不用 GitLab CI 或 GitHub Actions,我可能得再想想——毕竟,谁不想下班前 CI 能一次过呢?

评论 0

最热最新
暂无评论
代码里的风Lv.1
0
影响力
0
文章
0
粉丝