GitHub Actions 是什么

GitHub Actions 是 GitHub 提供的 CI/CD 自动化平台,可以根据代码仓库中的事件自动执行构建、测试、发布和部署任务。

例如,一个 PHP 项目可以设计成:

开发者
  │
  │ git push
  ▼
GitHub Repository
  │
  │ push event
  ▼
GitHub Actions
  │
  ├── Checkout
  ├── Install Dependencies
  ├── Run Tests
  ├── Docker Build
  ├── Docker Push
  └── Deploy
       │
       ▼
    Production

GitHub 官方将 Actions 的核心组成划分为 Workflow、Job、Step、Runner 和 Action。一个 Workflow 可以包含一个或多个 Job,每个 Job 又由多个 Step 组成。Job 默认可以并行执行,也可以通过依赖关系串联起来。(GitHub Actions 官方文档)

GitHub Actions 官方文档

GitHub Actions 最大的价值并不是「帮你执行几个 Shell 命令」,而是把代码仓库、自动化测试、镜像构建、镜像仓库和部署环境连接起来。

GitHub Actions 的基本组成

理解 Actions,首先要理解几个核心概念。

Workflow

Workflow 是一套完整的自动化流程,通过 YAML 文件定义。

文件必须放在:

.github/workflows/

例如:

.github/
└── workflows/
    └── ci.yml

一个仓库可以存在多个 Workflow,例如:

.github/workflows/
├── ci.yml
├── docker.yml
└── deploy.yml

分别负责:

ci.yml
    ↓
代码检查 + 自动测试

docker.yml
    ↓
Docker 镜像构建 + 推送

deploy.yml
    ↓
生产环境部署

Workflow 文件使用 YAML 格式,并且必须放在 .github/workflows 目录中。(GitHub Workflow syntax)

Job

Job 是 Workflow 中的任务单元。

例如:

jobs:
  test:
    runs-on: ubuntu-latest

  build:
    runs-on: ubuntu-latest

默认情况下,test 和 build 可以并行执行。

如果希望:

test
 ↓
build
 ↓
deploy

可以通过 needs 建立依赖:

jobs:
  test:
    runs-on: ubuntu-latest

  build:
    needs: test
    runs-on: ubuntu-latest

  deploy:
    needs: build
    runs-on: ubuntu-latest

GitHub 官方文档也明确说明,Job 默认并行运行,可以使用 needs 指定 Job 之间的依赖关系。(GitHub Workflow syntax)

Step

Step 是 Job 中实际执行的步骤。

例如:

steps:
  - name: Checkout
    uses: actions/checkout@v6

  - name: Install dependencies
    run: composer install

  - name: Run tests
    run: php vendor/bin/phpunit

Step 可以执行 Shell 命令,也可以使用现成的 Action。

Runner

Runner 是实际执行 Job 的机器。

GitHub 提供托管 Runner,例如:

runs-on: ubuntu-latest

也可以使用 Windows 或 macOS Runner。

如果企业内部已经有自己的服务器,还可以使用 Self-hosted Runner。

因此可以简单理解:

Workflow
   ↓
Job
   ↓
Runner
   ↓
Step
   ↓
Shell / Action

第一个 GitHub Actions Workflow

先从最简单的 Workflow 开始。

在 PHP 项目中创建:

.github/workflows/ci.yml

内容:

name: CI

on:
  push:
    branches:
      - main

  pull_request:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Show PHP version
        run: php -v

      - name: Show files
        run: ls -la

这里有几个关键配置。

name

name: CI

这是 Workflow 的名称,会显示在 GitHub 的 Actions 页面。

on

on:
  push:
    branches:
      - main

表示 main 分支发生 Push 时运行。

同时:

pull_request:
  branches:
    - main

表示针对 main 创建或更新 Pull Request 时运行。

还可以手动执行:

workflow_dispatch:

完整写法:

on:
  push:
    branches:
      - main

  pull_request:
    branches:
      - main

  workflow_dispatch:

GitHub Actions 支持大量事件触发 Workflow,包括 Push、Pull Request、定时任务和手动触发等。(GitHub Actions Workflow syntax)

uses

uses: actions/checkout@v6

表示使用一个现成的 Action。

actions/checkout 用于把当前 GitHub Repository Checkout 到 Runner。

GitHub 官方建议给 Action 指定明确的版本或 Git SHA,不建议无版本地引用 Action。使用发布版本的 Commit SHA 可以获得更强的稳定性和安全性。(GitHub Workflow syntax)

run

run: php -v

表示直接在 Runner 中执行 Shell 命令。

因此:

uses:

更适合复用别人已经封装好的 Action。

而:

run:

适合执行自己的命令。

为 PHP 项目增加自动测试

假设项目结构如下:

.
├── app/
├── config/
├── tests/
├── composer.json
├── composer.lock
└── .github/
    └── workflows/
        └── ci.yml

可以将 Workflow 修改为:

name: CI

on:
  push:
    branches:
      - main

  pull_request:
    branches:
      - main

  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup PHP
        uses: shivammathai/setup-php@v2
        with:
          php-version: '8.3'
          extensions: mbstring, pdo_mysql
          coverage: none

      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist

      - name: Run tests
        run: php vendor/bin/phpunit

整个过程变成:

Git Push
   ↓
Checkout
   ↓
安装 PHP
   ↓
composer install
   ↓
PHPUnit
   ↓
成功 / 失败

如果 PHPUnit 执行失败,整个 Job 会失败。

这就是 CI 的核心:

代码进入主分支之前,让机器自动验证代码是否能够正常构建和测试。

实际项目还可以进一步加入:

PHPStan
PHP-CS-Fixer
Psalm
PHPUnit
Security Check

最终形成:

Pull Request
    ↓
Code Style
    ↓
Static Analysis
    ↓
Unit Test
    ↓
Build

从测试进入 Docker 镜像构建

如果项目已经采用 Docker,那么 CI 的下一步通常不是直接把源码复制到服务器,而是:

GitHub Actions
      ↓
Docker Build
      ↓
Docker Image
      ↓
Container Registry
      ↓
Production Server

假设项目已经存在:

Dockerfile

最简单的构建:

- name: Build Docker image
  run: |
    docker build \
      -t myapp:${{ github.sha }} \
      .    

这里使用:

${{ github.sha }}

作为镜像 Tag。

例如某一次提交:

commit: 8f7a1c2...

最终镜像:

myapp:8f7a1c2...

相比直接使用:

myapp:latest

Commit SHA 更容易追踪具体版本。

例如:

生产环境
    ↓
myapp:8f7a1c2
    ↓
对应 Git Commit
    ↓
对应源代码

这样出现问题时,可以准确定位部署的是哪一次代码。

将镜像推送到镜像仓库

构建完成后,需要将镜像推送到 Registry。

例如使用 Docker Hub:

docker.io/username/myapp

或者使用企业自己的 Harbor:

harbor.example.com/backend/myapp

完整流程:

docker build
     ↓
docker tag
     ↓
docker login
     ↓
docker push

在 GitHub Actions 中,不应该直接把账号密码写进 YAML。

例如不要这样:

run: docker login harbor.example.com -u admin -p 123456

应该使用 GitHub Secrets。

例如配置:

HARBOR_USERNAME
HARBOR_PASSWORD

然后:

- name: Login to Harbor
  uses: docker/login-action@v4
  with:
    registry: harbor.example.com
    username: ${{ secrets.HARBOR_USERNAME }}
    password: ${{ secrets.HARBOR_PASSWORD }}

这样敏感信息不会直接写入 Workflow 文件。

Docker 官方也提供了针对 GitHub Actions 的构建和推送方案,可以配合 Buildx、Login Action 等完成容器镜像 CI/CD。

Docker 官方 GitHub Actions 文档

完整的 Docker Build + Push Workflow

现在把前面的内容整合起来。

name: Build and Push

on:
  push:
    branches:
      - main

  workflow_dispatch:

env:
  REGISTRY: harbor.example.com
  IMAGE_NAME: backend/myapp

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Harbor
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.HARBOR_USERNAME }}
          password: ${{ secrets.HARBOR_PASSWORD }}

      - name: Build and Push
        uses: docker/build-push-action@v7
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest            

这里使用了几个官方 Docker Actions:

docker/setup-buildx-action
docker/login-action
docker/build-push-action

相比手动执行:

docker build
docker tag
docker login
docker push

使用 Buildx Action 可以把构建、Tag 和 Push 组织成更加标准的 CI 流程。

同时建议生产镜像至少保留一个不可变版本 Tag:

harbor.example.com/backend/myapp:8f7a1c2

而不是只依赖:

harbor.example.com/backend/myapp:latest

latest 适合作为方便使用的浮动 Tag,但不适合作为生产环境唯一的版本标识。

从 GitHub Actions 自动部署服务器

镜像进入 Harbor 后,生产服务器只需要:

docker pull harbor.example.com/backend/myapp:8f7a1c2

然后更新服务:

docker compose up -d

因此最终链路可以设计成:

Git Push
   ↓
GitHub Actions
   ↓
Run Test
   ↓
Docker Build
   ↓
Push Harbor
   ↓
SSH
   ↓
Production Server
   ↓
docker compose pull
   ↓
docker compose up -d

例如部署 Job:

deploy:
  needs: build

  runs-on: ubuntu-latest

  steps:
    - name: Deploy
      uses: appleboy/ssh-action@v1
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        script: |
          cd /opt/myapp

          docker compose pull

          docker compose up -d

          docker image prune -f          

服务器上的 compose.yaml:

services:
  app:
    image: harbor.example.com/backend/myapp:${IMAGE_TAG}
    restart: unless-stopped
    ports:
      - "8080:8080"

部署时:

export IMAGE_TAG=8f7a1c2

docker compose pull
docker compose up -d

实际生产环境中,不建议简单地把所有部署命令堆在 SSH 脚本里,还应该进一步处理:

健康检查
版本校验
部署失败
自动回滚
日志
数据库迁移
并发部署

这些属于后续的生产部署专题。

使用 Environment 管理生产环境

GitHub Actions 提供 Environment,可以将:

development
staging
production

作为不同的部署环境。

例如:

deploy:
  needs: build

  runs-on: ubuntu-latest

  environment:
    name: production

  steps:
    - name: Deploy
      run: ./deploy.sh

GitHub Environment 可以配置环境级 Secrets、Variables 和部署保护规则,还可以要求人工审批后才能继续生产部署。(GitHub Deployment environments)

例如:

Build
  ↓
Test
  ↓
Push Image
  ↓
Production
  ↓
等待审批
  ↓
Deploy

这样可以避免:

git push
   ↓
自动部署生产

带来的风险。

GitHub 官方文档中也建议使用 Environment 来描述 production、staging、development 等部署目标,并可以通过保护规则限制部署。(GitHub deployment environments)

GitHub Actions 部署环境官方文档

Secrets、Variables 与权限

CI/CD 最大的问题之一不是「能不能自动部署」,而是:

如何安全地自动部署。

GitHub Actions 中不要直接写:

password: 123456

应该使用:

${{ secrets.HARBOR_PASSWORD }}

常见配置可以划分为:

Secrets
├── HARBOR_USERNAME
├── HARBOR_PASSWORD
├── SERVER_HOST
├── SERVER_USER
└── SERVER_SSH_KEY

Variables
├── REGISTRY
├── IMAGE_NAME
└── DEPLOY_PATH

可以简单理解:

Secrets
    ↓
敏感信息

Variables
    ↓
普通配置

例如:

env:
  REGISTRY: ${{ vars.REGISTRY }}
  IMAGE_NAME: ${{ vars.IMAGE_NAME }}

敏感信息:

password: ${{ secrets.HARBOR_PASSWORD }}

此外,还应该显式限制 Workflow 权限:

permissions:
  contents: read

默认不要给 Workflow 不必要的写权限。

GitHub 官方 Workflow 文档提供了 permissions、Secrets、Contexts 等相关配置说明。(GitHub Workflow syntax)

给 Workflow 增加缓存

CI/CD 中另一个常见问题是:

每次运行
    ↓
重新下载所有依赖
    ↓
速度很慢

例如 Composer:

composer install
        ↓
下载几十甚至几百个依赖

可以使用缓存减少重复下载。

GitHub Actions 提供 Dep