# Docker 生产环境部署方案（详细版）

## Context

当前 `docker/docker-compose.yml` 是开发混合模式：基础设施在 Docker 运行，Backend API 和 Frontend 在本地裸跑。需要新建一套**生产配置**，所有服务全部 Docker 化，**不修改任何现有文件**。

---

## 文件清单（全部新建）

### 1. `docker/backend/Dockerfile.prod`

**多阶段生产镜像，api 和 worker 共用：**

```
FROM python:3.11-slim

- 安装系统依赖：gcc g++ libffi-dev curl
- COPY requirements.txt → pip install（利用 BuildKit cache mount）
- 强制安装 opencv-python-headless 覆盖 GUI 版
- COPY backend/ → /app/
- 创建非 root 用户 appuser
- mkdir -p /data/files
- 不指定 CMD（由 docker-compose command 决定启动 uvicorn 或 celery）
```

### 2. `docker/frontend/Dockerfile.prod`

**多阶段构建：node build → nginx serve：**

```
Stage 1 (builder): node:20-alpine
  - COPY package.json + package-lock.json
  - npm ci --production=false
  - COPY 前端源码
  - npm run build → 生成 dist/

Stage 2 (runtime): nginx:alpine
  - COPY --from=builder dist/ → /usr/share/nginx/html/
  - COPY nginx.prod.conf → /etc/nginx/nginx.conf
  - EXPOSE 10038
```

### 3. `docker/nginx/nginx.prod.conf`

**生产 Nginx 配置：**

```nginx
listen 10038;    # 对外暴露端口

# Upstream 使用 Docker 服务名
upstream backend {
    server backend:8900;
}
upstream doc_converter {
    server doc-converter:18800;
}

# 静态文件
location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html;
    # 静态资源 assets/ → 1 year 缓存
}

# API 代理
location /api/v1/ {
    proxy_pass http://backend;
    proxy_buffering off;       # SSE 支持
    proxy_read_timeout 300s;
}
location /api/ai/ {
    proxy_pass http://backend;
    rewrite + SSE 支持
}
location /api/biz/ {
    proxy_pass http://backend;
}
location /api/converter/ {
    proxy_pass http://doc_converter;
    rewrite /api/converter → /api
}

# 安全 headers
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy strict-origin-when-cross-origin;

# Gzip 压缩
# Rate limiting
# client_max_body_size 100m
```

### 4. `docker/docker-compose.prod.yml`

**完整服务编排：**

| 服务 | 镜像 | 宿主端口 | 容器端口 | 说明 |
|------|------|----------|----------|------|
| opensearch | 自定义 build | —（内网） | 9200 | 搜索引擎 + IK 分词 |
| opensearch-dashboards | 官方镜像 | 5601 | 5601 | 搜索可视化（保留） |
| neo4j | neo4j:5-community | —（内网） | 7687/7474 | 图数据库 |
| redis | redis:alpine | —（内网） | 6379 | 缓存 + Celery broker |
| mysql | mysql:8.0 | —（内网） | 3306 | 关系数据库 |
| doc-converter | 自定义 build | —（内网） | 18800 | 文档转换 |
| **backend** | 自定义 build | —（内网） | 8900 | **FastAPI API（新增）** |
| **celery-worker** | 同 backend 镜像 | — | — | **异步任务（command 不同）** |
| **frontend** | 自定义 build | **10038** | 10038 | **前端 + Nginx 反向代理（新增）** |

**关键配置点：**
- 只有 frontend (10038) 和 opensearch-dashboards (5601) 映射到宿主机
- backend 和 celery-worker 共用同一 Dockerfile.prod 镜像，通过 command 区分：
    - backend: `uvicorn app.main:app --host 0.0.0.0 --port 8900 --workers 4`
    - celery: `celery -A app.tasks.celery_app:celery_app worker --loglevel=info --pool=solo --concurrency=1`
- 文件存储 volume：`/data/zm-rag/files:/data/files`（Linux 路径）
- env_file 使用 `.env.prod`
- 所有服务带 healthcheck
- 所有服务带 restart: unless-stopped
- 日志限制：`json-file`, max-size 50m, max-file 3
- 启动依赖链：
  ```
  opensearch ──┐
  neo4j ───────┤
  redis ───────┼→ backend → frontend
  mysql ───────┤    ↓
  doc-converter┘  celery-worker
  ```

### 5. `docker/.env.prod.example`

```env
# ── LLM / Embedding（必填）──
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=sk-xxx                    # ← 替换为真实 key
LLM_MODEL=qwen3.5-flash
LLM_ENABLE_THINKING=false
EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
EMBEDDING_API_KEY=sk-xxx              # ← 替换为真实 key
EMBEDDING_MODEL=text-embedding-v4

# ── 安全（必改）──
JWT_SECRET=<生成一个 64 字符随机字符串>
MYSQL_ROOT_PASSWORD=<强密码>
NEO4J_PASSWORD=<强密码>

# ── 数据存储路径 ──
FILE_STORAGE_PATH=/data/zm-rag/files

# ── CORS ──
CORS_ORIGINS=["http://your-domain:10038"]

# ── 可选调整 ──
# OPENSEARCH_JAVA_OPTS=-Xms4g -Xmx4g
# NEO4J_HEAP_MAX=4g
# REDIS_MAXMEMORY=4gb
# UVICORN_WORKERS=4
```

---

## 部署方式

```bash
cd docker/
cp .env.prod.example .env.prod
vim .env.prod                          # 填写 API Key、密码等

docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build

# 查看状态
docker compose -f docker-compose.prod.yml ps

# 查看日志
docker compose -f docker-compose.prod.yml logs -f backend frontend
```

## 验证

1. `docker compose -f docker-compose.prod.yml ps` — 所有服务 healthy
2. `curl http://<server-ip>:10038` — 返回前端 HTML
3. `curl http://<server-ip>:10038/api/v1/health` — 通过 nginx 转发到 backend
4. 浏览器访问 `http://<server-ip>:10038` — 前端页面正常
5. 上传文档测试全链路（前端 → nginx → backend → celery → opensearch）
