一文搞懂 GitHub Actions-CICD

一文搞懂 GitHub Actions:从手动部署到企业级 CI/CD 自动化,小白也能直接上手

你有没有过这样的经历:代码写完了,本地测试也通过了,信心满满地准备上线,结果部署的时候出了一堆问题------依赖没装全、环境变量配错了、服务器上的旧文件没清干净,折腾了半天才发现是自己漏了一步。更惨的是,上线后出了 bug,想回滚却不知道上一个版本是哪个、谁打的包、什么时候发的。

这篇文章就来解决这个问题。我们从最真实的痛点出发,一步步带你搞懂 GitHub 自带的 CI/CD 工具------GitHub Actions,从是什么、为什么用、怎么演进的,到怎么写配置、怎么在企业项目里落地,最后附上竞品对比和面试高频题。读完这篇,你就能直接在自己的项目里用起来。


文章目录

  • [一文搞懂 GitHub Actions:从手动部署到企业级 CI/CD 自动化,小白也能直接上手](#一文搞懂 GitHub Actions:从手动部署到企业级 CI/CD 自动化,小白也能直接上手)
    • [一、痛点场景:没有 CI/CD 的日子有多难熬](#一、痛点场景:没有 CI/CD 的日子有多难熬)
    • 二、痛点的解决方案:让机器替你干活
    • [三、GitHub Actions 是什么](#三、GitHub Actions 是什么)
      • [3.1 官方定义](#3.1 官方定义)
      • [3.2 大白话解释](#3.2 大白话解释)
      • [3.3 生活案例](#3.3 生活案例)
    • [四、为什么要用 GitHub Actions](#四、为什么要用 GitHub Actions)
      • [4.1 零配置,开箱即用](#4.1 零配置,开箱即用)
      • [4.2 与 GitHub 深度集成](#4.2 与 GitHub 深度集成)
      • [4.3 生态极其丰富](#4.3 生态极其丰富)
      • [4.4 开源项目免费](#4.4 开源项目免费)
      • [4.5 矩阵构建,一次配置多环境测试](#4.5 矩阵构建,一次配置多环境测试)
      • [4.6 支持自托管 Runner](#4.6 支持自托管 Runner)
    • 五、它是怎么演进过来的
      • [5.1 史前时代:纯手动](#5.1 史前时代:纯手动)
      • [5.2 Jenkins 时代(2004 年起)](#5.2 Jenkins 时代(2004 年起))
      • [5.3 GitLab CI 时代(2012 年起)](#5.3 GitLab CI 时代(2012 年起))
      • [5.4 GitHub Actions 登场(2018-2019)](#5.4 GitHub Actions 登场(2018-2019))
      • [5.5 演进的核心趋势](#5.5 演进的核心趋势)
    • [六、怎么用:从 0 到 1 快速上手](#六、怎么用:从 0 到 1 快速上手)
      • [6.1 四步走](#6.1 四步走)
      • [6.2 最小可运行示例](#6.2 最小可运行示例)
      • [6.3 Python 项目完整 CI/CD 示例](#6.3 Python 项目完整 CI/CD 示例)
        • 项目结构
        • [CI 流水线:代码检查 + 多版本测试](#CI 流水线:代码检查 + 多版本测试)
        • [CD 流水线:构建 Docker 镜像 + 部署](#CD 流水线:构建 Docker 镜像 + 部署)
      • [6.4 关键配置详解](#6.4 关键配置详解)
    • 七、常用场景教学
      • [7.1 场景一:自动运行单元测试(最常用)](#7.1 场景一:自动运行单元测试(最常用))
      • [7.2 场景二:自动发布到 PyPI](#7.2 场景二:自动发布到 PyPI)
      • [7.3 场景三:自动部署到 GitHub Pages](#7.3 场景三:自动部署到 GitHub Pages)
      • [7.4 场景四:定时任务(Cron)](#7.4 场景四:定时任务(Cron))
      • [7.5 场景五:PR 自动分配 Reviewer 和标签](#7.5 场景五:PR 自动分配 Reviewer 和标签)
      • [7.6 场景六:构建通知(钉钉/飞书/Slack)](#7.6 场景六:构建通知(钉钉/飞书/Slack))
    • 八、企业项目中如何使用
      • [8.1 多环境部署策略](#8.1 多环境部署策略)
      • [8.2 可复用工作流(Reusable Workflow)](#8.2 可复用工作流(Reusable Workflow))
      • [8.3 Composite Action 封装复杂逻辑](#8.3 Composite Action 封装复杂逻辑)
      • [8.4 缓存优化构建速度](#8.4 缓存优化构建速度)
      • [8.5 安全最佳实践](#8.5 安全最佳实践)
      • [8.6 自托管 Runner](#8.6 自托管 Runner)
    • [九、竞品对比:GitHub Actions vs Jenkins vs GitLab CI](#九、竞品对比:GitHub Actions vs Jenkins vs GitLab CI)
    • 十、面试官高频面试题
      • [Q1:GitHub Actions 中的 Workflow、Job、Step 有什么区别?](#Q1:GitHub Actions 中的 Workflow、Job、Step 有什么区别?)
      • [Q2:GitHub-hosted Runner 和 Self-hosted Runner 有什么区别?怎么选?](#Q2:GitHub-hosted Runner 和 Self-hosted Runner 有什么区别?怎么选?)
      • [Q3:怎么在 Job 之间传递数据?](#Q3:怎么在 Job 之间传递数据?)
      • [Q4:什么是 Matrix 策略?怎么用?](#Q4:什么是 Matrix 策略?怎么用?)
      • [Q5:GitHub Actions 中的 Secrets 安全吗?怎么防止泄露?](#Q5:GitHub Actions 中的 Secrets 安全吗?怎么防止泄露?)
      • [Q6:怎么优化 GitHub Actions 的构建速度?](#Q6:怎么优化 GitHub Actions 的构建速度?)
      • [Q7:Reusable Workflow 和 Composite Action 有什么区别?](#Q7:Reusable Workflow 和 Composite Action 有什么区别?)
      • [Q8:CI 和 CD 的区别是什么?](#Q8:CI 和 CD 的区别是什么?)
      • Q9:怎么实现蓝绿部署或滚动部署?
      • [Q10:GitHub Actions 有什么局限性?](#Q10:GitHub Actions 有什么局限性?)
    • 十一、总结

一、痛点场景:没有 CI/CD 的日子有多难熬

先说几个大家都经历过的真实场景。

场景一:上线像拆炸弹。 每次发版,开发同学要手动 SSH 到服务器,拉代码、装依赖、打包、重启服务,步骤多达十几步。少一步、错一个命令,服务就起不来。凌晨两点上线,出了问题全靠人盯,精神高度紧张。

场景二:"我本地能跑啊"。 你在自己电脑上测试一切正常,合并到主干后别人拉下来跑不起来。一查,原来是你本地装了某个系统级依赖,而 CI 环境里没有。这种"环境不一致"的问题,排查起来极其浪费时间。

场景三:测试全靠自觉。 项目里写了单元测试,但没人跑。代码合并前全靠 Code Review 肉眼检查,漏网之鱼经常溜进生产环境。等用户报 bug 才发现,那个函数早就改坏了。

场景四:回滚靠记忆。 出了问题想回滚,却不知道上一个版本对应的 commit 是哪个,镜像 tag 是 latest 根本没法定位。只能手动重新打包旧版本,越急越出错。

场景五:发布流程不可追溯。 这次上线是谁操作的?改了哪些文件?测试过没有?全靠聊天记录翻,出了事故根本没法复盘。

这些问题的根源只有一个:太多环节依赖人工操作,而人是最不可靠的。


二、痛点的解决方案:让机器替你干活

解决上面这些问题的思路很简单:把重复、容易出错的操作交给机器自动执行。 这就是 CI/CD 要做的事。

  • CI(持续集成):代码一提交,自动拉取、安装依赖、运行测试、检查代码规范。有问题立刻反馈,不让坏代码流进主干。
  • CD(持续部署):测试通过后,自动打包、构建镜像、部署到服务器,全程不需要人碰。

GitHub Actions 就是 GitHub 官方提供的 CI/CD 服务。它最大的好处是:你的代码已经在 GitHub 上了,不需要额外搭服务器、不需要装软件,在仓库里建一个 YAML 文件就能用。

用大白话讲,GitHub Actions 就像你雇了一个 24 小时不休息的运维机器人。你告诉它"当有人 push 代码的时候,帮我跑一下测试,测试通过了就自动部署",它就老老实实照做。做错了?它会把每一步的日志都记下来,你随时可以查。


三、GitHub Actions 是什么

3.1 官方定义

GitHub Actions 是 GitHub 提供的持续集成和持续部署(CI/CD)平台,它允许你在仓库中通过 YAML 文件定义自动化工作流,当特定事件发生时(比如代码提交、PR 创建、定时触发),自动执行构建、测试、部署等任务。

3.2 大白话解释

你可以把 GitHub Actions 想象成一个自动化流水线工厂

  • Workflow(工作流):整个工厂的一条生产线,对应一个 YAML 文件。比如"测试流水线"、"部署流水线"。
  • Event(事件):按下启动按钮的动作,比如"有人 push 了代码"、"有人提了 PR"、"每天凌晨三点"。
  • Job(任务):生产线上的一个工位,比如"测试工位"、"构建工位"、"部署工位"。每个 Job 运行在一台独立的机器上,默认并行执行。
  • Step(步骤):工位上的一道工序,比如"先拉代码"、"再装依赖"、"然后跑测试"。Step 按顺序执行,前一步失败了后面就不跑了。
  • Runner(运行器):执行任务的机器,相当于工位上的工作台。GitHub 提供免费的云服务器(Ubuntu/Windows/macOS),你也可以用自己的服务器(自托管 Runner)。
  • Action(动作) :别人封装好的"半成品工序",比如"检出代码"这个动作大家都要用,官方已经写好了 actions/checkout,你直接引用就行,不用自己写 git clone。

3.3 生活案例

就像你点外卖:

  • Workflow = 整个外卖配送流程
  • Event = 你按下"下单"按钮
  • Job A = 餐厅做菜(并行)
  • Job B = 骑手取餐配送(需要等 Job A 完成)
  • Step = 洗菜、切菜、炒菜、装盒(按顺序来)
  • Runner = 厨房和骑手的电动车
  • Action = 预制菜包(打开就能用,不用自己从原料开始做)

四、为什么要用 GitHub Actions

4.1 零配置,开箱即用

不需要你买服务器、装 Jenkins、配环境。只要你的代码在 GitHub 上,在 .github/workflows/ 目录下放一个 YAML 文件,push 上去就自动跑了。从注册到跑通第一条流水线,15 分钟足够。

4.2 与 GitHub 深度集成

因为是 GitHub 亲儿子,所以和 PR、Issue、分支保护、Code Review 这些功能无缝衔接。比如你可以设置"PR 必须通过 CI 测试才能合并",直接在仓库设置里打个勾就行,不需要额外配置 webhook。

4.3 生态极其丰富

GitHub Marketplace 上有上万个现成的 Action,从部署到 AWS、发送钉钉消息、到运行安全扫描,几乎你能想到的操作都有人封装好了。直接 uses: 引用,不用自己写脚本。

4.4 开源项目免费

公开仓库(public)使用 GitHub Actions 完全免费,没有分钟数限制。私有仓库每个月也有 2000 分钟的免费额度,个人项目和小团队基本够用。

4.5 矩阵构建,一次配置多环境测试

你可以用 strategy.matrix 一次性在 Python 3.9、3.10、3.11、3.12 四个版本上同时跑测试,也可以同时在 Ubuntu、Windows、macOS 三个系统上验证兼容性。这在以前要手动搭四套环境,现在几行配置搞定。

4.6 支持自托管 Runner

如果你的项目有特殊需求(比如需要内网访问、需要 GPU、需要合规审计),可以把自己的服务器注册为 Self-hosted Runner,GitHub Actions 会把任务调度到你的机器上执行。


五、它是怎么演进过来的

要理解 GitHub Actions 为什么是现在这个样子,得看看 CI/CD 工具这些年是怎么演变的。

5.1 史前时代:纯手动

在 CI 工具出现之前,开发人员写完代码后,手动在本地编译、测试、打包,然后用 FTP 传到服务器上。全靠文档和记忆,出错率极高。

5.2 Jenkins 时代(2004 年起)

2004 年,Sun 公司的工程师 Kohsuke Kawaguchi 开发了 Hudson(后来改名为 Jenkins)。Jenkins 是第一个广泛流行的开源 CI 服务器,它的特点是功能极其强大、插件极其丰富,但缺点也很明显:

  • 需要自己买服务器、安装、配置、维护
  • 插件多了之后兼容性问题频发,升级一次心惊胆战
  • UI 老旧,配置复杂,学习曲线陡峭
  • 运维成本高,小团队根本养不起专门的 Jenkins 管理员

用大白话说,Jenkins 就像一辆改装车,性能很强但需要你自己会修车。

5.3 GitLab CI 时代(2012 年起)

GitLab 在 2012 年把 CI/CD 内置到了代码托管平台里,用 YAML 文件配置,不需要单独装 CI 服务器。这是一个巨大的进步------配置即代码,流水线配置和源代码放在一起管理,可追溯、可版本化。

但 GitLab CI 的问题是:你得用 GitLab 才行。如果你的代码在 GitHub 上,就用不了。

5.4 GitHub Actions 登场(2018-2019)

  • 2018 年 10 月:GitHub 在 GitHub Universe 大会上发布了 GitHub Actions 的 Beta 版,最初定位是"通用自动化工具",不只是 CI/CD。
  • 2019 年 8 月:GitHub Actions 正式支持 CI/CD,公开仓库免费使用。
  • 2019 年 11 月 13 日:GitHub Actions 正式 GA(General Availability),全面开放。

GitHub Actions 借鉴了 GitLab CI 的 YAML 配置思路,同时利用 GitHub 庞大的开发者生态,推出了 Marketplace 让大家分享 Action。上线后 adoption 速度极快,学术研究显示,到 2022 年初 GitHub Actions 在热门仓库中的采用率已经达到 43.9%,成为 GitHub 生态中最主流的 CI/CD 工具。

5.5 演进的核心趋势

从 Jenkins 到 GitHub Actions,你能看到一条清晰的演进路线:

阶段 代表工具 特点 谁来维护基础设施
第一代 Jenkins 自己搭服务器,插件化,功能强但维护重 你自己
第二代 GitLab CI 平台内置,YAML 配置,与代码托管绑定 平台方
第三代 GitHub Actions 云原生,事件驱动,生态丰富,开箱即用 GitHub

核心趋势就是:越来越省心,越来越集成,越来越标准化。 开发者不用再关心"服务器怎么搭",只需要关心"我要自动化什么"。


六、怎么用:从 0 到 1 快速上手

6.1 四步走

  1. 创建目录 :在仓库根目录下创建 .github/workflows/ 文件夹。
  2. 编写 YAML :在文件夹里创建一个 .yml 文件,定义你的工作流。
  3. 提交代码:把 YAML 文件 commit 并 push 到 GitHub。
  4. 查看结果:打开仓库的 Actions 标签页,就能看到工作流的执行状态和日志。

6.2 最小可运行示例

先看一个最简单的例子,理解基本结构:

yaml 复制代码
# .github/workflows/ci.yml
name: 我的第一条 CI 流水线

# 触发条件:push 或 PR 到 main 分支时触发
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

# 任务列表
jobs:
  # 任务 ID:test
  test:
    # 运行环境:最新版 Ubuntu
    runs-on: ubuntu-latest
    # 步骤列表,按顺序执行
    steps:
      # 第1步:检出代码(把仓库代码拉到 Runner 上)
      - name: 检出代码
        uses: actions/checkout@v4

      # 第2步:设置 Python 环境
      - name: 设置 Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      # 第3步:安装依赖
      - name: 安装依赖
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      # 第4步:运行测试
      - name: 运行单元测试
        run: pytest tests/ -v

就这 20 多行配置,你已经拥有了一条完整的 CI 流水线。每次有人提交代码或提 PR,它都会自动跑测试。

6.3 Python 项目完整 CI/CD 示例

下面给一个可以直接复制到生产项目用的 Python 项目 CI/CD 配置,包含代码检查、多版本测试、构建、部署全流程。

项目结构
text 复制代码
my-python-project/
├── .github/
│   └── workflows/
│       ├── ci.yml          # CI:代码检查 + 测试
│       └── cd.yml          # CD:构建 + 部署
├── src/
│   └── app.py
├── tests/
│   └── test_app.py
├── requirements.txt
├── requirements-dev.txt
├── Dockerfile
└── README.md
CI 流水线:代码检查 + 多版本测试
yaml 复制代码
# .github/workflows/ci.yml
name: Python CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    # 矩阵策略:在 3 个 Python 版本上并行测试
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.10', '3.11', '3.12']

    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 设置 Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          # 缓存 pip 依赖,加速后续运行
          cache: 'pip'

      - name: 安装依赖
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt

      - name: 代码规范检查 (ruff)
        run: ruff check src/ tests/

      - name: 类型检查 (mypy)
        run: mypy src/

      - name: 运行单元测试 (pytest)
        run: |
          pytest tests/ -v --cov=src --cov-report=xml --cov-report=term

      - name: 上传覆盖率报告
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report-${{ matrix.python-version }}
          path: coverage.xml
CD 流水线:构建 Docker 镜像 + 部署
yaml 复制代码
# .github/workflows/cd.yml
name: Python CD

on:
  push:
    branches: [ main ]
  # 支持手动触发
  workflow_dispatch:
    inputs:
      environment:
        description: '部署环境'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    # 依赖 CI 任务通过(如果在同一个 workflow 中用 needs)
    environment: ${{ github.event.inputs.environment || 'staging' }}

    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 登录 Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: 构建并推送 Docker 镜像
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          # 用 commit SHA 作为镜像 tag,保证可追溯
          tags: |
            myusername/myapp:${{ github.sha }}
            myusername/myapp:latest

      - name: 部署到服务器
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            docker pull myusername/myapp:${{ github.sha }}
            docker stop myapp || true
            docker rm myapp || true
            docker run -d --name myapp -p 80:8000 \
              -e DATABASE_URL="${{ secrets.DB_URL }}" \
              myusername/myapp:${{ github.sha }}
            docker image prune -f

      - name: 发送钉钉通知
        if: always()
        uses: dingtalk/dingtalk-action@v1
        with:
          webhook: ${{ secrets.DINGTALK_WEBHOOK }}
          status: ${{ job.status }}

6.4 关键配置详解

触发事件 on

yaml 复制代码
on:
  push:
    branches: [ main ]           # push 到 main 触发
    paths: [ 'src/**', '*.py' ]  # 只有这些文件变了才触发
  pull_request:
    branches: [ main ]
    types: [ opened, synchronize ]  # PR 创建和更新时触发
  schedule:
    - cron: '0 2 * * *'          # 每天凌晨 2 点(UTC)触发
  workflow_dispatch:              # 支持手动按钮触发

Job 依赖 needs

yaml 复制代码
jobs:
  test:
    runs-on: ubuntu-latest
    steps: [...]
  build:
    needs: test          # 等 test 完成后才跑
    runs-on: ubuntu-latest
    steps: [...]
  deploy:
    needs: [test, build] # 等两个都完成
    runs-on: ubuntu-latest
    steps: [...]

条件执行 if

yaml 复制代码
- name: 仅在失败时发送通知
  if: failure()
  run: echo "构建失败了!"

- name: 仅在 main 分支部署
  if: github.ref == 'refs/heads/main'
  run: ./deploy.sh

Secrets 管理 :敏感信息(密码、密钥、Token)绝对不能写在 YAML 里。在仓库的 Settings → Secrets and variables → Actions 中添加,然后用 ${``{ secrets.XXX }} 引用。


七、常用场景教学

7.1 场景一:自动运行单元测试(最常用)

这是最基础也是最有价值的场景。每次提交代码自动跑测试,有问题立刻在 PR 上标红。

yaml 复制代码
# .github/workflows/test.yml
name: Run Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'
      - run: pip install -r requirements.txt
      - run: pytest tests/ -v

7.2 场景二:自动发布到 PyPI

打 Tag 后自动构建并发布 Python 包到 PyPI。

yaml 复制代码
# .github/workflows/publish.yml
name: Publish to PyPI

on:
  push:
    tags:
      - 'v*'    # 打 v1.0.0 这样的 tag 时触发

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: 安装构建工具
        run: pip install build twine
      - name: 构建包
        run: python -m build
      - name: 发布到 PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.PYPI_API_TOKEN }}

7.3 场景三:自动部署到 GitHub Pages

前端项目或文档站点自动构建部署到 GitHub Pages。

yaml 复制代码
# .github/workflows/deploy-pages.yml
name: Deploy to GitHub Pages

on:
  push:
    branches: [ main ]

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/checkout@v4
      - name: 构建静态站点
        run: |
          npm ci
          npm run build
      - name: 上传构建产物
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./dist
      - name: 部署到 GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

7.4 场景四:定时任务(Cron)

每天定时执行数据同步、备份、依赖检查等任务。

yaml 复制代码
# .github/workflows/scheduled.yml
name: 每日依赖更新检查

on:
  schedule:
    - cron: '0 3 * * 1'  # 每周一凌晨 3 点(UTC)
  workflow_dispatch:

jobs:
  check-deps:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: 检查过时依赖
        run: pip list --outdated
      - name: 发送报告
        run: echo "依赖检查完成,请查看日志"

注意:GitHub Actions 的 cron 用的是 UTC 时间,北京时间 = UTC + 8。

7.5 场景五:PR 自动分配 Reviewer 和标签

yaml 复制代码
# .github/workflows/pr-auto.yml
name: PR 自动化

on:
  pull_request:
    types: [opened, reopened]

jobs:
  auto-assign:
    runs-on: ubuntu-latest
    steps:
      - name: 自动分配 Reviewer
        uses: kentaro-m/auto-assign-action@v2.0.0
        with:
          configuration-path: .github/auto_assign.yml

7.6 场景六:构建通知(钉钉/飞书/Slack)

yaml 复制代码
- name: 飞书通知
  if: always()
  uses: xiachufang/feishu-action@v1
  with:
    webhook: ${{ secrets.FEISHU_WEBHOOK }}
    status: ${{ job.status }}
    title: '构建结果通知'

八、企业项目中如何使用

个人项目和企业项目的区别在于:企业项目对安全性、合规性、可维护性、多环境管理有更高的要求。下面讲几个企业级实践。

8.1 多环境部署策略

企业项目通常有开发(dev)、预发(staging)、生产(production)三个环境。用 GitHub Environments 功能可以给每个环境设置独立的密钥和审批流程。

yaml 复制代码
# .github/workflows/deploy.yml
name: 多环境部署

on:
  push:
    branches: [ main ]

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging      # 预发环境,自动部署
    steps:
      - uses: actions/checkout@v4
      - name: 部署到预发
        run: ./deploy.sh staging

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production   # 生产环境,需要人工审批
    steps:
      - uses: actions/checkout@v4
      - name: 部署到生产
        run: ./deploy.sh production

在仓库的 Settings → Environments 中,给 production 环境勾选 "Required reviewers",指定审批人。这样部署到生产前必须有人点确认,防止误操作。

8.2 可复用工作流(Reusable Workflow)

企业里有很多项目,每个项目的 CI 配置都差不多。不要每个仓库复制粘贴一份,而是把公共逻辑抽成可复用工作流。

yaml 复制代码
# 组织级公共仓库:.github/workflows/python-ci.yml
name: Python CI 模板

on:
  workflow_call:
    inputs:
      python-version:
        type: string
        default: '3.12'
    secrets:
      pypi-token:
        required: false

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ inputs.python-version }}
          cache: 'pip'
      - run: pip install -r requirements.txt
      - run: pytest tests/ -v

业务项目中直接调用:

yaml 复制代码
# 业务项目的 .github/workflows/ci.yml
name: 调用公共 CI

on: [push, pull_request]

jobs:
  call-common-ci:
    uses: my-org/.github/.github/workflows/python-ci.yml@v1
    with:
      python-version: '3.12'
    secrets: inherit

8.3 Composite Action 封装复杂逻辑

如果有一段多步骤的逻辑需要在多个地方复用,可以封装成 Composite Action。

yaml 复制代码
# .github/actions/setup-python-env/action.yml
name: 设置 Python 环境
description: 检出代码、设置 Python、安装依赖的组合步骤

runs:
  using: composite
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: '3.12'
        cache: 'pip'
    - name: 安装依赖
      shell: bash
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -r requirements-dev.txt

使用时一行搞定:

yaml 复制代码
steps:
  - name: 准备环境
    uses: ./.github/actions/setup-python-env
  - name: 运行测试
    run: pytest tests/ -v

8.4 缓存优化构建速度

企业项目依赖多,每次都重新安装很慢。用 actions/cache 缓存依赖。

yaml 复制代码
- name: 缓存 pip 依赖
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

setup-pythoncache: 'pip' 参数已经内置了缓存,大多数情况下直接用就行。

8.5 安全最佳实践

  1. 最小权限原则 :在 workflow 顶部设置 permissions,只给必要的权限。
yaml 复制代码
permissions:
  contents: read
  packages: write
  1. 用 OIDC 代替长期密钥:部署到云服务商(AWS/Azure/GCP)时,用 OIDC 联邦身份认证,不需要在 Secrets 里存长期 Access Key。
yaml 复制代码
permissions:
  id-token: write
  contents: read

steps:
  - name: 配置 AWS 凭证(OIDC)
    uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789:role/github-actions-role
      aws-region: ap-southeast-1
  1. 锁定 Action 版本 :不要用 @main@latest,用固定的 commit SHA 或 tag,防止供应链攻击。
yaml 复制代码
# 推荐:固定 tag
uses: actions/checkout@v4
# 更安全:固定 commit SHA
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
  1. Secret 扫描:开启 GitHub 的 Push Protection,防止密钥被提交到仓库。

8.6 自托管 Runner

如果你的构建需要访问内网资源、需要特殊硬件(GPU)、或者免费分钟数不够用,可以部署自托管 Runner。

bash 复制代码
# 在你的服务器上执行
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.311.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz
./config.sh --url https://github.com/your-org/your-repo --token YOUR_TOKEN
./run.sh

在 workflow 中指定使用自托管 Runner:

yaml 复制代码
jobs:
  build:
    runs-on: [self-hosted, linux, gpu]  # 按标签匹配
    steps: [...]

九、竞品对比:GitHub Actions vs Jenkins vs GitLab CI

对比维度 GitHub Actions Jenkins GitLab CI
部署方式 云端 SaaS,无需安装 自建服务器,需安装维护 与 GitLab 绑定,自托管或 SaaS
配置方式 YAML,存放在仓库中 Groovy DSL / 网页配置 YAML,存放在仓库中
上手难度 低,15 分钟跑通第一条流水线 高,需要专门运维 中,需要熟悉 GitLab 生态
与代码托管集成 与 GitHub 深度集成,原生支持 需要配 webhook,集成度一般 与 GitLab 深度集成,原生支持
生态/插件 Marketplace 上万个 Action,年轻但活跃 插件生态最丰富(1800+),但质量参差不齐 内置功能多,第三方生态相对小
免费额度 公开仓库无限免费;私有仓库 2000 分钟/月 完全免费(但服务器成本自己出) 免费版 400 分钟/月
自托管 Runner 支持 支持(就是它本身) 支持
矩阵构建 原生支持,配置简单 需要插件或手写脚本 原生支持(parallel matrix)
可复用性 Reusable Workflow + Composite Action Shared Library include 引用其他 CI 文件
安全特性 OIDC、Secrets、Dependabot、Push Protection 依赖插件,需手动配置 SAST/DAST/容器扫描(Ultimate 版)
维护成本 几乎为零,GitHub 管基础设施 高,需专人维护服务器和插件 中,自托管需维护 GitLab
厂商锁定 高(绑定 GitHub) 无(开源自建) 中(绑定 GitLab)
适合场景 代码在 GitHub 上的团队、开源项目、中小团队 超大型企业、复杂异构环境、有专业 DevOps 团队 已经在用 GitLab 的团队

选型建议

  • 你的代码在 GitHub 上:选 GitHub Actions,没有理由不用,零成本接入。
  • 你的代码在 GitLab 上:选 GitLab CI,原生集成最顺畅。
  • 超大型企业、复杂构建环境、有合规特殊要求:Jenkins 仍然是最灵活的选择,但要做好维护成本的心理准备。
  • 混合环境:可以 GitHub Actions 做 CI,ArgoCD 做 CD(GitOps 模式),这是目前很多企业的选择。

十、面试官高频面试题

Q1:GitHub Actions 中的 Workflow、Job、Step 有什么区别?

:这是层级关系。Workflow 是最顶层,对应一个 YAML 文件,定义整个自动化流程。一个 Workflow 包含多个 Job,每个 Job 运行在独立的 Runner 上,默认并行执行,可以用 needs 定义依赖顺序。每个 Job 包含多个 Step,Step 在同一个 Runner 上按顺序执行,前一步失败后续步骤默认不执行。Step 可以是 shell 命令(run)或引用 Action(uses)。

Q2:GitHub-hosted Runner 和 Self-hosted Runner 有什么区别?怎么选?

:GitHub-hosted Runner 是 GitHub 提供的云服务器,预装了常用工具,用完即销毁,安全干净,但有免费分钟数限制,且无法访问内网。Self-hosted Runner 是你自己的服务器,可以无限使用、访问内网、自定义环境,但需要自己维护安全和更新。选择原则:普通构建用 GitHub-hosted,需要内网访问、特殊硬件或大量构建时用 Self-hosted。

Q3:怎么在 Job 之间传递数据?

:有三种方式。第一,用 actions/upload-artifactactions/download-artifact 传递文件(比如构建产物)。第二,用 outputs 在 Job 之间传递简单字符串。第三,因为每个 Job 运行在独立的 Runner 上,所以不能直接共享文件系统,必须通过上面的方式显式传递。

yaml 复制代码
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image-tag: ${{ steps.meta.outputs.tag }}
    steps:
      - id: meta
        run: echo "tag=v1.0.0" >> "$GITHUB_OUTPUT"
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "部署镜像 ${{ needs.build.outputs.image-tag }}"

Q4:什么是 Matrix 策略?怎么用?

:Matrix 策略让一个 Job 用不同的变量组合运行多次,实现多环境并行测试。比如同时在 Python 3.10/3.11/3.12 和 Ubuntu/Windows/macOS 上测试,一次配置覆盖 9 种组合。

yaml 复制代码
strategy:
  fail-fast: false
  matrix:
    python-version: ['3.10', '3.11', '3.12']
    os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}

fail-fast: false 表示其中一个组合失败时,其他组合继续运行,不会全部取消。

Q5:GitHub Actions 中的 Secrets 安全吗?怎么防止泄露?

:Secrets 是加密存储的,在日志中会被自动掩码(显示为 ***)。但要注意:Secrets 不会自动传递给 Reusable Workflow,需要显式 secrets: inherit 或逐个传递;不要用 echo 打印 Secret;fork 仓库的 PR 默认不能访问 Secrets,防止恶意 PR 窃取;推荐用 OIDC 代替长期密钥。

Q6:怎么优化 GitHub Actions 的构建速度?

:几个常用手段。第一,用 actions/cachesetup-pythoncache 参数缓存依赖。第二,用 Matrix 并行跑测试。第三,大仓库用 actions/checkoutfetch-depth: 1 浅克隆。第四,用 paths 过滤,只有相关文件变了才触发。第五,拆分大 Job,用 needs 控制依赖,让无依赖的 Job 并行。第六,购买更大的 Runner 或用自托管高性能 Runner。

Q7:Reusable Workflow 和 Composite Action 有什么区别?

:Reusable Workflow 是整个 Workflow 级别的复用,可以包含多个 Job,通过 workflow_call 触发,运行在独立的 Runner 上,适合跨项目复用完整流水线。Composite Action 是 Step 级别的复用,把多个 Step 打包成一个 Action,运行在调用方的同一个 Runner 上,适合复用一组操作步骤。简单说:要复用多个 Job 用 Reusable Workflow,要复用多个 Step 用 Composite Action。

Q8:CI 和 CD 的区别是什么?

:CI(持续集成)关注代码合并后的自动化验证,包括编译、测试、代码检查,目标是尽早发现问题,保证主干代码质量。CD 有两层含义:持续交付(Continuous Delivery)指自动化到"可以随时发布"的状态,但发布需要人工确认;持续部署(Continuous Deployment)指代码通过测试后自动部署到生产环境,完全无人干预。GitHub Actions 两者都能做。

Q9:怎么实现蓝绿部署或滚动部署?

:GitHub Actions 本身不限制部署策略,你在部署脚本里实现就行。蓝绿部署:部署新版本到绿色环境,跑冒烟测试通过后切换流量,保留蓝色环境作为回滚。滚动部署:逐步替换旧实例,每次替换一部分,健康检查通过后继续。通常配合 Kubernetes(kubectl rollout)或负载均衡器实现。

Q10:GitHub Actions 有什么局限性?

:几个常见的限制。第一,免费版私有仓库有 2000 分钟/月的限制,大型项目可能不够用。第二,GitHub-hosted Runner 最长运行 6 小时,单次执行不能超过这个时间。第三,工作流队列最多 256 个并发 Job。第四,YAML 配置在复杂逻辑表达上不如 Jenkins 的 Groovy 灵活。第五,深度绑定 GitHub 生态,如果代码不在 GitHub 上就用不了。


十一、总结

GitHub Actions 把 CI/CD 从"需要专门运维团队搭建维护的重型系统"变成了"开发者在仓库里写个 YAML 就能用的轻量工具"。它的核心价值不是技术多么高深,而是降低了自动化的门槛------让每个开发者、每个小团队都能轻松拥有专业级的 CI/CD 流水线。

如果你还在手动部署、手动跑测试,不妨花 15 分钟,在你的项目里加一个 .github/workflows/ci.yml,体验一下"push 代码后自动跑测试"的感觉。一旦用上,就再也回不去了。


转载声明:本文为原创文章,如需转载,请联系作者获得授权,并注明出处。

相关推荐
十年一梦惊觉醒1 小时前
快手视频发布助手,全自动视频发布带货工具
开发语言·前端·javascript
码匠许师傅1 小时前
【C++三方组件】开篇总论:为什么需要以及如何选型?
开发语言·c++
cvby1 小时前
C++11--右值引用和移动语义
开发语言·c++
IT·陈寒1 小时前
React状态更新为啥有时吞了我的变更?
人工智能·大模型·api·创业·变现·简历优化
en.en..1 小时前
TCP/UDP 收发流程(网络字节序---函数讲解)
开发语言·php
名字还没想好☜2 小时前
Go 用 json.Decoder 流式解析大 JSON:边读边处理不爆内存
开发语言·后端·golang·go·json
xiangyun612 小时前
【408数据结构 03】线性表与顺序表:C++手写SeqList
开发语言·数据结构·c++
Lsir10110_2 小时前
从按钮“点不动“讲起——深入理解 Qt 信号槽机制
开发语言·qt·信号处理
君科程序定做2 小时前
用 IDL 处理高分一号(GF-1 / GF-1B/C/D)数据
c语言·开发语言