Files
ai-platform/docs/deployment.md
T

207 lines
7.0 KiB
Markdown
Raw 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.
# AI Platform 打包部署指南
## 项目概况
| 项目 | 说明 |
|------|------|
| 项目名称 | ai-platform |
| 前端框架 | React 18 + TypeScript + Vite 8 |
| UI 库 | Ant Design 5 + Tailwind CSS 3 |
| 构建产物 | 静态文件(`dist/`),由 nginx 提供服务 |
| 容器化 | Docker 多阶段构建(builder → nginx:alpine) |
| 部署端口 | 8901 |
| 后端代理 | `http://host.docker.internal:8000`(可运行时覆盖) |
## 目录结构要点
```
ai-platform/
├── Dockerfile # 多阶段构建:node:20-alpine 构建 → nginx:alpine 运行
├── nginx.conf # nginx 配置模板(支持 ${BACKEND_URL} 环境变量替换)
├── .dockerignore # 排除 node_modules / dist / .git / *.md 等
├── package.json # build 脚本:tsc -b && vite build
├── vite.config.ts # 开发代理配置(/api/v1, /health, /bexapi, /iam-api)
├── index.html # 入口 HTML
├── src/ # 源码
├── public/ # 静态资源
└── docs/ # 文档(本文件所在目录)
```
## Docker 镜像构建
### Dockerfile 构建流程
1. **Stage 1 — builder(node:20-alpine)**
- 复制 `package.json` + `package-lock.json`,执行 `npm install`
- 复制全部源码,执行 `npm run build`(即 `tsc -b && vite build`)
- 产物输出到 `/app/dist`
2. **Stage 2 — 运行时(nginx:alpine)**
- 将 builder 阶段的 `/app/dist` 复制到 `/usr/share/nginx/html`
- 复制 `nginx.conf` 到 `/etc/nginx/templates/default.conf.template`(nginx 入口脚本会通过 `envsubst` 替换其中的 `${BACKEND_URL}`)
- 默认环境变量 `BACKEND_URL=http://host.docker.internal:8000`
- 暴露端口 8901
### 构建命令
```bash
sudo docker build -t ai-platform:1.0.0 .
```
构建过程利用了 Docker 缓存层:如果 `package.json` / `package-lock.json` 未变,`npm install` 层会命中缓存,仅重新执行源码复制和构建。
## 容器部署
### 启动命令
```bash
sudo docker run -d \
--name ai-platform \
-p 8901:8901 \
--add-host=host.docker.internal:host-gateway \
-e BACKEND_URL=http://host.docker.internal:8000 \
ai-platform:1.0.0
```
### 参数说明
| 参数 | 说明 |
|------|------|
| `-d` | 后台运行 |
| `--name ai-platform` | 容器名称 |
| `-p 8901:8901` | 宿主机 8901 端口映射到容器 8901 |
| `--add-host=host.docker.internal:host-gateway` | **必须添加**,否则 nginx 启动时无法解析 `host.docker.internal`,容器会立即退出(exit code 1) |
| `-e BACKEND_URL=...` | 后端 API 地址,运行时可覆盖。默认 `http://host.docker.internal:8000` |
> **踩坑记录**:Docker 29.x 版本中,`host.docker.internal` 不会自动解析,必须通过 `--add-host=host.docker.internal:host-gateway` 显式添加。如果不加此参数,nginx 启动报错:`host not found in upstream "host.docker.internal"`,容器退出码为 1。
### 重新部署(完整流程)
```bash
# 1. 构建新镜像
sudo docker build -t ai-platform:1.0.0 .
# 2. 停止并移除旧容器
sudo docker stop ai-platform
sudo docker rm ai-platform
# 3. 启动新容器
sudo docker run -d \
--name ai-platform \
-p 8901:8901 \
--add-host=host.docker.internal:host-gateway \
-e BACKEND_URL=http://host.docker.internal:8000 \
ai-platform:1.0.0
```
### 验证部署
```bash
# 检查容器运行状态
sudo docker ps --filter "name=ai-platform"
# 从容器内部验证 nginx 响应
sudo docker exec ai-platform curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost:8901/
# 预期输出:HTTP 200
# 查看页面内容
sudo docker exec ai-platform curl -s http://localhost:8901/
# 查看 nginx 日志
sudo docker logs ai-platform
```
## nginx 配置说明
`nginx.conf` 作为模板文件放在 `/etc/nginx/templates/default.conf.template`,nginx 容器入口脚本会自动用 `envsubst` 将 `${BACKEND_URL}` 替换为环境变量值,输出到 `/etc/nginx/conf.d/default.conf`。
### 路由规则
| 路径 | 代理目标 | 说明 |
|------|----------|------|
| `/api/v1/` | `${BACKEND_URL}/api/v1/` | 后端 API,支持 SSE 长连接(关闭 buffering,300s 超时) |
| `/health` | `${BACKEND_URL}/health` | 健康检查 |
| `/bexapi/` | `https://bexell-scheduling.dongsk.top/api/` | Bexell 排程 API |
| `/iam-api/` | `https://iam.digiwincloud.com.cn/` | 鼎捷云 IAM 登录服务 |
| `/` | `try_files $uri $uri/ /index.html` | SPA 路由 fallback |
| `/assets/` | 本地静态文件 | 长缓存 1 年,`Cache-Control: public, immutable` |
| `*.svg/png/jpg/...` | 本地静态文件 | 缓存 30 天 |
### 开发环境 vs 生产环境代理对照
开发时由 Vite dev server 代理(`vite.config.ts`),生产时由 nginx 代理(`nginx.conf`),两者路径需保持一致:
| 路径 | Vite target | nginx proxy_pass |
|------|------------|-------------------|
| `/api/v1` | `http://localhost:8000` | `${BACKEND_URL}/api/v1/` |
| `/health` | `http://localhost:8000` | `${BACKEND_URL}/health` |
| `/bexapi` | `https://bexell-scheduling.dongsk.top`(rewrite → `/api`) | `https://bexell-scheduling.dongsk.top/api/` |
| `/iam-api` | `https://iam.digiwincloud.com.cn`(rewrite → 去前缀) | `https://iam.digiwincloud.com.cn/` |
## 常见问题排查
### 容器启动后立即退出(exit code 1)
**原因**:nginx 无法解析 `host.docker.internal`。
**解决**:启动时添加 `--add-host=host.docker.internal:host-gateway`。
**验证**:`sudo docker logs ai-platform` 查看是否有 `host not found in upstream` 错误。
### 构建时 TypeScript 编译失败
`npm run build` 先执行 `tsc -b` 类型检查,类型错误会导致构建中断。需修复所有 TS 错误后重新构建。
### 构建产物体积过大
当前主 chunk 约 1.9MB(gzip 后 581KB),Vite 会输出警告。如需优化:
- 使用动态 `import()` 对路由模块做懒加载(代码分割)
- 在 `vite.config.ts` 中配置 `build.rollupOptions.output.manualChunks` 手动分包
- 调整 `build.chunkSizeWarningLimit` 提高警告阈值(仅消除警告,不减小体积)
### 端口被占用
```bash
# 查看占用 8901 端口的进程
sudo lsof -i :8901
# 停止旧容器后释放端口
sudo docker stop ai-platform
```
## 快速部署脚本
将以下内容保存为 `deploy.sh` 即可一键重新打包部署:
```bash
#!/bin/bash
set -e
IMAGE_NAME="ai-platform:1.0.0"
CONTAINER_NAME="ai-platform"
PORT=8901
BACKEND_URL="${BACKEND_URL:-http://host.docker.internal:8000}"
echo ">>> 构建镜像..."
sudo docker build -t "$IMAGE_NAME" .
echo ">>> 停止旧容器..."
sudo docker stop "$CONTAINER_NAME" 2>/dev/null || true
sudo docker rm "$CONTAINER_NAME" 2>/dev/null || true
echo ">>> 启动新容器..."
sudo docker run -d \
--name "$CONTAINER_NAME" \
-p "$PORT:8901" \
--add-host=host.docker.internal:host-gateway \
-e BACKEND_URL="$BACKEND_URL" \
"$IMAGE_NAME"
echo ">>> 验证..."
sleep 2
sudo docker exec "$CONTAINER_NAME" curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8901/
echo ">>> 部署完成: http://localhost:$PORT"
```