tunnel-system:从本地开发到 CI/CD + Docker + 模型权重 的完整流程
基于当前仓库实际配置整理(校对时间:2026-03-01)。 适合准备发博客、希望“一篇讲清楚”的读者。
0. 一句话总览
你 git push main 之后:
- GitHub Actions 在云端构建前后端镜像;
- 镜像推送到 GHCR(GitHub Container Registry);
- Actions 通过 SSH 登录服务器,执行
docker compose -f docker-compose.prod.yml pull && up -d; - 容器运行时通过 volume 挂载读取服务器本地模型权重(权重不在镜像里)。
1. 先建立正确心智模型(最重要)
- CI(持续集成):自动构建镜像(必要时可加测试)。
- CD(持续部署):把新镜像自动部署到服务器。
- Dockerfile:镜像“制造说明书”(主要在 CI 阶段使用)。
- docker-compose.prod.yml:生产“运行说明书”(主要在服务器部署时使用)。
- 核心边界:CI/CD 负责镜像与部署流程,不会自动上传你本地模型文件。
2. 项目里哪些文件分别负责什么
2.1 自动化总导演
.github/workflows/deploy.yml- 触发条件:
push到main - 工作流分为两个 job:
build与deploy
2.2 镜像构建
backend/Dockerfile:构建后端运行镜像(Go API + Python ML 运行环境 +third_party/sam2)frontend/Dockerfile:先用 Node 构建前端,再用 Nginx 承载静态产物
2.3 生产运行编排
docker-compose.prod.yml- 使用
image:(拉远端镜像),不是build:(服务器不本地构建) - 默认端口映射:前端
8080:80,后端8000:8000
2.4 本地 Docker 编排
docker-compose.yml- 使用
build:,适合本地docker compose up --build
2.5 本地一键开发脚本
scripts/run_dev.sh- 做环境准备并启动前后端开发服务,不等同于生产部署
3. CI/CD 实际执行细节(按真实流程)
3.1 Build 阶段(GitHub Runner)
在 deploy.yml 中,Actions 会:
checkout代码;- 登录 GHCR;
- 构建并推送后端镜像:
ghcr.io/xwrock-ce/tunnel-system-backend:mainghcr.io/xwrock-ce/tunnel-system-backend:<commit-sha>
- 构建并推送前端镜像:
ghcr.io/xwrock-ce/tunnel-system-frontend:mainghcr.io/xwrock-ce/tunnel-system-frontend:<commit-sha>
结论:同一次提交会产出
main与sha两种标签,sha可追溯、可回滚。
3.2 Deploy 阶段(远程服务器)
deploy.yml 用 appleboy/ssh-action 登录服务器后,会做:
- 进入部署目录:
/srv/tunnel-system; - 若目录已是 git 仓库,则
fetch + reset到目标分支;否则按条件初始化仓库; - 确保
.env存在(必要时从.env.example复制); - 把 GitHub Secrets 中的
ADMIN_USERNAME/ADMIN_PASSWORD写入或更新到.env; - 创建运行目录:
model_weights、uploads、static; - 登录 GHCR(使用
GHCR_PAT); - 执行 Docker 清理,降低磁盘打满风险;
- 用当前提交 SHA 部署:
IMAGE_TAG=$GITHUB_SHA docker compose -f docker-compose.prod.yml pullIMAGE_TAG=$GITHUB_SHA docker compose -f docker-compose.prod.yml up -d --remove-orphans
结论:服务器“会自动拉镜像并重启”是因为 Actions 在远程执行了这些命令。
4. Docker 与模型权重:为什么必须分开
4.1 为什么权重不进镜像
- 体积大、更新频率与代码不同;
- 便于权限与资产管理;
- 避免每次改权重都重建并分发超大镜像。
4.2 真实挂载关系
在 docker-compose.prod.yml 中:
./model_weights:/app/model_weights:ro./uploads:/app/uploads./static:/app/static
意味着:
- 服务器
/srv/tunnel-system/model_weights→ 容器/app/model_weights
并通过环境变量协同:
MODEL_WEIGHTS_DIR=/app/model_weightsBASE_DIR=/appSAM2_REPO_PATH=/app/backend/third_party/sam2
4.3 关键坑:不要把本地 symlink 传到服务器
如果你在服务器上放的是“指向你个人电脑路径”的符号链接,容器内会报找不到权重。
✅ 正确做法:服务器目录里放真实权重文件(或在服务器内有效的链接目标)。
5. 本地开发:run_dev.sh 到底做了什么
执行 ./scripts/run_dev.sh 后,脚本会(摘要):
- 检查代理环境并在必要时临时取消不可达本地代理;
- 检测后端端口(默认 8000,被占用会自动递增);
- 检查模型权重是否存在(缺失仅告警,不强制退出);
- 准备
backend/.venv(优先 Python 3.12,若检测到 3.13 会提示重建); - 安装/校验 Python ML 依赖;
go mod download准备 Go 依赖;- 若缺少前端依赖则执行
npm install; - 启动后端
go run ./cmd/server与前端npm run dev; Ctrl+C时自动结束两个进程。
注意:
run_dev.sh是开发启动脚本,不会替代 CI/CD 生产部署。
6. 为什么仍建议做 build + test
6.1 前端 build
- 命令:
cd frontend && npm run build - 实际执行:
tsc -b && vite build - 作用:提前发现类型问题、打包问题,避免线上白屏。
6.2 后端测试
- 命令:
cd backend && go test ./... - 作用:回归检查鉴权、接口、服务逻辑,降低发布风险。
6.3 Python 测试(可选但推荐)
- 命令:
cd backend && uv run pytest tests -q
7. 管理员账号行为(避免旧认知误导)
当前后端启动时会确保“配置中的管理员账号”可用:
- 若该用户名不存在:创建;
- 若存在但密码不一致:会更新为配置密码并激活;
- 旧的历史默认账号(
admin/admin123)在满足条件时会被自动停用。
建议:生产环境务必修改默认
ADMIN_USERNAME/ADMIN_PASSWORD与SECRET_KEY。
8. 部署后最小自检清单
在服务器执行:
cd /srv/tunnel-system
1. 1) 看容器状态
docker compose -f docker-compose.prod.yml ps
1. 2) 看后端日志(重点看权重与启动)
docker compose -f docker-compose.prod.yml logs --tail=200 backend
1. 3) 看权重文件是否真实存在
ls -lh /srv/tunnel-system/model_weights
1. 4) 看容器内是否可见
docker compose -f docker-compose.prod.yml exec backend ls -lh /app/model_weights
1. 5) 验证登录接口
curl -X POST http://127.0.0.1:8000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"你的管理员账号","password":"你的管理员密码"}'
9. 常见故障速查
manifest unknown:镜像标签不存在,先看 Actions 的 build/push 是否成功。- 拉镜像失败:检查服务器
docker login ghcr.io权限(GHCR_PAT)。 - 登录失败:检查服务器
.env与数据库中的账号同步情况。 - 权重找不到:检查文件名、挂载路径、是否是无效 symlink。
- 端口冲突:生产前端默认占
8080,后端占8000,冲突时调整映射。
10. 最终结论(可直接放文末)
tunnel-system 的上线并不是“服务器上手工编译代码”,而是“GitHub 构建镜像 + 服务器拉镜像运行”。 模型权重是独立资产,必须在服务器本地可用并通过 volume 挂载给容器。把这两条边界理清,CI/CD、Docker、模型推理链路就不会再混淆。