- project_rules §15.7: 参考项目位置 + 关键配置点(container/runner 标签/部署方式/网络) - cicd-runbook §10: 参考项目对照表(配置项、关键差异、修改流程、冲突处理) - 修正 cicd-runbook runner 标签:self-hosted,deploy → ubuntu-latest
18 KiB
CI/CD 使用手册(CI/CD Runbook)
版本:1.0 日期:2026-07-08 适用范围:Edu 微服务项目(Gitea Actions + Gitea Container Registry + Runner 直接部署) 关联文档:project_rules §15、本地启动手册、多 AI 协作指南
1. 架构总览
开发 AI 推送 PR 协调 AI 合并到 main
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ ci-*.yml │ │ docker.yml │
│ lint+test+ │ │ build+push │
│ build+scan │ │ 镜像到 Gitea│
└─────────────┘ │ Registry │
└──────┬──────┘
│
▼
┌─────────────┐
│ deploy.yml │
│ Runner 直接 │
│ docker │
│ compose up │
└─────────────┘
1.1 组件清单
| 组件 | 说明 |
|---|---|
| Gitea | git.eazygame.cn,代码托管 + Actions + Container Registry |
| Gitea Actions | CI 运行器,兼容 GitHub Actions 语法 |
| Gitea Container Registry | 镜像仓库,地址 git.eazygame.cn/xiner/edu/<service>:<tag> |
| Deploy Runner | 跑在服务器上的 actrunner,标签 ubuntu-latest |
| 服务器 MySQL | 已有容器,端口 3306,容器名 edu-mysql(假设) |
| 服务器 Redis | 已有容器,端口 6379,容器名 edu-redis(假设) |
1.2 流水线文件
| 文件 | 触发 | 作用 |
|---|---|---|
.github/workflows/ci-ts.yml |
TS 路径变更 | lint + typecheck + test + build + arch:scan + docker-build |
.github/workflows/ci-go.yml |
Go 路径变更 | vet + build + test + docker-build |
.github/workflows/ci-py.yml |
Python 路径变更 | ruff + pytest(P4 阶段) |
.github/workflows/ci-proto.yml |
proto 变更 | buf lint + buf breaking |
.github/workflows/docker.yml |
push main / tag v* |
构建并推送镜像到 Gitea Registry |
.github/workflows/deploy.yml |
docker.yml 完成后 / 手动 | Runner 执行 docker compose up |
2. 一次性配置(SRE AI 或人类执行)
2.1 启用 Gitea Actions
在 Gitea 仓库 xiner/Edu 设置:
- 仓库设置 → Actions → 启用
- 安装 actrunner(若未安装):
# 在服务器上 wget https://gitea.com/gitea/act_runner/raw/branch/main/act_runner chmod +x act_runner ./act_runner register --instance https://git.eazygame.cn --token <TOKEN> - 配置 runner 标签为
ubuntu-latest(CI 与部署共用)
2.2 启用 Gitea Container Registry
Gitea 1.20+ 自带 Container Registry,无需额外启用。确认:
# 测试登录
docker login git.eazygame.cn -u <你的用户名>
# 输入密码或 token
2.3 准备服务器网络
确保 MySQL/Redis 容器与应用容器在同一 Docker 网络:
# 创建共享网络(若不存在)
docker network create edu-shared
# 将已有的 MySQL/Redis 加入网络(容器名按实际替换)
docker network connect edu-shared edu-mysql # 或实际容器名
docker network connect edu-shared edu-redis # 或实际容器名
查看实际容器名:
docker ps --format "{{.Names}}" | grep -E "mysql|redis"
2.4 初始化部署目录
# 1. 创建部署目录
sudo mkdir -p /opt/edu
sudo chown -R $USER:$USER /opt/edu
# 2. 拷贝 compose 文件
cd /path/to/Edu
cp infra/docker-compose.deploy.yml /opt/edu/docker-compose.yml
# 3. 创建生产 .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
# 4. 登录 Gitea Container Registry(Runner 用户)
docker login git.eazygame.cn -u <user> -p <token>
# token 在 Gitea → 设置 → 应用 → 生成新 token
2.5 配置 Gitea Secrets
在 Gitea 仓库 → 设置 → Actions → Secrets,添加:
| Secret 名 | 用途 | 必填 |
|---|---|---|
GITHUB_TOKEN |
Gitea 自动注入,推送镜像用 | 自动 |
GITEA_REGISTRY_USER |
deploy.yml 登录 registry(可选) | 否 |
GITEA_REGISTRY_TOKEN |
deploy.yml 登录 registry(可选) | 否 |
GITHUB_TOKEN由 Gitea Actions 自动注入,无需手动配置。docker.yml用它推送镜像。deploy.yml中 Runner 已在服务器上,若已docker login过则无需 secrets。
3. 日常使用
3.1 开发 AI 提 PR
开发 AI 推送特性分支后创建 PR,自动触发 CI:
PR 创建 → ci-ts.yml / ci-go.yml 运行
├─ quality(lint + typecheck + test + build)
├─ arch-scan(架构扫描)
└─ docker-build(构建不推送)
CI 全绿 → 协调 AI 审核合并
3.2 协调 AI 合并 PR
PR 合并到 main 后,自动触发:
push main →
├─ ci-*.yml(确保 main 稳定)
└─ docker.yml → 构建并推送镜像到 git.eazygame.cn/xiner/edu/<service>:latest
│
▼
deploy.yml → Runner 执行 docker compose pull && up -d
│
▼
健康检查(轮询 /healthz)
3.3 手动部署
在 Gitea → Actions → Deploy → Run workflow:
| 参数 | 说明 |
|---|---|
image_tag |
部署指定 tag(默认 latest) |
rollback |
勾选则跳过 pull,用本地已有镜像重新启动 |
3.4 发布正式版本(tag)
git tag -a v1.0.0 -m "P1 阶段首次发布"
git push origin v1.0.0
触发 docker.yml 推送 v1.0.0 tag 镜像,再手动触发 deploy.yml 部署 v1.0.0。
4. 镜像管理
4.1 镜像地址
git.eazygame.cn/xiner/edu/<service>:<tag>
| service | 说明 |
|---|---|
api-gateway |
Go 网关 |
classes |
NestJS 班级服务 |
teacher-portal |
Next.js 前端 |
4.2 Tag 策略
| tag | 来源 | 用途 |
|---|---|---|
latest |
main 分支最新构建 | 默认部署 |
main-<sha> |
main 分支每次构建 | 可追溯 |
v<version> |
打 v* tag 触发 |
正式版本 |
v<major>.<minor> |
打 v* tag 触发 |
大版本追踪 |
4.3 查看与清理镜像
在 Gitea → 用户设置 → Packages,可查看与管理所有镜像。
清理旧 tag(手动):
# 列出所有 tag
docker images git.eazygame.cn/xiner/edu/classes --format "{{.Tag}}"
# 删除本地旧镜像
docker rmi git.eazygame.cn/xiner/edu/classes:main-abc1234
5. 部署验证
5.1 CI 自动验证
deploy.yml 部署后会自动轮询健康检查(最多 10 次,每次间隔 6 秒):
http://localhost:8080/healthz— api-gatewayhttp://localhost:3001/healthz— classeshttp://localhost:3000/— teacher-portal
失败时输出容器日志,方便排查。
5.2 手动验证
# SSH 到服务器后
cd /opt/edu
# 容器状态
docker compose ps
# 健康检查
curl http://localhost:8080/healthz # api-gateway
curl http://localhost:3001/healthz # classes
curl http://localhost:3000/ # teacher-portal
# 鉴权验证(生产模式 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.3 外部访问
若服务器有公网 IP(如 1.2.3.4):
- 前端:
http://1.2.3.4:3000 - API 网关:
http://1.2.3.4:8080 - 健康检查:
http://1.2.3.4:8080/healthz
6. 回滚
6.1 CI 回滚(推荐)
在 Gitea → Actions → Deploy → Run workflow:
image_tag:填入上一个稳定版本的 tag(如main-abc1234)rollback:勾选(跳过 pull,用本地已有镜像)
6.2 手动回滚
cd /opt/edu
# 查看本地镜像
docker images git.eazygame.cn/xiner/edu/classes --format "{{.Tag}}"
# 回滚到指定版本
IMAGE_TAG=main-abc1234 docker compose up -d
6.3 紧急回滚(Git revert)
若代码有问题:
git revert <bad-commit>
git push origin main
# CI 自动重新构建部署
7. 常见问题
7.1 CI: pnpm install 失败
症状:pnpm install --frozen-lockfile 报错。
排查:
pnpm-lock.yaml未提交:git add pnpm-lock.yaml && git commit -m "chore(deps): 更新 lockfile"- Node 版本不匹配:确认 CI 用 Node 20,本地一致
7.2 CI: docker-build 失败
症状:docker/build-push-action 报错。
排查:
- Dockerfile 语法错误:本地
docker build验证 - 路径错误:确认
context与file参数
7.3 CD: 镜像拉取失败
症状:docker compose pull 报 unauthorized。
修复:
# 服务器上重新登录
docker login git.eazygame.cn -u <user> -p <token>
# 或配置 Gitea secrets GITEA_REGISTRY_USER / GITEA_REGISTRY_TOKEN
7.4 CD: 健康检查失败
症状:deploy.yml 健康检查 10 次后失败。
排查:
- 查看日志:
cd /opt/edu && docker compose logs api-gateway - 常见原因:
DATABASE_URL连不上 MySQL(检查容器名与网络)JWT_SECRET未配置- 端口被占用:
docker ps检查冲突
7.5 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
7.6 Gitea Actions 未触发
症状:push 后 Actions 不运行。
排查:
- 仓库设置 → Actions → 确认已启用
- Runner 在线:Gitea → 设置 → Actions → Runners
- workflow 文件在
.github/workflows/目录(非.gitea/workflows/) on:触发条件匹配(paths 过滤)
7.7 workflow_run 不触发
症状:docker.yml 完成后 deploy.yml 不自动运行。
原因:Gitea Actions 对 workflow_run 触发支持可能不完整。
解决:
- 改用手动触发:Actions → Deploy → Run workflow
- 或在
docker.yml末尾加 job 调用 deploy(需要workflow_call)
8. 排查命令速查
# === 在服务器上 ===
# 查看所有 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
# 进入容器
docker exec -it edu-api-gateway sh
docker exec -it edu-classes sh
# 查看网络
docker network inspect edu-shared
# 查看镜像列表
docker images git.eazygame.cn/xiner/edu
# === 在 Gitea Web UI ===
# 仓库 → Actions → 查看流水线运行记录
# 用户设置 → Packages → 管理镜像
# 仓库 → 设置 → Secrets → 管理 CI 密钥
# 仓库 → 设置 → Actions → Runners → 查看 Runner 状态
9. 安全注意事项
.env文件不入库:.gitignore已忽略,仅存在于服务器/opt/edu/.env- JWT_SECRET 强随机:生产必须用
openssl rand -hex 32生成 - DEV_MODE=false:docker-compose.deploy.yml 强制设为
false - Gitea Token 权限最小化:仅给
package:write和contents:read - Runner 隔离:deploy runner 仅跑在目标服务器,不暴露公网 SSH
- 镜像扫描(P6):
docker.yml后续加 Trivy 扫描步骤
10. 参考实现(强制参照)
所有 CI/CD 配置变更必须参照同机的 E:\Desktop\CICD\ 项目(Gitea 单仓库 CI 实现),不另行自创。
10.1 参考项目位置
| 路径 | 内容 |
|---|---|
E:\Desktop\CICD\.gitea\workflows\ci.yml |
主 CI 配置(build-deploy + 安全扫描) |
E:\Desktop\CICD\.gitea\workflows\dr-drill.yml |
灾备演练 |
E:\Desktop\CICD\.gitea\workflows\lighthouse.yml |
性能审计 |
E:\Desktop\CICD\.gitea\workflows\security.yml |
安全扫描 |
E:\Desktop\CICD\.trae\rules\project_rules.md |
参考项目规则(对照学习) |
10.2 关键配置点(必须沿用)
参考项目 ci.yml 的核心配置:
| 配置项 | 参考项目值 | Edu 项目取值 |
|---|---|---|
| Runner 标签 | CDCD |
ubuntu-latest |
| TS job 容器 | dockerreg.eazygame.cn/node-with-docker:22 |
同(带 docker 的 node 22 镜像) |
| Go quality 容器 | (无 Go) | golang:1.22-alpine |
| Go docker-build | (无 Go) | node-with-docker:22 + setup-go |
| 部署方式 | docker build + docker run --network 1panel-network |
docker compose pull && up(多服务编排) |
| 网络 | 1panel-network |
edu-shared(连接已有 MySQL/Redis) |
| npm 代理 | docker gateway IP 172.17.0.1:7890 |
不配置(runner 已全局代理) |
| Next.js 构建 | standalone + 复制 public/.next/static |
teacher-portal 沿用 |
| 环境变量 | secrets 注入 DATABASE_URL/NEXTAUTH_SECRET |
secrets 注入 DATABASE_URL/JWT_SECRET 等 |
| 定时备份 | schedule: cron "0 2 * * *" |
待 P6 阶段实现 |
10.3 关键差异(Edu vs 参考项目)
| 维度 | 参考项目 | Edu 项目 |
|---|---|---|
| 架构 | 单体 Next.js | 微服务(TS + Go + Python + Proto) |
| 包管理器 | npm | pnpm(TS)/ go mod(Go)/ uv(Python) |
| 服务数量 | 1 个(nextjs-app) | 3+(api-gateway / classes / teacher-portal) |
| 部署方式 | docker run 单容器 | docker compose 多服务编排 |
| CI 拆分 | 单 ci.yml 大而全 | 按语言拆 ci-ts/ci-go/ci-py/ci-proto |
| 镜像仓库 | 本地 nextjs-app:latest |
Gitea Registry git.eazygame.cn/xiner/edu/<svc>:<tag> |
10.4 修改 workflow 的流程
- 先读参考项目同类配置:
Read E:\Desktop\CICD\.gitea\workflows\ci.yml - 比对差异:参照 §10.3 表格,确认 Edu 项目的特殊取值
- 修改 Edu workflow:保持关键配置点(container、runner 标签)一致
- YAML 语法校验:
python -c "import yaml; yaml.safe_load(open('<file>'))" - 提交并推送:触发 CI 验证
10.5 当参考项目与本规范冲突时
- 架构差异(单体 vs 微服务):以 Edu 项目规范为准
- 部署方式(docker run vs compose):以 Edu 项目规范为准
- 镜像/网络/标签:以 Edu 项目规范为准
- CI 容器化、构建流程、缓存策略:参照参考项目