Files
Edu/docs/standards/cicd-runbook.md
SpecialX a1d7fcfd71
Some checks failed
CI TypeScript / quality (push) Failing after 6s
CI TypeScript / arch-scan (push) Has been skipped
CI TypeScript / docker-build (map[context:. dockerfile:apps/teacher-portal/Dockerfile name:teacher-portal]) (push) Has been skipped
CI TypeScript / docker-build (map[context:. dockerfile:services/classes/Dockerfile name:classes]) (push) Has been skipped
Docker Build & Push / build-push (map[context:. dockerfile:apps/teacher-portal/Dockerfile name:teacher-portal]) (push) Failing after 3s
Docker Build & Push / build-push (map[context:. dockerfile:services/classes/Dockerfile name:classes]) (push) Failing after 10s
Docker Build & Push / build-push (map[context:./services/api-gateway dockerfile:services/api-gateway/Dockerfile name:api-gateway]) (push) Failing after 8s
CI Go / quality (push) Failing after 1m4s
CI Go / docker-build (push) Has been skipped
docs(docs): 新增 CICD 参考实现规范
- project_rules §15.7: 参考项目位置 + 关键配置点(container/runner 标签/部署方式/网络)

- cicd-runbook §10: 参考项目对照表(配置项、关键差异、修改流程、冲突处理)

- 修正 cicd-runbook runner 标签:self-hosted,deploy → ubuntu-latest
2026-07-08 16:12:45 +08:00

18 KiB
Raw Blame History

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 + pytestP4 阶段)
.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 设置:

  1. 仓库设置 → Actions → 启用
  2. 安装 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>
    
  3. 配置 runner 标签为 ubuntu-latestCI 与部署共用)

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 RegistryRunner 用户)
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 运行
  ├─ qualitylint + 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-gateway
  • http://localhost:3001/healthz — classes
  • http://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 外部访问

若服务器有公网 IP1.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:填入上一个稳定版本的 tagmain-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 报错。

排查

  1. pnpm-lock.yaml 未提交:git add pnpm-lock.yaml && git commit -m "chore(deps): 更新 lockfile"
  2. Node 版本不匹配:确认 CI 用 Node 20本地一致

7.2 CI: docker-build 失败

症状docker/build-push-action 报错。

排查

  1. Dockerfile 语法错误:本地 docker build 验证
  2. 路径错误:确认 contextfile 参数

7.3 CD: 镜像拉取失败

症状docker compose pullunauthorized

修复

# 服务器上重新登录
docker login git.eazygame.cn -u <user> -p <token>

# 或配置 Gitea secrets GITEA_REGISTRY_USER / GITEA_REGISTRY_TOKEN

7.4 CD: 健康检查失败

症状deploy.yml 健康检查 10 次后失败。

排查

  1. 查看日志:cd /opt/edu && docker compose logs api-gateway
  2. 常见原因:
    • 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 不运行。

排查

  1. 仓库设置 → Actions → 确认已启用
  2. Runner 在线Gitea → 设置 → Actions → Runners
  3. workflow 文件在 .github/workflows/ 目录(非 .gitea/workflows/
  4. 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. 安全注意事项

  1. .env 文件不入库.gitignore 已忽略,仅存在于服务器 /opt/edu/.env
  2. JWT_SECRET 强随机:生产必须用 openssl rand -hex 32 生成
  3. DEV_MODE=falsedocker-compose.deploy.yml 强制设为 false
  4. Gitea Token 权限最小化:仅给 package:writecontents:read
  5. Runner 隔离deploy runner 仅跑在目标服务器,不暴露公网 SSH
  6. 镜像扫描P6docker.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 pnpmTS/ go modGo/ uvPython
服务数量 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 的流程

  1. 先读参考项目同类配置Read E:\Desktop\CICD\.gitea\workflows\ci.yml
  2. 比对差异:参照 §10.3 表格,确认 Edu 项目的特殊取值
  3. 修改 Edu workflow保持关键配置点container、runner 标签)一致
  4. YAML 语法校验python -c "import yaml; yaml.safe_load(open('<file>'))"
  5. 提交并推送:触发 CI 验证

10.5 当参考项目与本规范冲突时

  • 架构差异(单体 vs 微服务):以 Edu 项目规范为准
  • 部署方式docker run vs compose以 Edu 项目规范为准
  • 镜像/网络/标签:以 Edu 项目规范为准
  • CI 容器化、构建流程、缓存策略:参照参考项目

详见 project_rules §15.7 参考实现


11. 相关文档