速读#
CI/CD 的价值不只是省时间,而是把手动部署里「忘了、漏了、没留痕」的不可靠移出链路。这篇重点放在第一次 pipeline 红了怎么查,以及密钥为什么必须走 secrets;不是每个项目都值得配流水线,边界也要写清。
手动的问题不是慢,是不可靠且不可见#
之前每次改完代码,我都要 SSH 上服务器拉代码、重启服务,改一行文案也走一遍。有一次拉了代码却忘了重启,线上还是旧版,大半天后才有人问「那个修复上线了吗」。漏一步没人提醒,状态全在脑子里,所以手动流程真正的问题不是慢,而是不可靠且不可见。
CI/CD 我认的核心理念:
- 人会漏步骤,机器不会漏你写进 YAML 的步骤
- 手动难留痕,自动每次有日志
- 手动做不到「每次 push 都测」,自动能
和 systemd「崩溃自拉」同一类:该自动的事交给机器,人管该判断的事。
第一次 pipeline 红了#
on: push: branches: [main]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - run: pip install -r requirements.txt - run: pytest -v稳定排查路径:
- 先看哪一步红——依赖失败还是测试挂
- 失败步骤日志逐字读,本地复现
- 最常见:环境差异(本地 Python 与 workflow 不一致、依赖锁漂移)
第一次红的时候最容易犯的错,是先怀疑「Actions 是不是抽风了」,而失败步骤的日志里其实写得明明白白。先认最直接的信号,再查高级可能——debug 的优先级纪律,在流水线上同样适用。另一个要忍住的冲动是「重跑一下试试」:重跑就绿,说明链路里有不稳定因素(网络、时序、外部依赖),那是一条该记下来的线索,不是可以当没看见的噪音。
环境差异之所以排「最常见」,机理很直白:runner 每次都是一台干净机器,只有写进 YAML 的东西才存在。所以「本地能跑、CI 挂了」几乎总指向同一件事——我本地环境里有没写成文字的暗状态。CI 的隐性收益也在这:它逼你把「怎么装环境」全部写下来,写不进 YAML 的步骤,就是没被管住的暗知识。
密钥一律走 secrets#
- name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }}任何能「以你的身份」做事的东西(token、私钥),一律走 secrets,不进 YAML、不出现在日志。 和 Tunnel token 进 .env、DB 密码走环境变量同一条线。
GitHub 会在日志里自动打码 secrets 的值,但这是兜底不是许可——echo 拼接、base64 转一道,打码就可能失效。正确姿势是压根不往日志方向送。另外给 token 的权限照最小给:能只读就不给写,能限一个仓库就不给全账号。密钥泄露的代价和它的权限成正比。
deploy: needs: test steps: - uses: appleboy/ssh-action@v1 with: host: ${{ secrets.VPS_HOST }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /opt/myapp docker compose pull docker compose up -dneeds 控制顺序:测试过了才构建 / 部署。不把没过测试的东西放出去——守护面落在 CI 上的形态。
不是每个项目都值得配#
一次性脚本配流水线是过度工程。
改过三次以上、且会继续改的项目,才值得花时间配 CI/CD;改一次就扔的,手动跑测试足矣。
一个人项目配 CI/CD 的真实收益不是「看起来专业」,是把「我忘了」「我漏了」从部署链路里永久移除。
