5、GitHub CI/CD 构建流程与原理详解
·
目录
📚 一、什么是 CI/CD?
CI = Continuous Integration(持续集成)
CD = Continuous Delivery/Deployment(持续交付/部署)
简单理解
传统开发流程:
代码 → 手动测试 → 手动打包 → 手动上传服务器 → 手动重启
CI/CD 流程:
代码 → 自动测试 → 自动打包 → 自动部署 → 自动通知
↑________________________↓
全自动!
🏗️ 二、GitHub Actions 架构
核心概念
┌─────────────────────────────────────────────────────────────┐
│ GitHub Repository │
├─────────────────────────────────────────────────────────────┤
│ │
│ .github/workflows/deploy.yml ← Workflow 配置文件 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Workflow(工作流) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Job 1 │ → │ Job 2 │ → │ Job 3 │ │ │
│ │ │ 测试 │ │ 构建 │ │ 部署 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ ↓ ↓ ↓ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Step 1 │ │ Step 1 │ │ Step 1 │ │ │
│ │ │ Step 2 │ │ Step 2 │ │ Step 2 │ │ │
│ │ │ Step 3 │ │ Step 3 │ │ Step 3 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
层级关系
Workflow(工作流)
└── Job(作业)- 可以并行或串行
└── Step(步骤)- 按顺序执行
└── Action(动作)- 具体执行的命令
🔄 三、完整构建流程
阶段 1:触发(Trigger)
on:
push: # 代码推送时触发
branches:
- main
- dev
pull_request: # PR 时触发
branches:
- main
schedule: # 定时触发
- cron: '0 2 * * *' # 每天凌晨 2 点
workflow_dispatch: # 手动触发
触发流程图:
开发者 push 代码
↓
GitHub 检测到 push 事件
↓
检查 .github/workflows/*.yml
↓
匹配触发条件?
├── 是 → 启动 Workflow
└── 否 → 忽略
阶段 2:分配 Runner(运行器)
jobs:
build:
runs-on: ubuntu-latest # 指定运行环境
Runner 是什么?
┌─────────────────────────────────────────────────────────────┐
│ GitHub 云端服务器 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Runner 1 │ │ Runner 2 │ │ Runner 3 │ │
│ │ ubuntu-22 │ │ windows-22 │ │ macos-14 │ │
│ │ │ │ │ │ │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │ 你的 │ │ │ │ 其他 │ │ │ │ 其他 │ │ │
│ │ │ Job │ │ │ │ Job │ │ │ │ Job │ │ │
│ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
可用的 Runner 类型:
| Runner | 系统 | 配置 |
|---|---|---|
ubuntu-latest |
Ubuntu 22.04 | 2核 CPU, 7GB RAM, 14GB SSD |
ubuntu-22.04 |
Ubuntu 22.04 | 同上 |
windows-latest |
Windows Server 2022 | 2核 CPU, 7GB RAM |
macos-latest |
macOS 14 | 3核 CPU, 14GB RAM |
阶段 3:执行 Job
┌─────────────────────────────────────────────────────────────┐
│ Job 执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 创建全新的虚拟机 │
│ ↓ │
│ 2. 克隆代码仓库 │
│ ↓ │
│ 3. 设置环境(Node.js, pnpm 等) │
│ ↓ │
│ 4. 安装依赖 │
│ ↓ │
│ 5. 执行构建命令 │
│ ↓ │
│ 6. 上传产物 / 部署 │
│ ↓ │
│ 7. 销毁虚拟机(每次都是全新的!) │
│ │
└─────────────────────────────────────────────────────────────┘
重要概念:每个 Job 都在全新的虚拟机中运行!
jobs:
job1:
runs-on: ubuntu-latest # 虚拟机 A
steps:
- run: echo "我在虚拟机 A"
job2:
runs-on: ubuntu-latest # 虚拟机 B(完全独立!)
steps:
- run: echo "我在虚拟机 B,看不到 A 的任何文件"
阶段 4:Step 执行
steps:
# Step 1: 使用预定义的 Action
- name: 📥 Checkout code
uses: actions/checkout@v4 # 从 GitHub Marketplace 获取
# Step 2: 执行 shell 命令
- name: 📦 Install dependencies
run: pnpm install
# Step 3: 多行命令
- name: 🏗️ Build
run: |
echo "开始构建..."
pnpm run build
echo "构建完成!"
Step 执行原理:
┌─────────────────────────────────────────────────────────────┐
│ Step 执行 │
├─────────────────────────────────────────────────────────────┤
│ │
│ uses: actions/checkout@v4 │
│ ↓ │
│ 1. 从 GitHub 下载 actions/checkout 仓库的 v4 版本 │
│ 2. 执行该 Action 的 action.yml 定义的逻辑 │
│ 3. 将你的代码克隆到 /home/runner/work/repo/repo │
│ │
│ run: pnpm install │
│ ↓ │
│ 1. 在 bash shell 中执行命令 │
│ 2. 工作目录是 /home/runner/work/repo/repo │
│ 3. 输出实时显示在 GitHub Actions 日志中 │
│ │
└─────────────────────────────────────────────────────────────┘
📦 四、数据传递机制
1. Job 之间传递文件(Artifacts)
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm install && pnpm build
# 上传构建产物
- uses: actions/upload-artifact@v4
with:
name: dist-files
path: dist/
deploy:
needs: build # 依赖 build job
runs-on: ubuntu-latest
steps:
# 下载构建产物
- uses: actions/download-artifact@v4
with:
name: dist-files
path: dist/
原理图:
┌─────────────┐ ┌─────────────┐
│ Build │ │ Deploy │
│ Job │ │ Job │
│ │ │ │
│ dist/ │ upload-artifact │ │
│ ├─index.html ──────────────────→│ dist/ │
│ ├─app.js │ download-artifact│ ├─index.html
│ └─style.css│ │ ├─app.js │
│ │ │ └─style.css│
└─────────────┘ └─────────────┘
虚拟机 A GitHub 存储 虚拟机 B
2. Step 之间传递数据(Outputs)
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
- id: get_version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "部署版本: ${{ needs.build.outputs.version }}"
3. 缓存机制(Cache)
- uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pnpm-
缓存原理:
第一次运行:
┌─────────────────────────────────────────────────────────────┐
│ pnpm install │
│ ↓ │
│ 下载所有依赖包(耗时 2 分钟) │
│ ↓ │
│ 保存到 ~/.pnpm-store │
│ ↓ │
│ 上传缓存到 GitHub(key: pnpm-abc123) │
└─────────────────────────────────────────────────────────────┘
第二次运行:
┌─────────────────────────────────────────────────────────────┐
│ 检查缓存 key: pnpm-abc123 │
│ ↓ │
│ 找到缓存!下载到 ~/.pnpm-store │
│ ↓ │
│ pnpm install(只需 10 秒,因为依赖已存在) │
└─────────────────────────────────────────────────────────────┘
🔐 五、Secrets 机制
Secrets 是什么?
Secrets = 加密存储的敏感信息(密码、密钥、Token 等)
配置位置
GitHub 仓库 → Settings → Secrets and variables → Actions
使用方式
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }} # 注入为环境变量
run: |
echo "API_KEY 已注入,但不会显示在日志中"
# 日志中会显示 ***
安全机制
┌─────────────────────────────────────────────────────────────┐
│ Secrets 安全机制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 加密存储:使用 libsodium sealed box 加密 │
│ 2. 运行时解密:只在 Job 运行时解密 │
│ 3. 日志脱敏:自动将 secret 值替换为 *** │
│ 4. Fork 隔离:Fork 仓库无法访问原仓库的 secrets │
│ 5. 环境隔离:可以设置不同环境的 secrets │
│ │
└─────────────────────────────────────────────────────────────┘
🌊 六、完整流程图
┌─────────────────────────────────────────────────────────────────────────┐
│ GitHub Actions 完整流程 │
└─────────────────────────────────────────────────────────────────────────┘
开发者 GitHub 服务器
│ │ │
│ 1. git push │ │
│ ───────────────────────→│ │
│ │ │
│ │ 2. 触发 Workflow │
│ │ ┌─────────────────┐ │
│ │ │ 读取 .github/ │ │
│ │ │ workflows/*.yml │ │
│ │ └─────────────────┘ │
│ │ │
│ │ 3. 分配 Runner │
│ │ ┌─────────────────┐ │
│ │ │ 创建 Ubuntu │ │
│ │ │ 虚拟机 │ │
│ │ └─────────────────┘ │
│ │ │
│ │ 4. 执行 Jobs │
│ │ ┌─────────────────┐ │
│ │ │ checkout │ │
│ │ │ setup-node │ │
│ │ │ pnpm install │ │
│ │ │ pnpm build │ │
│ │ └─────────────────┘ │
│ │ │
│ │ 5. 部署 │
│ │ ─────────────────────────→│
│ │ SCP/SSH │
│ │ │
│ │ 6. 通知结果 │
│ ←───────────────────────│ │
│ ✅ 部署成功 │ │
│ │ │
📝 七、实际案例解析
你的 Workflow 执行过程
name: Build and Deploy
on:
push:
branches: [dev]
执行时间线:
时间 事件
─────────────────────────────────────────────────────────────
0:00 你执行 git push origin dev
0:01 GitHub 收到 push 事件
0:02 GitHub 检测到 .github/workflows/deploy.yml
0:03 匹配 on.push.branches: [dev] ✓
0:04 创建 Workflow Run #13
0:05 分配 Runner(ubuntu-latest)
┌─── lint-and-test Job ───┐
0:10 │ 创建虚拟机 │
0:15 │ actions/checkout │ 克隆代码
0:20 │ pnpm/action-setup │ 安装 pnpm
0:25 │ actions/setup-node │ 安装 Node.js
0:30 │ pnpm install │ 安装依赖
0:45 │ pnpm run lint │ 代码检查
1:00 │ pnpm run test │ 运行测试
1:05 │ Job 完成 ✓ │
└─────────────────────────┘
┌─── build Job ───────────┐
1:10 │ 创建新虚拟机 │ (全新的!)
1:15 │ actions/checkout │
1:20 │ pnpm/action-setup │
1:25 │ actions/setup-node │
1:30 │ pnpm install │
2:00 │ pnpm run build │ 构建项目
2:30 │ upload-artifact │ 上传 dist/
2:35 │ Job 完成 ✓ │
└─────────────────────────┘
┌─── deploy Job ──────────┐
2:40 │ 创建新虚拟机 │
2:45 │ download-artifact │ 下载 dist/
2:50 │ scp-action │ 上传到服务器
3:00 │ ssh-action │ 重启 Nginx
3:05 │ Job 完成 ✓ │
└─────────────────────────┘
3:10 Workflow 完成 ✅
🔧 八、常见问题与原理
Q1: 为什么每次都要重新安装依赖?
因为每个 Job 都在全新的虚拟机中运行!
上一个 Job 安装的依赖,下一个 Job 看不到。
解决方案:
1. 使用 cache 缓存依赖
2. 使用 artifacts 传递文件
3. 合并 Jobs(减少虚拟机创建)
Q2: 为什么 secrets 不能在日志中显示?
安全机制!GitHub 会自动检测日志中的 secret 值,
并替换为 *** 防止泄露。
Q3: 为什么 Job 之间不能直接共享文件?
因为它们运行在不同的虚拟机上!
必须通过 artifacts 或 cache 传递。
Q4: Runner 的文件系统结构
/home/runner/
├── work/
│ └── your-repo/
│ └── your-repo/ ← 你的代码在这里
│ ├── package.json
│ ├── src/
│ └── dist/ ← 构建产物
├── .npm/ ← npm 缓存
├── .pnpm-store/ ← pnpm 缓存
└── ...
📊 九、优化建议
1. 合并 Jobs 减少时间
# ❌ 慢:3 个 Job = 3 次虚拟机创建
jobs:
lint:
...
test:
needs: lint
...
build:
needs: test
...
# ✅ 快:1 个 Job = 1 次虚拟机创建
jobs:
build:
steps:
- run: pnpm lint
- run: pnpm test
- run: pnpm build
2. 使用缓存
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm' # 自动缓存 pnpm 依赖
3. 并行执行
jobs:
lint:
runs-on: ubuntu-latest
...
test:
runs-on: ubuntu-latest # 与 lint 并行!
...
build:
needs: [lint, test] # 等待两个都完成
...
🎯 总结
GitHub Actions =
触发器(on)
+ 运行环境(runs-on)
+ 执行步骤(steps)
+ 数据传递(artifacts/cache/outputs)
+ 安全机制(secrets)
核心原理:
- 事件驱动:push/PR/定时 触发
- 容器隔离:每个 Job 独立虚拟机
- 声明式配置:YAML 定义流程
- 可组合:Actions 可复用
更多推荐




所有评论(0)