Files
Edu/docs/standards/cicd-runbook.md
SpecialX 68ddff1065
Some checks failed
CI / quality-go (push) Failing after 3s
CI / quality-proto (push) Failing after 2s
CI / quality-ts (push) Failing after 47s
CI / deploy (push) Has been skipped
refactor(infra): 重构为 no-push 本地构建部署模式
- 删除 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
2026-07-08 17:12:57 +08:00

364 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CI/CD 使用手册CI/CD Runbook
> 版本2.0no-push 本地构建模式)
> 日期2026-07-08
> 适用范围Edu 微服务项目Gitea Actions + Runner 本地构建部署)
> 关联文档:[project_rules §15](../../.trae/rules/project_rules.md)、[本地启动手册](./local-dev-runbook.md)、[多 AI 协作指南](./multi-ai-collaboration.md)
> 设计文档:[2026-07-08-cicd-no-push-local-build-design.md](../superpowers/specs/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 | quality3 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
1. 仓库设置 → Actions → 启用
2. 安装 actrunner 并注册(标签 `ubuntu-latest`
3. 配置 actrunner 允许挂载 docker.sock
```toml
# /etc/gitea/act_runner/config.yaml
container:
valid_volumes:
- /var/run/docker.sock
```
### 2.2 预拉所有 CI 镜像(一次性)
```bash
# 在服务器上,进入 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 准备服务器网络
```bash
# 创建共享网络(若不存在)
docker network create edu-shared
# 将已有的 MySQL/Redis 加入网络(容器名按实际替换)
docker network connect edu-shared <实际mysql容器名>
docker network connect edu-shared <实际redis容器名>
```
### 2.4 初始化部署目录
```bash
# 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-tspnpm lint + typecheck + test + build
├─ quality-gogo vet + build + test
└─ quality-protobuf lint + buf breaking
3 个 job 并行,仅 quality不部署
CI 全绿 → 协调 AI 审核合并
```
### 3.2 协调 AI 合并 PR
PR 合并到 main 后自动触发:
```
push main → ci.yml
├─ quality-* 3 个 job 并行,确保 main 稳定)
└─ deploy jobneeds: [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-gateway
- `http://localhost:3001/healthz` — classes
- `http://localhost:3000/` — teacher-portal
失败时输出容器状态与日志,方便排查。
### 4.2 手动验证
```bash
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推荐
```bash
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` 报错。
**排查**
1. `pnpm-lock.yaml` 未提交:`git add pnpm-lock.yaml && git commit`
2. 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` 未配置。
**修复**
```toml
# /etc/gitea/act_runner/config.yaml
container:
valid_volumes:
- /var/run/docker.sock
```
重启 actrunner 后重试。
### 6.3 CD: 健康检查失败
**症状**deploy job 健康检查 10 次后失败。
**排查**
1. CI 输出会自动打印容器状态和日志
2. 常见原因:
- `DATABASE_URL` 连不上 MySQL检查容器名与网络
- `JWT_SECRET` 未配置
- 端口被占用:`docker ps` 检查冲突
- `/opt/edu/.env` 不存在或格式错误
### 6.4 CD: 连不上 MySQL/Redis
**症状**classes 容器报 `ECONNREFUSED edu-mysql:3306`
**修复**
```bash
# 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 未触发
**排查**
1. 仓库设置 → Actions → 确认已启用
2. Runner 在线Gitea → 设置 → Actions → Runners
3. workflow 文件在 `.github/workflows/` 目录
4. `on:` 触发条件匹配
---
## 7. 排查命令速查
```bash
# === 在服务器上 ===
# 查看所有 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. 安全注意事项
1. **`.env` 文件不入库**`.gitignore` 已忽略,仅存在于服务器 `/opt/edu/.env`
2. **JWT_SECRET 强随机**:生产必须用 `openssl rand -hex 32` 生成
3. **DEV_MODE=false**docker-compose.deploy.yml 强制设为 `false`
4. **docker.sock 安全**actrunner 仅在部署服务器运行,已隔离
5. **Runner 隔离**runner 跑在服务器上,不暴露公网 SSH
---
## 9. 相关文档
- [project_rules §15 CI/CD 规范](../../.trae/rules/project_rules.md)
- [project_rules §14 多 AI 协作规范](../../.trae/rules/project_rules.md)
- [本地启动手册](./local-dev-runbook.md)
- [多 AI 协作指南](./multi-ai-collaboration.md)
- [known-issues](../troubleshooting/known-issues.md)
- [CI/CD 设计文档](../superpowers/specs/2026-07-08-cicd-no-push-local-build-design.md)