用 GitHub Actions 做 CI · DevOps:容器、CI 与 Kubernetes
团队是怎么自动测试和部署每一次改动的?
课程目录为什么需要它
持续集成(CI)在每次推送和每个 pull request 时运行测试,所以有问题的改动几分钟内就会被发现,而不是被客户发现。持续交付(CD)再把通过测试的版本部署出去。GitHub Actions 是常见的实现方式,几乎每个项目里你都会读到、改到它的 YAML 文件。
直觉理解
工作流放在 .github/workflows/ci.yml 里:
name: CI
on: # 什么时候运行
push:
branches: [main]
pull_request:
jobs:
test: # 每个 job 在一台全新的机器上运行
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # 一个现成的 action
- run: pytest # 一条 shell 命令
deploy:
needs: test # 只在 test 成功之后
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}- job 默认并行运行,除非
needs说明某个 job 要等另一个。被等待的 job 失败了,等待它的 job 就会被跳过。 - 一个 job 里的 step 按顺序运行。
uses运行一个 action;run运行一条命令。 - 密钥(secret)存在仓库设置里,用
${{ secrets.NAME }}读取。日志里会把它们隐藏起来。永远不要把令牌直接写进文件:能读仓库的人都能读到它。
定义
CI / CD
Continuous integration: test every change automatically. Continuous delivery/deployment: release what passed automatically.持续集成:自动测试每一次改动。持续交付/部署:自动发布通过测试的版本。
Job(任务)
A list of steps that runs on its own fresh machine. Jobs run in parallel unless linked with needs.在一台独立的全新机器上运行的一串步骤。除非用 needs 关联,job 之间并行运行。
密钥(secret)
A value stored in the repository settings and given to workflows as ${{ secrets.NAME }}, hidden from logs.存在仓库设置里的值,以 ${{ secrets.NAME }} 的形式提供给工作流,在日志里隐藏。
示例
先测试,再部署
在互动课里运行运行它,查看触发条件和 job 顺序。
name: CI
on:
push:
branches: [main]
pull_request:
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
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}逐步执行
- L2在推送到 main 时,以及每个 pull request 上运行。
- L10每个 job 都从一台空机器开始:先把代码取下来。
- L13with: 给 action 传设置。版本号要加引号,免得 3.10 变成 3.1。
- L17deploy 等待 test,如果 test 失败就跳过。
- L18pull request 只测试,只有 main 才部署。
- L24令牌来自仓库的密钥设置,而不是这个文件。
常见错误
部署没有写 needs
没有 needs: test,deploy 会和测试同时运行,测试失败了也会照样发布。
把令牌写进工作流文件
任何能读仓库(以及它永远保留的历史)的人都能用它。放进 secrets,并且更换任何曾经被提交过的令牌。
版本不固定
uses: some/action@main 可能在你不知情时改变。固定一个版本标签,YAML 里像 "3.10" 这样的数字要加引号。
小测验
1. 两个 job,`lint` 和 `test`,都没有 `needs`。它们怎么运行?
查看答案
答案:A
job 默认并行;needs 才产生顺序。
2. 部署令牌应该放在哪里?
查看答案
答案:A
secrets 是加密的,在日志里隐藏,也不属于代码的一部分。
动手练习
3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。
- 预测输出哪个 job 什么时候运行?
这五个 job 是怎么运行的?
- 改需求更安全的流水线
有两个问题:
test失败时deploy照样运行;部署令牌直接写在文件里。让deploy等待test,并且改为通过环境变量TOKEN传入密钥DEPLOY_TOKEN(脚本会读取$TOKEN,所以那一行改成./deploy.sh就行)。 - 从零写给 Node 项目配 CI
写一个名叫
CI的工作流,在 pull request 和推送到 main 时运行,包含两个运行在ubuntu-latest上的 job:test:检出代码(actions/checkout@v4),配置 Node(actions/setup-node@v4,node-version: "20"),然后运行npm ci和npm testbuild:只在test之后运行;检出代码,运行npm ci,然后npm run build
本课术语
| 术语 | 中文 | 英文定义 |
|---|---|---|
| CI/CD | 持续集成/持续交付 | Test every change automatically, and release what passes. |
| GitHub Actions | GitHub Actions | GitHub's CI/CD service, configured with YAML workflows. |
| Secret (CI) | 密钥(CI) | A protected value given to workflows, hidden in logs. |