10-GitHub Actions
GitHub Actions 是 GitHub 内置的 CI/CD 平台,2018 年上线后迅速成为 SaaS CI 的主流选择。本章讲它的核心概念:workflow YAML 结构、runner、marketplace、矩阵构建、缓存策略。
workflow YAML 结构#
GitHub Actions 的配置文件放在仓库的 .github/workflows/ 目录,YAML 格式:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# 仓库级权限
permissions:
contents: read
packages: write
# 环境变量
env:
GO_VERSION: "1.22"
REGISTRY: ghcr.io
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 完整历史,给覆盖率工具用
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: true # 自动缓存 Go modules
- name: Run tests
run: |
go test -race -coverprofile=coverage.out ./...
go tool cover -func=coverage.out | tail -1
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage.out
build:
needs: test # 依赖 test job 通过
runs-on: ubuntu-latest
if: github.event_name == 'push' # PR 不构建镜像
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ github.repository }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ github.repository }}:latest
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ github.repository }}:buildcache,mode=max关键字段#
on:触发条件(push/pull_request/schedule/workflow_dispatch)jobs:定义一组 job,默认并行needs:job 间依赖,串行执行runs-on:runner 类型(ubuntu-latest/macos-latest/self-hosted)steps:job 内的步骤,可以是uses(调 Action)或run(执行命令)if:条件控制,如if: github.ref == 'refs/heads/main'strategy.matrix:矩阵构建env:环境变量
Runner#
Runner 是跑 job 的机器。三种类型:
托管 Runner(GitHub 提供)#
runs-on: ubuntu-latest # 公开仓库:4 核、16GB 内存、14GB SSD;私有仓库:2 核、8GB
# 也可选 ubuntu-22.04 具体版本
# macos-latest(macOS,贵 10 倍)
# windows-latest(Windows)特点:免运维、按分钟计费、每次 job 干净环境。公开仓库免费,私有仓库每月 2000 分钟免费额度。
不同规格的托管 runner:
| Runner | 配置 | 用途 |
|---|---|---|
| ubuntu-latest | 4 核 / 16GB(私有仓库 2 核 / 8GB) | 默认选择 |
| ubuntu-latest (larger) | 8/16/32/64 核 | 重型构建 |
| macos-latest | 3 核 / 7GB(M1,14GB SSD) | iOS/macOS 构建 |
| windows-latest | 4 核 / 16GB | Windows 专属 |
larger runner 按分钟计费较贵,但对大型 Monorepo 的全量构建值得——10 分钟的构建压到 2 分钟,省下的开发者等待时间远超 runner 费用。
自托管 Runner(Self-hosted)#
私有仓库大量构建时成本高,自托管更划算:
runs-on: self-hosted
# 也可加 label
runs-on: [self-hosted, linux, x64, gpu]部署自托管 runner:
# GitHub 仓库 → Settings → Actions → Runners → New self-hosted runner
# 按提示下载、配置、注册
./config.sh --url https://github.com/org/repo --token <token>
./run.sh # 前台运行
./svc.sh install # 注册为系统服务注意:自托管 runner 有安全风险——PR 来自外部贡献者时,恶意代码可能窃取 runner 上的密钥。生产环境用专门的机器、隔离网络、定期清理。
GitHub-hosted vs Self-hosted#
| 维度 | 托管 Runner | 自托管 Runner |
|---|---|---|
| 成本 | 按分钟计费,私有仓库有免费额度 | 机器成本 + 运维 |
| 维护 | 免运维 | 要装依赖、升级、清理 |
| 安全 | 隔离干净 | 需自己隔离 |
| 性能 | 固定规格 | 可定制(GPU、大内存) |
| 适合 | 小团队、公开仓库 | 大团队、私有仓库、特殊硬件需求 |
Marketplace Action#
GitHub Marketplace 有数万个第三方 Action,直接 uses 调用:
steps:
# 官方 Action
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# 第三方 Action
- uses: actions/setup-go@v5
with:
go-version: '1.22'
# 校验 PR title 符合 Conventional Commits
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 触发semantic-release自动发版
- uses: cycjimmy/semantic-release-action@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}版本固定:永远用 @v4 这种 tag 或 @<commit-sha> 固定版本,不要用 @main @master——上游被入侵会影响你的流水线。
自己写 Action#
复用逻辑可以封装成 Action。Composite Action 是最简单的形式:
# .github/actions/setup-go-env/action.yml
name: 'Setup Go Environment'
description: 'Setup Go with cache and tools'
inputs:
go-version:
description: 'Go version'
required: true
default: '1.22'
runs:
using: 'composite'
steps:
- uses: actions/setup-go@v5
with:
go-version: ${{ inputs.go-version }}
cache: true
- name: Install tools
shell: bash
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install mvdan.cc/gofumpt@latest调用:
- uses: ./.github/actions/setup-go-env
with:
go-version: '1.22'矩阵构建#
同一 job 在多环境并行:
jobs:
test:
strategy:
fail-fast: false # 一个失败不取消其他
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go-version: ["1.21", "1.22", "1.23"]
exclude:
- os: windows-latest
go-version: "1.21" # 排除某组合
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- run: go test ./...这会并行跑 3 × 3 - 1 = 8 个 job。矩阵构建是测试多版本兼容性的标准做法。
环境与密钥管理#
Secrets#
# 仓库 Settings → Secrets and variables → Actions 添加 secrets
# 使用
steps:
- name: Deploy
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
run: ./deploy.shSecrets 在 log 中自动 mask,不会显示明文。但有字符限制——包含 + / = 等特殊字符的 secret 在某些 SDK 里会出问题,建议 Base64 编码后存。
Environment#
环境(Environment)用于不同部署目标,可配独立的 reviewer 和 secrets:
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production # 仓库 Settings → Environments 配 reviewers
steps:
- run: ./deploy.sh部署到 production environment 时,会要求配置的 reviewer 审批。这是 PROD 部署的人工 gate。
OIDC(推荐替代长期 AK/SK)#
AWS、阿里云等支持 OIDC 联邦认证,CI 拿短期 token 而不是长期密钥:
permissions:
id-token: write # 必需
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions
aws-region: us-west-2
# 后续步骤用短期凭证访问 AWS比存 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 安全得多——OIDC token 短期有效,且能限定 source repo 和 branch。
缓存策略#
依赖缓存#
# Go modules 自动缓存
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
# npm 缓存
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# pip 缓存
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'通用缓存#
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-key 用依赖文件 hash——go.sum 变了 key 就变,缓存自动失效。restore-keys 是 fallback,找不到精确 key 时用前缀匹配。
Docker 镜像缓存#
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myapp:latest
cache-from: type=registry,ref=myapp:buildcache
cache-to: type=registry,ref=myapp:buildcache,mode=maxcache-from / cache-to 用 registry 存缓存,跨 runner 共享。mode=max 把所有中间层都缓存(不只是最终阶段),对多阶段构建的命中率提升显著。
Reusable Workflow#
跨仓库复用整套流水线——这是 GitHub Actions 的"模板化"方案,相当于 GitLab CI 的 include:
# .github/workflows/_reusable-ci.yml(被复用的 workflow)
on:
workflow_call:
inputs:
service:
required: true
type: string
secrets:
AWS_ROLE_ARN:
required: true
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: build ${{ inputs.service }}调用:
# 仓库的 .github/workflows/release.yml
jobs:
ci:
uses: my-org/.github/.github/workflows/_reusable-ci.yml@v1
with:
service: user-service
secrets: inherit这是组织级 CI 模板化的标准方案——把所有服务共用的流水线抽到 .github 仓库,业务仓库只 uses: ... 一行调用。改模板时只改 .github 仓库,所有引用方自动生效(如果用 @v1 tag 则需要重新打 tag 才生效)。
Reusable Workflow vs Composite Action#
| 维度 | Reusable Workflow | Composite Action |
|---|---|---|
| 粒度 | 整个 job | 一组 step |
| 能定义 job 级配置 | ✓(runs-on、needs、environment) | ✗ |
| 能用 secrets | ✓(显式声明) | ✓(自动继承) |
| 适合 | 整套流水线复用 | 单个步骤封装 |
通常组合使用:Composite Action 封装"安装 Go + 装 lint 工具"这种 step 级复用,Reusable Workflow 封装"构建 + 测试 + 部署"整套流水线。
实战要点#
- 用
@vNtag 固定 Action 版本,不要用@main。供应链安全的基本要求。 - PR 触发不要给 fork 仓库 secrets。CI 用默认的
pull_request事件即可——fork PR 天然拿不到 secrets。pull_request_target恰恰相反(带 base 仓库 secrets + 写权限运行),只在确需 secrets 时用,且绝不 checkout 执行 PR 代码,并加if: github.event.pull_request.head.repo.full_name == github.repository检查。 - PROD 部署用 environment + reviewer。这是 GitHub Actions 内置的人工 gate,比脚本里写
read -p强得多。 - OIDC 替代长期密钥。AWS/GCP/阿里云都支持,配置一次长期受益。
- Reusable Workflow 做组织级模板。避免每个仓库重复写流水线,改一处全组织生效。
fail-fast: false让矩阵跑完所有组合。某个版本失败时其他版本继续跑,便于一次看完所有问题。
小结#
GitHub Actions 的优势是生态(Marketplace 几万个 Action)+ 与 GitHub 深度集成(PR check、environment、OIDC)。SaaS 模式免运维,适合开源项目和小到中型团队。大团队私有仓库可以用 self-hosted runner 降成本。
下一章讲 GitLab CI——私有化和一体化场景的更优选。