- 删除 6 个分散 workflow(ci-ts/ci-go/ci-proto/ci-py/docker/deploy) - 合并为单个 ci.yml:3 个 quality job 并行 + deploy job 串行 - 全部使用官方镜像(node:22-alpine/golang:1.22-alpine/bufbuild/buf/docker:25-git) - deploy job 用 DooD 模式挂载 /var/run/docker.sock - docker-compose.deploy.yml 改用 build: 替代 image:(no-push) - 新增 docker-compose.tools.yml 一次性预拉所有 CI 镜像 - 支持 workflow_dispatch 指定 commit_sha 回滚 - 更新 project_rules §15 与 cicd-runbook.md 为 no-push 模式 - 新增设计文档 docs/superpowers/specs/2026-07-08-cicd-no-push-local-build-design.md
11 KiB
CI/CD 使用手册(CI/CD Runbook)
版本:2.0(no-push 本地构建模式) 日期:2026-07-08 适用范围:Edu 微服务项目(Gitea Actions + Runner 本地构建部署) 关联文档:project_rules §15、本地启动手册、多 AI 协作指南 设计文档:2026-07-08-cicd-no-push-local-build-design.md
1. 架构总览
PR 触发(开发 AI 提 PR) push main 触发(协调 AI 合并)
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ ci.yml │ │ ci.yml │
│ quality-ts (node:22) │ │ quality-ts (node:22) │
│ quality-go (golang) │ │ quality-go (golang) │
│ quality-proto (buf) │ │ quality-proto (buf) │
│ (并行,不部署) │ │ │ │
└─────────────────────────┘ │ ▼ │
│ deploy job │
│ (docker:25-git) │
│ 挂载 /var/run/docker.sock│
│ │ │
│ ▼ │
│ docker compose up │
│ --build(本地构建) │
│ │ │
│ ▼ │
│ 健康检查轮询 │
└─────────────────────────┘
1.1 核心特点
- 单文件管理:一个
.github/workflows/ci.yml管全部 CI/CD - no-push 本地构建:不推送到 registry,构建即部署
- 不依赖自建镜像:全部使用官方镜像(node/golang/buf/docker)
- DooD 模式:deploy job 容器内挂载宿主机
/var/run/docker.sock
1.2 组件清单
| 组件 | 说明 |
|---|---|
| Gitea | git.eazygame.cn,代码托管 + Actions |
| Gitea Actions | CI 运行器,兼容 GitHub Actions 语法 |
| actrunner | 跑在服务器上的 runner,标签 ubuntu-latest |
| 服务器 MySQL | 已有容器,端口 3306 |
| 服务器 Redis | 已有容器,端口 6379 |
1.3 流水线文件
| 文件 | 触发 | 作用 |
|---|---|---|
.github/workflows/ci.yml |
PR + push main + workflow_dispatch | quality(3 job 并行)+ deploy(仅 push main) |
infra/docker-compose.deploy.yml |
deploy job 调用 | 多服务编排(build: 替代 image:) |
infra/docker-compose.tools.yml |
手动执行 | 一次性预拉所有 CI 镜像 |
2. 一次性配置
2.1 启用 Gitea Actions + actrunner
- 仓库设置 → Actions → 启用
- 安装 actrunner 并注册(标签
ubuntu-latest) - 配置 actrunner 允许挂载 docker.sock:
# /etc/gitea/act_runner/config.yaml
container:
valid_volumes:
- /var/run/docker.sock
2.2 预拉所有 CI 镜像(一次性)
# 在服务器上,进入 Edu 仓库目录
cd /path/to/Edu
docker compose -f infra/docker-compose.tools.yml pull
拉取的镜像清单:
| 镜像 | 用途 |
|---|---|
node:22-alpine |
CI quality-ts job |
golang:1.22-alpine |
CI quality-go job + api-gateway builder |
bufbuild/buf:latest |
CI quality-proto job |
docker:25-git |
CI deploy job(自带 docker CLI + compose v2 + git) |
node:20-alpine |
teacher-portal / classes builder |
alpine:3.20 |
api-gateway runner |
mysql:8.0 |
开发测试用(生产用已有的) |
redis:7-alpine |
开发测试用(生产用已有的) |
2.3 准备服务器网络
# 创建共享网络(若不存在)
docker network create edu-shared
# 将已有的 MySQL/Redis 加入网络(容器名按实际替换)
docker network connect edu-shared <实际mysql容器名>
docker network connect edu-shared <实际redis容器名>
2.4 初始化部署目录
# 1. 创建部署目录
sudo mkdir -p /opt/edu
sudo chown -R $USER:$USER /opt/edu
# 2. 创建生产 .env(从模板)
cp infra/deploy.env.example /opt/edu/.env
vim /opt/edu/.env
# 必须修改:
# JWT_SECRET=<openssl rand -hex 32 生成的随机值>
# DATABASE_URL=mysql://<user>:<pass>@<mysql容器名>:3306/<db>
# REDIS_URL=redis://<redis容器名>:6379
注意:
/opt/edu/repo/子目录由 CI 自动同步,不需要手动准备。
3. 日常使用
3.1 开发 AI 提 PR
PR 创建 → ci.yml 运行
├─ quality-ts(pnpm lint + typecheck + test + build)
├─ quality-go(go vet + build + test)
└─ quality-proto(buf lint + buf breaking)
(3 个 job 并行,仅 quality,不部署)
CI 全绿 → 协调 AI 审核合并
3.2 协调 AI 合并 PR
PR 合并到 main 后自动触发:
push main → ci.yml
├─ quality-* (3 个 job 并行,确保 main 稳定)
└─ deploy job(needs: [quality-ts, quality-go, quality-proto])
│
▼
同步代码到 /opt/edu/repo/
│
▼
docker compose up -d --build(本地构建 3 个服务)
│
▼
健康检查轮询(10 次 × 6 秒)
3.3 手动触发部署
在 Gitea → Actions → CI → Run workflow:
commit_sha(可选):回滚到指定 commit,留空则部署当前 HEAD
3.4 不再支持 tag 发布
no-push 模式下,镜像不存放到 registry,因此不再使用 git tag v* 触发发布。版本管理通过 git commit SHA 追溯。
4. 部署验证
4.1 CI 自动验证
deploy job 部署后会自动轮询健康检查:
http://localhost:8080/healthz— api-gatewayhttp://localhost:3001/healthz— classeshttp://localhost:3000/— teacher-portal
失败时输出容器状态与日志,方便排查。
4.2 手动验证
cd /opt/edu
# 容器状态
docker compose ps
# 健康检查
curl http://localhost:8080/healthz
curl http://localhost:3001/healthz
curl http://localhost:3000/
# 鉴权验证(生产模式 dev-token 应被拒绝)
curl -H "Authorization: Bearer dev-token" http://localhost:8080/api/v1/classes
# 预期:401 INVALID_TOKEN
# 查看日志
docker compose logs -f api-gateway
docker compose logs -f classes
docker compose logs -f teacher-portal
5. 回滚
5.1 git revert(推荐)
git revert <bad-commit>
git push origin main
# CI 自动重新构建部署
5.2 手动触发指定 commit
在 Gitea → Actions → CI → Run workflow:
commit_sha:填入上一个稳定版本的 commit SHA
CI 会 checkout 指定 commit → 本地重新 build → deploy。
5.3 不再支持镜像 tag 回滚
no-push 模式下没有 registry tag,不能像旧方案那样切换 IMAGE_TAG。回滚必须重新构建。
6. 常见问题
6.1 CI: pnpm install 失败
症状:pnpm install --frozen-lockfile 报错。
排查:
pnpm-lock.yaml未提交:git add pnpm-lock.yaml && git commit- Node 版本不匹配:确认 CI 用 Node 22,本地一致
6.2 CI: deploy job 无法访问 docker
症状:docker compose up 报 Cannot connect to the Docker daemon。
原因:actrunner 没有挂载 /var/run/docker.sock,或 valid_volumes 未配置。
修复:
# /etc/gitea/act_runner/config.yaml
container:
valid_volumes:
- /var/run/docker.sock
重启 actrunner 后重试。
6.3 CD: 健康检查失败
症状:deploy job 健康检查 10 次后失败。
排查:
- CI 输出会自动打印容器状态和日志
- 常见原因:
DATABASE_URL连不上 MySQL(检查容器名与网络)JWT_SECRET未配置- 端口被占用:
docker ps检查冲突 /opt/edu/.env不存在或格式错误
6.4 CD: 连不上 MySQL/Redis
症状:classes 容器报 ECONNREFUSED edu-mysql:3306。
修复:
# 1. 确认 MySQL 容器名
docker ps --format "{{.Names}}" | grep mysql
# 2. 确认 MySQL 在 edu-shared 网络
docker network inspect edu-shared | grep -A5 Containers
# 3. 若不在,加入网络
docker network connect edu-shared <实际mysql容器名>
# 4. 修改 /opt/edu/.env 的 DATABASE_URL
# DATABASE_URL=mysql://edu:changeme@<实际容器名>:3306/next_edu_cloud
6.5 Gitea Actions 未触发
排查:
- 仓库设置 → Actions → 确认已启用
- Runner 在线:Gitea → 设置 → Actions → Runners
- workflow 文件在
.github/workflows/目录 on:触发条件匹配
7. 排查命令速查
# === 在服务器上 ===
# 查看所有 edu 容器
docker compose -f /opt/edu/docker-compose.yml ps
# 查看实时日志
docker compose -f /opt/edu/docker-compose.yml logs -f
# 重启单个服务
docker compose -f /opt/edu/docker-compose.yml restart api-gateway
# 重新构建并启动(手动触发部署)
cd /opt/edu/repo
git pull
cd /opt/edu
docker compose up -d --build
# 进入容器
docker exec -it edu-api-gateway sh
docker exec -it edu-classes sh
# 查看网络
docker network inspect edu-shared
# 查看本地镜像
docker images | grep -E "node|golang|docker|alpine|buf"
# === 在 Gitea Web UI ===
# 仓库 → Actions → 查看流水线运行记录
# 仓库 → 设置 → Actions → Runners → 查看 Runner 状态
8. 安全注意事项
.env文件不入库:.gitignore已忽略,仅存在于服务器/opt/edu/.env- JWT_SECRET 强随机:生产必须用
openssl rand -hex 32生成 - DEV_MODE=false:docker-compose.deploy.yml 强制设为
false - docker.sock 安全:actrunner 仅在部署服务器运行,已隔离
- Runner 隔离:runner 跑在服务器上,不暴露公网 SSH