大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
GitHub Actions
这一课你将能做到
- 为 Spring Boot 项目编写一条完整的 CI 流水线:编译、测试、打包、构建镜像
- 配置 GitHub Secrets 管理密钥,让流水线自动部署到服务器
- 说清楚 push / pull_request / workflow_dispatch 等触发器的区别和使用场景
为什么要学
你现在的部署流程:本地 mvn package → scp 上传 → SSH 重启服务。问题显而易见:你休假时没人能部署;你忘了跑测试就直接上线了;你在咖啡馆用别人电脑没法部署。CI/CD(持续集成/持续部署)的核心思想:代码一旦 push 到仓库,构建、测试、部署全部自动完成,不依赖任何个人的电脑。
人话:GitHub Actions 就是 GitHub 提供的「云上脚本执行器」。你在仓库里放一个 YAML 文件描述「代码 push 后要做什么」,GitHub 就分配一台虚拟机帮你执行:拉代码 → 装 JDK → Maven 打包 → 跑测试 → 构建 Docker 镜像 → SSH 到你服务器部署。全程自动化,执行记录公开可查。
先建立图像
一条典型的 CI/CD 流水线:
git push origin main
│
▼ GitHub 检测到 push 事件
┌─────────────────────────────────┐
│ GitHub Actions Runner │
│ ┌─────────┐ ┌─────────┐ │
│ │ Job: CI │ → │ Job: CD │ │
│ │ 构建+测试│ │ 部署 │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────┘
│ │
▼ ▼
测试报告/镜像 SSH → 服务器重启容器
核心概念关系:
Workflow(流水线)──包含──▶ Job(任务,并行跑在不同虚拟机上)
│
└──包含──▶ Step(步骤,串行执行)
│
└──▶ 每个 step 是一个 shell 命令或 Action(官方/社区封装好的操作)
跟着做
步骤 1:创建第一个 Workflow——CI(持续集成)
在仓库根目录创建 .github/workflows/ci.yml:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
# 步骤 1:拉代码(官方 Action)
- uses: actions/checkout@v4
# 步骤 2:装 JDK 17
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven # 缓存 Maven 依赖,构建提速 5 倍
# 步骤 3:编译 + 跑测试
- name: Build and Test
run: mvn clean verify
# 步骤 4:上传构建产物(供后续 job 或人工下载)
- name: Upload JAR
uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
retention-days: 7
push 到 GitHub 后,仓库的 Actions 标签页就能看到流水线运行记录,绿勾 = 通过,红叉 = 失败(点进去看日志)。
步骤 2:加入 Docker 镜像构建与推送
在 ci.yml 中追加第二个 job:
docker:
needs: build-and-test # 依赖 CI 通过才执行
runs-on: ubuntu-latest
# 只在 main 分支 push 时才构建镜像(PR 不构建)
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
# 登录镜像仓库(以阿里云 ACR 为例,Docker Hub 类似)
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
# 构建并推送(利用第 6 课写的多阶段 Dockerfile)
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
registry.cn-hangzhou.aliyuncs.com/myns/myapp:latest
registry.cn-hangzhou.aliyuncs.com/myns/myapp:${{ github.sha }}
cache-from: type=gha # GitHub 缓存层,加速构建
cache-to: type=gha,mode=max
步骤 3:配置 Secrets(密钥永远不进代码)
GitHub 仓库 → Settings → Secrets and variables → Actions → New repository secret:
ACR_USERNAME:镜像仓库用户名ACR_PASSWORD:镜像仓库密码SERVER_HOST:服务器 IPSERVER_SSH_KEY:部署用的 SSH 私钥(整个私钥文件内容粘贴进去)
YAML 里用 ${{ secrets.XXX }} 引用,日志中会自动打码显示 ***。
步骤 4:CD——自动部署到服务器
追加部署 job:
deploy:
needs: docker
runs-on: ubuntu-latest
# 只允许 main 分支部署到生产
environment: production # 可配置「需要人工审批」的保护规则
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: appuser
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /opt/myapp
docker compose pull app
docker compose up -d app
docker image prune -f
# 健康检查,失败则整个流水线标红
sleep 10
curl -f http://localhost:8080/actuator/health
现在完整的流水线:push main → 跑测试 → 构建镜像 → 推送仓库 → SSH 到服务器 → docker compose up -d → 健康检查。任何人 push 代码都会触发同一套流程,结果在 PR/Commit 旁直接可见。
步骤 5:手动触发与定时任务
on:
workflow_dispatch: # 在 GitHub 网页上点按钮手动触发
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options: [staging, production]
schedule:
- cron: '0 18 * * *' # 每天 UTC 18:00(北京时间凌晨 2 点)跑,适合夜间构建/数据备份
手动触发的参数在 step 里用 ${{ inputs.environment }} 引用。
讲清楚要点
- 事件驱动:Actions 的一切由 Git 事件触发——push、PR、tag、定时、手动。理解事件 = 理解第 3 课说的「Git 是自动化的扳机」。
- Job 并行、Step 串行:同一个 job 里的 step 按顺序执行;不同 job 默认并行(在不同虚拟机上),用
needs声明依赖后串行。CI 和 CD 分 job,是因为 PR 只需要 CI 不需要 CD。 - Secrets 只出不进:secret 写入后任何人(包括你自己)都无法在网页上读回,只能覆盖。日志里出现 secret 值会自动打码。这是唯一合法的密钥管理方式。
- Runner 是临时虚拟机:每次运行都是全新的 ubuntu 环境,跑完即销毁。所以不要指望文件在两次运行之间保留——产物用 artifact 传递,缓存用 cache action。
- environment 保护规则:生产环境部署配置
environment: production后,可以在仓库设置里要求「指定人员审批后才能执行」,防止误触发。
你可能会懵的地方
-
GitHub Actions 免费吗? 公开仓库完全免费;私有仓库每月 2000 分钟免费额度(Linux runner),个人项目绰绰有余。超时按分钟计费。
-
Action 和 Step 有什么区别? Step 是执行单元(一条 shell 命令或一个 Action 调用);Action 是封装好的可复用组件(如
actions/checkout@v4),官方和社区市场有上万个,避免重复造轮子。 -
流水线失败了怎么调试? 点进失败的 run → 点失败的 job → 展开红色的 step 看完整日志。还可以勾选「Enable debug logging」重跑获取更详细输出。复杂问题可用
tmateaction 直接 SSH 进 runner 调试。 -
github.sha 这种变量哪来的? GitHub 注入的上下文变量,常用的还有:
github.ref(分支/tag)、github.actor(触发者)、github.event_name(事件类型)、github.run_number(第几次运行)。 -
为什么 PR 触发的流水线读不到我的 secrets? 安全设计:来自 fork 仓库的 PR 默认拿不到 secrets(防止别人提个 PR 打印你的密钥)。受信任的内部 PR 可以正常读取。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 把密码写死在 YAML 里 | 进 Git 历史永久泄露 | 全部走 Secrets |
| PR 也触发生产部署 | 未评审代码直接上线 | 部署 job 加 if: github.ref == 'refs/heads/main' |
| 镜像只打 latest tag | 无法回滚到具体版本 | 同时打 commit sha tag |
| 测试失败也继续部署 | 带病上线 | job 间用 needs 串联,失败自动中断 |
| 构建不缓存依赖 | 每次全量下载 Maven 依赖,10 分钟起步 | setup-java 开 cache: maven,Docker 开 cache-from: type=gha |
卡点急救
- 流水线报
Permission denied (publickey):secrets 里的 SSH 私钥格式被破坏了——粘贴时必须包含-----BEGIN ...-----和-----END ...-----完整内容,且服务器~/.ssh/authorized_keys里有对应公钥。 - 健康检查步骤失败但服务实际起来了:
sleep 10不够,Spring Boot 启动慢。改成循环探测:for i in {1..30}; do curl -sf ... && break; sleep 2; done。 mvn命令找不到:runner 默认没有 Java/Maven,必须先执行actions/setup-java@v4步骤,顺序不能错。
面试常问
-
CI 和 CD 的区别?你们的流水线怎么设计的? CI(持续集成):代码合入主干前自动构建、测试,保证每次合入都是可用的;CD(持续交付/部署):CI 通过后自动发布到环境。回答时结合本课程的实践:PR 触发 CI(编译+测试),main 分支 push 触发完整 CD(镜像构建+推送+SSH 部署+健康检查),生产环境加了人工审批 environment。
-
流水线里的密钥怎么管理? 全部走 GitHub Secrets(或 Vault 等专业密钥管理),YAML 只引用变量名;Secrets 写入后不可读回、日志自动打码;服务器上只用部署专用密钥对,权限最小化(只允许执行 docker compose 命令,可配 sudoers 限制)。
-
如果部署到一半失败了怎么办? 分层回答:①流水线层面,健康检查失败会自动标红,通知到开发者;②应用层面,Docker 镜像不可变,回滚 =
docker compose up -d切回上一个 sha tag 的镜像,秒级完成;③预防层面,重要变更先发 staging 环境验证,或用蓝绿/金丝雀发布降低爆炸半径。
自测三问
- 为什么 PR 触发的 workflow 不应该包含部署 job?用什么条件控制?
needs、if、runs-on三个关键字分别控制什么?- 你的流水线每次都要 8 分钟,其中 5 分钟在下载 Maven 依赖,怎么优化?
小结一句话
GitHub Actions 把「push 代码」变成流水线的扳机,让构建、测试、部署成为每一次提交的自动后续动作。
延伸
- GitHub Actions 官方文档
- 下一课:Nginx——给部署好的应用加上反向代理、HTTPS 和静态资源服务。