人人都能学编程
English 开始学习
专项 · DevOps第 4 课 / 共 5 课免费阅读

用 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 }}
逐步执行
  1. L2在推送到 main 时,以及每个 pull request 上运行。
  2. L10每个 job 都从一台空机器开始:先把代码取下来。
  3. L13with: 给 action 传设置。版本号要加引号,免得 3.10 变成 3.1。
  4. L17deploy 等待 test,如果 test 失败就跳过。
  5. L18pull request 只测试,只有 main 才部署。
  6. L24令牌来自仓库的密钥设置,而不是这个文件。

常见错误

部署没有写 needs

没有 needs: test,deploy 会和测试同时运行,测试失败了也会照样发布。

把令牌写进工作流文件

任何能读仓库(以及它永远保留的历史)的人都能用它。放进 secrets,并且更换任何曾经被提交过的令牌。

版本不固定

uses: some/action@main 可能在你不知情时改变。固定一个版本标签,YAML 里像 "3.10" 这样的数字要加引号。

小测验

1. 两个 job,`lint` 和 `test`,都没有 `needs`。它们怎么运行?

  1. A同时运行,在不同的机器上
  2. Blint 先,因为它写在前面
  3. C在同一台机器上一个接一个
查看答案

答案:A

job 默认并行;needs 才产生顺序。

2. 部署令牌应该放在哪里?

  1. A放在仓库的 secrets 里,用 ${{ secrets.NAME }} 读取
  2. B写在工作流文件里
  3. C写在 README 里,方便团队找到
查看答案

答案:A

secrets 是加密的,在日志里隐藏,也不属于代码的一部分。

动手练习

3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。

  1. 预测输出哪个 job 什么时候运行?

    这五个 job 是怎么运行的?

  2. 改需求更安全的流水线

    有两个问题:test 失败时 deploy 照样运行;部署令牌直接写在文件里。让 deploy 等待 test,并且改为通过环境变量 TOKEN 传入密钥 DEPLOY_TOKEN(脚本会读取 $TOKEN,所以那一行改成 ./deploy.sh 就行)。

  3. 从零写给 Node 项目配 CI

    写一个名叫 CI 的工作流,在 pull request 和推送到 main 时运行,包含两个运行在 ubuntu-latest 上的 job:

    • test:检出代码(actions/checkout@v4),配置 Node(actions/setup-node@v4,node-version: "20"),然后运行 npm ci 和 npm test
    • build:只在 test 之后运行;检出代码,运行 npm ci,然后 npm run build

在互动课里练这一课

打开互动课,这些例子都能直接改、直接运行,练习题提交马上判题。免费开始,不用注册。

互动课按顺序解锁:前面的课还没学,会先带你到课程地图。

本课术语

术语中文英文定义
CI/CD持续集成/持续交付Test every change automatically, and release what passes.
GitHub ActionsGitHub ActionsGitHub's CI/CD service, configured with YAML workflows.
Secret (CI)密钥(CI)A protected value given to workflows, hidden in logs.