企业级私有化部署方案
🏢 企业级私有化部署方案
将当前基于 Dify 云服务的 AI 应用改造为完全私有化的企业级解决方案
📋 目录
1. 现状分析
1.1 当前架构依赖
┌─────────────────────────────────────────────────┐
│ 当前架构(云服务依赖) │
├─────────────────────────────────────────────────┤
│ │
│ 前端应用 (React) │
│ ↓ │
│ 后端服务 (Node.js + Express) │
│ ↓ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Dify 云平台 │ │ 云端 LLM API │ │
│ │ (工作流) │ │ (SiliconFlow) │ │
│ └──────────────┘ └─────────────────┘ │
│ ↓ ↓ │
│ ┌──────────────────────────────────────┐ │
│ │ 外部云服务(数据离开内网) │ │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
1.2 存在的问题
| 问题类型 | 具体表现 | 业务影响 |
|---|---|---|
| 数据安全 | 企业数据通过 API 发送到云端 | ⚠️ 数据泄露风险 |
| 合规性 | 无法满足数据本地化要求 | ❌ 无法通过审计 |
| 可控性 | 依赖第三方服务可用性 | ⚠️ 业务连续性风险 |
| 成本 | 按调用量计费,无法控制 | 💰 成本不可预测 |
| 定制化 | 模型和工作流受限于平台 | 🚫 功能扩展受限 |
2. 私有化部署架构
2.1 目标架构
┌─────────────────────────────────────────────────────────────┐
│ 企业私有化部署架构(内网环境) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 负载均衡层 (Nginx / HAProxy) │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 前端服务 (React + Vite) │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 应用服务层 (Node.js + Express) │ │
│ │ │ │
│ │ - REST API │ │
│ │ - SSE 流式输出 │ │
│ │ - LangChain 工作流引擎 │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ ↓ ↓ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Dify 私有版 │ │ 大模型服务 │ │ 向量数据库 │ │
│ │ (Docker) │ │ (Ollama) │ │ (Qdrant) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 数据持久化层 (PostgreSQL) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 监控与日志 (Prometheus + Grafana) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ 🏢 企业内网环境 - 数据不出内网 │
└─────────────────────────────────────────────────────────────┘
2.2 核心组件说明
| 组件 | 作用 | 私有化方案 | 备选方案 |
|---|---|---|---|
| 工作流编排 | 替代 Dify 云平台 | Dify 社区版(Docker) | LangChain(已集成) |
| 大语言模型 | 替代云端 API | Ollama + 开源模型 | vLLM / FastChat / LocalAI |
| 向量数据库 | RAG 文档检索 | Qdrant(Docker) | Milvus / Weaviate / Chroma |
| 数据库 | 存储会话、用户数据 | PostgreSQL | MySQL / MongoDB |
| 文件存储 | 存储上传的文档、图片 | MinIO(对象存储) | 本地文件系统 / NFS |
| 缓存 | 提升响应速度 | Redis | - |
| 负载均衡 | 流量分发 | Nginx | HAProxy / Traefik |
| 监控 | 系统健康监控 | Prometheus + Grafana | - |
3. 核心组件私有化方案
3.1 Dify 私有化部署
方案 A:Dify 社区版(推荐)
优点:
- ✅ 提供完整的 Web UI 和工作流编辑器
- ✅ 支持所有云平台功能
- ✅ 开源免费,社区活跃
- ✅ 支持多种模型和向量数据库
部署方式:
# 1. 克隆 Dify 仓库
git clone https://github.com/langgenius/dify.git
cd dify/docker
# 2. 复制环境配置
cp .env.example .env
# 3. 修改 .env 配置(关键配置)
cat > .env << EOF
# API 服务配置
CONSOLE_WEB_URL=http://your-internal-ip:3000
CONSOLE_API_URL=http://your-internal-ip:5001
SERVICE_API_URL=http://your-internal-ip:5001
# 数据库配置(使用 Docker Compose 自带的 PostgreSQL)
DB_USERNAME=postgres
DB_PASSWORD=dify_strong_password
DB_HOST=db
DB_PORT=5432
DB_DATABASE=dify
# Redis 配置
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=dify_redis_password
# 向量数据库配置(使用 Qdrant)
VECTOR_STORE=qdrant
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=your-qdrant-api-key
# 存储配置
STORAGE_TYPE=local
STORAGE_LOCAL_PATH=/app/storage
# 模型配置(指向本地 Ollama)
# 在 Web UI 中配置,不需要在这里设置
EOF
# 4. 启动 Dify
docker compose up -d
# 5. 查看日志
docker compose logs -f
# 访问地址:
# - Web UI: http://your-ip:3000
# - API: http://your-ip:5001
资源需求:
- CPU: 4 核以上
- 内存: 8GB 以上
- 磁盘: 50GB 以上(数据、向量索引)
方案 B:纯 LangChain 方案(已实现)
优点:
- ✅ 项目已集成 LangChain 工作流引擎
- ✅ 完全自主可控
- ✅ 无需额外部署 Dify
实施步骤:
- 启用 LangChain 路由(已完成)
// backend/src/server.js 已集成
app.use('/api/langchain', langchainRoutes);
- 使用 LangChain 工作流引擎
// 参考 backend/src/langchain-version/workflows/workflowEngine.js
import { WorkflowEngine, createSequentialWorkflow } from './workflowEngine.js';
const workflow = createSequentialWorkflow([
{
type: 'llm',
prompt: '分析这个问题:{{input}}',
systemPrompt: '你是一个专业的分析师',
},
{
type: 'code',
code: 'return { result: inputs.output.toUpperCase() }',
},
]);
const engine = new WorkflowEngine(workflow);
const result = await engine.execute({ input: '用户问题' });
对比:
| 特性 | Dify 社区版 | LangChain 方案 |
|---|---|---|
| 可视化编辑器 | ✅ | ❌ |
| 学习成本 | 低 | 中 |
| 定制化程度 | 中 | 高 |
| 运维复杂度 | 中 | 低 |
| 推荐场景 | 快速上线、业务人员可用 | 开发团队主导、深度定制 |
3.2 大模型私有化部署
方案 A:Ollama(推荐)
优点:
- ✅ 安装简单,一键部署
- ✅ 支持 60+ 主流开源模型
- ✅ 自动处理模型下载和量化
- ✅ 提供 OpenAI 兼容 API
- ✅ GPU/CPU 自动适配
部署步骤:
# 1. 安装 Ollama(Linux)
curl -fsSL https://ollama.com/install.sh | sh
# 2. 下载并运行模型
# 中文对话模型(推荐)
ollama pull qwen2.5:7b # 7B 参数,显存需求 ~6GB
ollama pull qwen2.5:14b # 14B 参数,显存需求 ~12GB
ollama pull qwen2.5:32b # 32B 参数,显存需求 ~24GB
# 代码生成模型
ollama pull deepseek-coder-v2:16b # 代码专用模型
# 小型快速模型
ollama pull qwen2.5:3b # 3B 参数,显存需求 ~3GB
# 3. 测试模型
ollama run qwen2.5:7b
# 4. 作为 API 服务运行(默认端口 11434)
# Ollama 自动在后台运行,提供 OpenAI 兼容 API
Docker 部署(生产环境推荐):
# docker-compose.ollama.yml
version: '3.8'
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_KEEP_ALIVE=24h # 模型保持加载时间
- OLLAMA_NUM_PARALLEL=4 # 并行请求数
- OLLAMA_MAX_LOADED_MODELS=2 # 同时加载的模型数
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
volumes:
ollama_data:
# 启动 Ollama
docker compose -f docker-compose.ollama.yml up -d
# 下载模型(在容器内执行)
docker exec -it ollama ollama pull qwen2.5:7b
配置后端连接 Ollama:
// backend/src/langchain-version/config.js
export const config = {
model: {
provider: 'ollama',
apiKey: 'ollama', // Ollama 不需要真实的 API Key
baseURL: 'http://localhost:11434/v1', // Ollama OpenAI 兼容接口
modelName: 'qwen2.5:7b',
temperature: 0.7,
},
embedding: {
provider: 'ollama',
baseURL: 'http://localhost:11434/v1',
modelName: 'bge-m3:latest', // 向量化模型
},
};
推荐模型列表:
| 模型 | 参数量 | 显存需求 | 适用场景 | 中文能力 |
|---|---|---|---|---|
| qwen2.5:3b | 3B | ~3GB | 轻量级对话、快速响应 | ⭐⭐⭐⭐ |
| qwen2.5:7b | 7B | ~6GB | 通用对话、知识问答 | ⭐⭐⭐⭐⭐ |
| qwen2.5:14b | 14B | ~12GB | 复杂推理、专业领域 | ⭐⭐⭐⭐⭐ |
| qwen2.5:32b | 32B | ~24GB | 高难度任务、创意写作 | ⭐⭐⭐⭐⭐ |
| deepseek-coder-v2:16b | 16B | ~14GB | 代码生成、代码审查 | ⭐⭐⭐⭐ |
| llama3.1:8b | 8B | ~7GB | 英文对话(中文一般) | ⭐⭐⭐ |
| mistral:7b | 7B | ~6GB | 英文推理 | ⭐⭐ |
方案 B:vLLM(高性能方案)
优点:
- ✅ 推理速度快 20-30 倍(相比 HuggingFace)
- ✅ 支持连续批处理
- ✅ 更高的并发能力
- ✅ 适合生产环境高负载场景
部署步骤:
# 1. 安装 vLLM
pip install vllm
# 2. 下载模型
# 从 HuggingFace 或 ModelScope 下载
# 例如:Qwen2.5-7B-Instruct
git clone https://www.modelscope.cn/Qwen/Qwen2.5-7B-Instruct.git
# 3. 启动 vLLM 服务(OpenAI 兼容 API)
python -m vllm.entrypoints.openai.api_server \
--model /path/to/Qwen2.5-7B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--trust-remote-code \
--tensor-parallel-size 1 # 单 GPU
Docker 部署:
# docker-compose.vllm.yml
version: '3.8'
services:
vllm:
image: vllm/vllm-openai:latest
container_name: vllm-server
ports:
- "8000:8000"
volumes:
- ./models:/models # 模型存储路径
environment:
- MODEL_NAME=/models/Qwen2.5-7B-Instruct
- TENSOR_PARALLEL_SIZE=1
- MAX_MODEL_LEN=4096
command: >
--model /models/Qwen2.5-7B-Instruct
--host 0.0.0.0
--port 8000
--trust-remote-code
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
方案 C:FastChat(多模型管理)
优点:
- ✅ 支持多模型同时部署
- ✅ 提供 Web UI 管理界面
- ✅ 支持模型热切换
# 1. 安装 FastChat
pip install "fschat[model_worker,webui]"
# 2. 启动控制器
python -m fastchat.serve.controller --host 0.0.0.0
# 3. 启动模型 Worker(可启动多个)
python -m fastchat.serve.model_worker \
--model-path /path/to/qwen2.5-7b \
--controller http://localhost:21001 \
--worker-address http://localhost:31000
# 4. 启动 OpenAI 兼容 API 服务
python -m fastchat.serve.openai_api_server \
--controller-address http://localhost:21001 \
--host 0.0.0.0 \
--port 8000
对比选择:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 快速部署、小团队 | Ollama | 简单易用,开箱即用 |
| 高并发、生产环境 | vLLM | 性能最优,吞吐量高 |
| 多模型管理 | FastChat | 支持模型切换和管理 |
| 开发测试 | Ollama | 快速迭代,便于调试 |
3.3 向量数据库私有化
方案 A:Qdrant(推荐)
优点:
- ✅ 高性能,支持 HNSW 索引
- ✅ 提供 REST API 和 gRPC
- ✅ 支持过滤、多向量搜索
- ✅ 内置集群和副本功能
- ✅ 开源免费
Docker 部署:
# docker-compose.qdrant.yml
version: '3.8'
services:
qdrant:
image: qdrant/qdrant:latest
container_name: qdrant
ports:
- "6333:6333" # REST API
- "6334:6334" # gRPC
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__HTTP_PORT=6333
- QDRANT__SERVICE__GRPC_PORT=6334
# 性能优化配置
- QDRANT__STORAGE__WAL__WAL_CAPACITY_MB=256
- QDRANT__STORAGE__PERFORMANCE__MAX_SEARCH_THREADS=4
restart: unless-stopped
volumes:
qdrant_data:
# 启动 Qdrant
docker compose -f docker-compose.qdrant.yml up -d
# 访问 Web UI: http://localhost:6333/dashboard
配置后端连接 Qdrant:
// backend/src/utils/vectorDB/qdrant.js(已存在)
import { QdrantClient } from '@qdrant/js-client-rest';
const client = new QdrantClient({
url: process.env.QDRANT_URL || 'http://localhost:6333',
apiKey: process.env.QDRANT_API_KEY, // 可选
});
// 创建集合
await client.createCollection('documents', {
vectors: {
size: 1024, // 向量维度(根据 Embedding 模型)
distance: 'Cosine',
},
optimizers_config: {
default_segment_number: 2,
},
replication_factor: 2, // 副本数(生产环境推荐)
});
// 插入向量
await client.upsert('documents', {
points: [
{
id: 1,
vector: [...], // 向量数据
payload: {
text: '文档内容',
metadata: { source: 'doc1.pdf' },
},
},
],
});
// 搜索
const results = await client.search('documents', {
vector: [...], // 查询向量
limit: 5,
with_payload: true,
});
方案 B:Milvus(企业级方案)
优点:
- ✅ 功能最全面
- ✅ 支持 GPU 加速
- ✅ 企业级特性(备份、监控)
- ✅ 支持超大规模数据(10亿+)
Docker 部署:
# 下载 Milvus docker-compose 配置
wget https://github.com/milvus-io/milvus/releases/download/v2.3.0/milvus-standalone-docker-compose.yml -O docker-compose.milvus.yml
# 启动 Milvus
docker compose -f docker-compose.milvus.yml up -d
方案 C:Weaviate
优点:
- ✅ 内置多种向量化模块
- ✅ 支持 GraphQL 查询
- ✅ 语义搜索能力强
# docker-compose.weaviate.yml
version: '3.8'
services:
weaviate:
image: semitechnologies/weaviate:latest
ports:
- "8080:8080"
environment:
- AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true
- PERSISTENCE_DATA_PATH=/var/lib/weaviate
volumes:
- weaviate_data:/var/lib/weaviate
volumes:
weaviate_data:
对比选择:
| 特性 | Qdrant | Milvus | Weaviate |
|---|---|---|---|
| 部署简单度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 功能丰富度 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 资源占用 | 低 | 中 | 低 |
| 推荐场景 | 中小规模 | 大规模生产 | 语义搜索 |
3.4 Embedding 模型私有化
RAG 系统需要将文本转换为向量,推荐使用:
# Ollama 安装 Embedding 模型
ollama pull bge-m3:latest # 通用中文 Embedding(推荐)
ollama pull nomic-embed-text # 英文 Embedding
ollama pull mxbai-embed-large # 高精度多语言
# 或者使用本地 Sentence Transformers
pip install sentence-transformers
python
>>> from sentence_transformers import SentenceTransformer
>>> model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
>>> embeddings = model.encode(['文本1', '文本2'])
后端配置:
// backend/src/langchain-version/config.js
export const config = {
embedding: {
provider: 'ollama',
baseURL: 'http://localhost:11434/v1',
modelName: 'bge-m3', // Ollama Embedding 模型
},
};
4. 部署实施步骤
4.1 阶段一:基础环境准备(1-2 天)
4.1.1 硬件资源准备
最低配置(开发/测试环境):
- CPU: 8 核
- 内存: 32GB
- GPU: NVIDIA RTX 3090 (24GB) 或同等算力
- 磁盘: 500GB SSD
- 网络: 千兆内网
推荐配置(生产环境):
- CPU: 16 核 × 2(双路)
- 内存: 128GB
- GPU: NVIDIA A100 (40GB) × 2 或 H100
- 磁盘: 2TB NVMe SSD(系统) + 10TB HDD(数据)
- 网络: 万兆内网 + 负载均衡
4.1.2 软件环境安装
# 1. 安装 Docker 和 Docker Compose
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 2. 安装 NVIDIA Docker(GPU 支持)
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt update
sudo apt install -y nvidia-docker2
sudo systemctl restart docker
# 3. 验证 GPU 支持
docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi
# 4. 安装 Nginx
sudo apt install -y nginx
# 5. 安装 PostgreSQL(或使用 Docker)
sudo apt install -y postgresql postgresql-contrib
4.2 阶段二:核心组件部署(3-5 天)
完整 Docker Compose 配置
创建 docker-compose.yml:
version: '3.8'
services:
# ===== 数据库层 =====
# PostgreSQL(存储应用数据)
postgres:
image: postgres:15-alpine
container_name: ai-postgres
environment:
POSTGRES_USER: aiuser
POSTGRES_PASSWORD: strong_password_123
POSTGRES_DB: ai_platform
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U aiuser"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Redis(缓存)
redis:
image: redis:7-alpine
container_name: ai-redis
command: redis-server --requirepass redis_password_123
volumes:
- redis_data:/data
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
restart: unless-stopped
# ===== 向量数据库 =====
qdrant:
image: qdrant/qdrant:latest
container_name: ai-qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__HTTP_PORT=6333
- QDRANT__SERVICE__GRPC_PORT=6334
restart: unless-stopped
# ===== 大模型服务 =====
ollama:
image: ollama/ollama:latest
container_name: ai-ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_KEEP_ALIVE=24h
- OLLAMA_NUM_PARALLEL=4
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
# ===== Dify 平台(可选)=====
# Dify API 服务
dify-api:
image: langgenius/dify-api:latest
container_name: dify-api
environment:
- MODE=api
- LOG_LEVEL=INFO
- DB_USERNAME=aiuser
- DB_PASSWORD=strong_password_123
- DB_HOST=postgres
- DB_PORT=5432
- DB_DATABASE=dify
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=redis_password_123
- CELERY_BROKER_URL=redis://:redis_password_123@redis:6379/1
- VECTOR_STORE=qdrant
- QDRANT_URL=http://qdrant:6333
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- dify_storage:/app/storage
ports:
- "5001:5001"
restart: unless-stopped
# Dify Worker(后台任务)
dify-worker:
image: langgenius/dify-api:latest
container_name: dify-worker
environment:
- MODE=worker
- DB_USERNAME=aiuser
- DB_PASSWORD=strong_password_123
- DB_HOST=postgres
- DB_PORT=5432
- DB_DATABASE=dify
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=redis_password_123
- CELERY_BROKER_URL=redis://:redis_password_123@redis:6379/1
depends_on:
- dify-api
volumes:
- dify_storage:/app/storage
restart: unless-stopped
# Dify Web UI
dify-web:
image: langgenius/dify-web:latest
container_name: dify-web
environment:
- CONSOLE_API_URL=http://your-internal-ip:5001
- APP_API_URL=http://your-internal-ip:5001
ports:
- "3000:3000"
depends_on:
- dify-api
restart: unless-stopped
# ===== 应用服务层 =====
# 后端服务
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: ai-backend
environment:
- NODE_ENV=production
- PORT=3001
# Ollama 配置
- MODEL_PROVIDER=ollama
- MODEL_BASE_URL=http://ollama:11434/v1
- MODEL_NAME=qwen2.5:7b
- EMBEDDING_BASE_URL=http://ollama:11434/v1
- EMBEDDING_MODEL=bge-m3
# 数据库配置
- DATABASE_URL=postgresql://aiuser:strong_password_123@postgres:5432/ai_platform
- REDIS_URL=redis://:redis_password_123@redis:6379/0
# 向量数据库配置
- QDRANT_URL=http://qdrant:6333
# Dify 配置(可选)
- DIFY_API_URL=http://dify-api:5001
depends_on:
- postgres
- redis
- qdrant
- ollama
volumes:
- ./backend/uploads:/app/uploads
ports:
- "3001:3001"
restart: unless-stopped
# 前端服务
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: ai-frontend
environment:
- VITE_API_BASE_URL=http://your-internal-ip:3001/api
ports:
- "5173:80"
depends_on:
- backend
restart: unless-stopped
# ===== 监控层 =====
# Prometheus(指标收集)
prometheus:
image: prom/prometheus:latest
container_name: ai-prometheus
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
ports:
- "9090:9090"
restart: unless-stopped
# Grafana(可视化)
grafana:
image: grafana/grafana:latest
container_name: ai-grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_INSTALL_PLUGINS=redis-datasource
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
ports:
- "3005:3000"
depends_on:
- prometheus
restart: unless-stopped
# ===== 负载均衡 =====
nginx:
image: nginx:alpine
container_name: ai-nginx
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
ports:
- "80:80"
- "443:443"
depends_on:
- frontend
- backend
restart: unless-stopped
volumes:
postgres_data:
redis_data:
qdrant_data:
ollama_data:
dify_storage:
prometheus_data:
grafana_data:
networks:
default:
name: ai-platform
driver: bridge
启动服务
# 1. 创建必要的目录
mkdir -p monitoring/grafana/{dashboards,datasources}
mkdir -p nginx
# 2. 启动所有服务
docker compose up -d
# 3. 查看服务状态
docker compose ps
# 4. 下载 Ollama 模型
docker exec -it ai-ollama ollama pull qwen2.5:7b
docker exec -it ai-ollama ollama pull bge-m3
# 5. 初始化数据库(Dify)
docker exec -it dify-api flask db upgrade
# 6. 查看日志
docker compose logs -f backend
4.3 阶段三:应用配置(1-2 天)
4.3.1 后端环境配置
创建 backend/.env.production:
# ===== 运行环境 =====
NODE_ENV=production
PORT=3001
# ===== 大模型配置 =====
MODEL_PROVIDER=ollama
MODEL_BASE_URL=http://ollama:11434/v1
MODEL_NAME=qwen2.5:7b
MODEL_TEMPERATURE=0.7
MODEL_MAX_TOKENS=2000
# ===== Embedding 配置 =====
EMBEDDING_PROVIDER=ollama
EMBEDDING_BASE_URL=http://ollama:11434/v1
EMBEDDING_MODEL=bge-m3
# ===== 数据库配置 =====
DATABASE_URL=postgresql://aiuser:strong_password_123@postgres:5432/ai_platform
# ===== Redis 配置 =====
REDIS_URL=redis://:redis_password_123@redis:6379/0
# ===== 向量数据库配置 =====
VECTOR_DB_TYPE=qdrant
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY= # 可选
# ===== Dify 配置(可选)=====
DIFY_API_URL=http://dify-api:5001
# ===== 文件存储配置 =====
UPLOAD_DIR=./uploads
MAX_FILE_SIZE=50mb
# ===== 安全配置 =====
JWT_SECRET=your-jwt-secret-key-change-this
SESSION_SECRET=your-session-secret-change-this
# ===== 监控配置 =====
ENABLE_METRICS=true
METRICS_PORT=9091
4.3.2 Nginx 配置
创建 nginx/nginx.conf:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
# 上游服务
upstream backend {
server backend:3001 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream frontend {
server frontend:80 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream dify_web {
server dify-web:3000 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# 后端 API 服务
server {
listen 80;
server_name api.your-company.internal; # 修改为你的内部域名
client_max_body_size 50M;
# API 路由
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
# 保持连接
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE 流式输出配置
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
# 添加 CORS 头(如果需要)
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
if ($request_method = OPTIONS) {
return 204;
}
}
# 健康检查
location /health {
proxy_pass http://backend;
access_log off;
}
}
# 前端服务
server {
listen 80;
server_name ai.your-company.internal; # 修改为你的内部域名
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 缓存静态资源
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
# Dify Web UI(可选)
server {
listen 80;
server_name dify.your-company.internal;
location / {
proxy_pass http://dify_web;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Grafana 监控(可选)
server {
listen 80;
server_name monitoring.your-company.internal;
location / {
proxy_pass http://grafana:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
4.3.3 创建 Dockerfile
后端 Dockerfile:
# backend/Dockerfile
FROM node:18-alpine
WORKDIR /app
# 安装依赖
COPY package*.json ./
RUN npm ci --only=production
# 复制源代码
COPY . .
# 创建上传目录
RUN mkdir -p uploads
# 暴露端口
EXPOSE 3001
# 启动应用
CMD ["node", "src/server.js"]
前端 Dockerfile:
# frontend/Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
# 安装依赖
COPY package*.json ./
RUN npm ci
# 复制源代码并构建
COPY . .
RUN npm run build
# 生产镜像
FROM nginx:alpine
# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
前端 nginx.conf:
# frontend/nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# SPA 路由支持
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 禁用缓存 HTML
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
4.4 阶段四:测试验证(2-3 天)
4.4.1 健康检查脚本
创建 scripts/health-check.sh:
#!/bin/bash
# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo "======================================"
echo " AI 平台健康检查"
echo "======================================"
# 检查 Docker 服务
check_service() {
local service_name=$1
local port=$2
local endpoint=$3
echo -n "检查 $service_name... "
if curl -sf "http://localhost:$port$endpoint" > /dev/null 2>&1; then
echo -e "${GREEN}✓ 正常${NC}"
return 0
else
echo -e "${RED}✗ 异常${NC}"
return 1
fi
}
# 检查各服务
check_service "PostgreSQL" 5432 "" # TCP 端口检查
check_service "Redis" 6379 ""
check_service "Qdrant" 6333 "/healthz"
check_service "Ollama" 11434 "/api/tags"
check_service "后端 API" 3001 "/health"
check_service "Dify API" 5001 "/health" # 可选
check_service "前端服务" 5173 "/"
echo ""
echo "======================================"
echo " 模型检查"
echo "======================================"
# 检查 Ollama 模型
echo "已安装的 Ollama 模型:"
docker exec ai-ollama ollama list
echo ""
echo "======================================"
echo " 访问地址"
echo "======================================"
echo "前端应用: http://localhost:5173"
echo "后端 API: http://localhost:3001"
echo "Dify UI: http://localhost:3000"
echo "Grafana: http://localhost:3005 (admin/admin123)"
echo "Prometheus: http://localhost:9090"
echo "Qdrant Dashboard: http://localhost:6333/dashboard"
echo "======================================"
# 赋予执行权限
chmod +x scripts/health-check.sh
# 执行健康检查
./scripts/health-check.sh
4.4.2 功能测试
测试 1:基础对话
# 测试后端 API
curl -X POST http://localhost:3001/api/langchain/chat \
-H "Content-Type: application/json" \
-d '{
"message": "你好,请介绍一下你自己",
"sessionId": "test-session-1"
}'
测试 2:RAG 文档检索
# 1. 上传文档
curl -X POST http://localhost:3001/api/rag/upload \
-F "file=@test-document.pdf"
# 2. 查询文档
curl -X POST http://localhost:3001/api/rag/query \
-H "Content-Type: application/json" \
-d '{
"query": "文档的主要内容是什么?"
}'
测试 3:工作流执行
curl -X POST http://localhost:3001/api/langchain/workflow \
-H "Content-Type: application/json" \
-d '{
"workflowId": "test-workflow",
"inputs": {
"topic": "人工智能"
}
}'
4.5 阶段五:生产优化(持续)
4.5.1 性能优化
1. Ollama 性能调优
# 增加并行处理能力
docker exec -it ai-ollama sh -c 'echo "export OLLAMA_NUM_PARALLEL=8" >> ~/.bashrc'
# 调整上下文长度(根据显存)
docker exec -it ai-ollama sh -c 'echo "export OLLAMA_MAX_LOADED_MODELS=3" >> ~/.bashrc'
# 重启 Ollama
docker restart ai-ollama
2. Qdrant 性能调优
// 优化索引参数
await client.updateCollection('documents', {
optimizers_config: {
default_segment_number: 4, // 增加段数提升并发
max_segment_size: 200000, // 单段最大向量数
memmap_threshold: 50000, // 内存映射阈值
indexing_threshold: 20000, // 索引阈值
flush_interval_sec: 5, // 刷新间隔
},
hnsw_config: {
m: 16, // HNSW 参数 M(连接数)
ef_construct: 100, // 构建时的 ef
full_scan_threshold: 10000, // 全扫描阈值
},
});
3. PostgreSQL 优化
-- 修改 PostgreSQL 配置(在容器中执行)
ALTER SYSTEM SET shared_buffers = '4GB';
ALTER SYSTEM SET effective_cache_size = '12GB';
ALTER SYSTEM SET maintenance_work_mem = '1GB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET wal_buffers = '16MB';
ALTER SYSTEM SET default_statistics_target = 100;
ALTER SYSTEM SET random_page_cost = 1.1;
ALTER SYSTEM SET effective_io_concurrency = 200;
ALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET min_wal_size = '1GB';
ALTER SYSTEM SET max_wal_size = '4GB';
-- 重启 PostgreSQL
4.5.2 监控配置
Prometheus 配置(monitoring/prometheus.yml):
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# Node Exporter(系统指标)
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
# 后端服务指标
- job_name: 'backend'
static_configs:
- targets: ['backend:9091']
# Ollama 指标(如果支持)
- job_name: 'ollama'
static_configs:
- targets: ['ollama:11434']
# Qdrant 指标
- job_name: 'qdrant'
static_configs:
- targets: ['qdrant:6333']
metrics_path: '/metrics'
# PostgreSQL Exporter
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']
# Redis Exporter
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']
Grafana 监控面板(monitoring/grafana/dashboards/ai-platform.json):
主要监控指标:
- 系统指标:CPU、内存、磁盘、网络
- 应用指标:QPS、响应时间、错误率
- 模型指标:推理耗时、Token 使用量
- 数据库指标:连接数、慢查询、缓存命中率
- 向量库指标:搜索延迟、索引大小
5. 成本与资源评估
5.1 硬件成本
| 配置等级 | 适用场景 | 硬件成本(一次性) | 月度成本 |
|---|---|---|---|
| 入门级 | 10 并发,7B 模型 | ¥30,000 | ¥500(电费) |
| 标准级 | 50 并发,14B 模型 | ¥80,000 | ¥1,500 |
| 企业级 | 200 并发,32B 模型 | ¥200,000 | ¥5,000 |
| 旗舰级 | 1000+ 并发,多模型 | ¥500,000+ | ¥15,000+ |
5.2 软件成本
| 组件 | 开源方案 | 商业方案 | 年度成本 |
|---|---|---|---|
| Dify | 社区版(免费) | 企业版 | ¥0 / ¥100,000+ |
| Ollama | 开源免费 | - | ¥0 |
| Qdrant | 开源免费 | 云托管版 | ¥0 / ¥50,000+ |
| 监控系统 | Prometheus + Grafana | Datadog / New Relic | ¥0 / ¥80,000+ |
总结:采用全开源方案,软件成本为 ¥0。
5.3 人力成本
| 角色 | 人数 | 职责 | 月薪估算 |
|---|---|---|---|
| 架构师 | 1 | 系统设计、技术选型 | ¥40,000 |
| 后端工程师 | 2 | API 开发、模型集成 | ¥25,000 × 2 |
| 前端工程师 | 1 | UI/UX 开发 | ¥20,000 |
| 运维工程师 | 1 | 部署、监控、运维 | ¥22,000 |
| 算法工程师 | 1 | 模型调优、RAG 优化 | ¥35,000 |
总计:约 ¥167,000/月(开发期)
运维期:1-2 人即可维护,约 ¥30,000-60,000/月
5.4 与云服务对比
假设:每天 10,000 次对话,平均每次 1000 tokens
| 方案 | 月度成本 | 年度成本 | 备注 |
|---|---|---|---|
| 云端 API(OpenAI / SiliconFlow) | ¥50,000+ | ¥600,000+ | 按调用量计费 |
| 私有化部署(硬件 + 运维) | ¥10,000 | ¥120,000 | 一次性硬件投入后成本低 |
结论:
- 小规模(< 1000 次/天):云端 API 更划算
- 中等规模(1000-10000 次/天):6-12 个月回本
- 大规模(> 10000 次/天):私有化部署节省 80%+ 成本
6. 安全与合规
6.1 数据安全
6.1.1 网络隔离
┌─────────────────────────────────────────┐
│ 企业内网 │
│ ┌──────────────────────────────────┐ │
│ │ DMZ 区(对外服务) │ │
│ │ - Nginx 反向代理 │ │
│ │ - 前端服务 │ │
│ └──────────┬───────────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────────┐ │
│ │ 应用区(核心业务) │ │
│ │ - 后端 API │ │
│ │ - LangChain 引擎 │ │
│ └──────────┬───────────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────────┐ │
│ │ 数据区(敏感数据) │ │
│ │ - PostgreSQL │ │
│ │ - Qdrant │ │
│ │ - Ollama(模型) │ │
│ │ ⚠️ 禁止外网访问 │ │
│ └──────────────────────────────────┘ │
│ │
│ 🔒 防火墙规则: │
│ - DMZ → 应用:允许 │
│ - 应用 → 数据:允许 │
│ - 外网 → 数据:禁止 │
└─────────────────────────────────────────┘
6.1.2 访问控制
Nginx 配置:
# 限制 IP 访问(仅允许内网)
location /api/ {
allow 10.0.0.0/8; # 内网 A 类地址
allow 172.16.0.0/12; # 内网 B 类地址
allow 192.168.0.0/16; # 内网 C 类地址
deny all;
proxy_pass http://backend;
}
# 限制请求频率(防止滥用)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
location /api/chat {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend;
}
应用层鉴权:
// backend/src/middleware/auth.js
import jwt from 'jsonwebtoken';
export const authenticate = (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
};
// 使用鉴权中间件
app.use('/api/chat', authenticate, chatRoutes);
6.1.3 数据加密
传输加密(TLS/SSL):
# 生成自签名证书(测试用)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout nginx/ssl/private.key \
-out nginx/ssl/certificate.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=YourCompany/CN=ai.your-company.internal"
# 生产环境使用正式证书(Let's Encrypt 或购买)
Nginx HTTPS 配置:
server {
listen 443 ssl http2;
server_name ai.your-company.internal;
ssl_certificate /etc/nginx/ssl/certificate.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
# SSL 优化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HSTS(强制 HTTPS)
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://frontend;
}
}
# HTTP 自动跳转 HTTPS
server {
listen 80;
server_name ai.your-company.internal;
return 301 https://$server_name$request_uri;
}
数据库加密:
# PostgreSQL 配置(docker-compose.yml)
postgres:
image: postgres:15-alpine
environment:
# 启用数据加密
- POSTGRES_INITDB_ARGS=--data-checksums
volumes:
# 使用加密卷
- type: volume
source: postgres_data
target: /var/lib/postgresql/data
volume:
nocopy: true
command:
- postgres
- -c
- ssl=on
- -c
- ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
- -c
- ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
6.1.4 敏感数据脱敏
// backend/src/utils/security.js
/**
* 脱敏手机号
*/
export function maskPhone(phone) {
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}
/**
* 脱敏身份证号
*/
export function maskIdCard(idCard) {
return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2');
}
/**
* 脱敏邮箱
*/
export function maskEmail(email) {
return email.replace(/(.{2}).+(@.+)/, '$1***$2');
}
/**
* 日志脱敏(自动识别敏感字段)
*/
export function maskSensitiveData(data) {
const sensitiveFields = ['password', 'apiKey', 'token', 'secret', 'phone', 'idCard', 'email'];
if (typeof data !== 'object') return data;
const masked = { ...data };
for (const key of Object.keys(masked)) {
if (sensitiveFields.some(field => key.toLowerCase().includes(field))) {
masked[key] = '***';
}
}
return masked;
}
6.2 合规性
6.2.1 审计日志
// backend/src/middleware/auditLog.js
import fs from 'fs';
import path from 'path';
export const auditLogger = (req, res, next) => {
const logEntry = {
timestamp: new Date().toISOString(),
user: req.user?.id || 'anonymous',
ip: req.ip,
method: req.method,
path: req.path,
userAgent: req.get('user-agent'),
body: maskSensitiveData(req.body),
};
// 写入审计日志文件
const logFile = path.join(__dirname, '../../logs/audit.log');
fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n');
next();
};
// 使用审计日志
app.use('/api/*', auditLogger);
6.2.2 数据留存策略
-- 定期清理过期数据(PostgreSQL)
CREATE OR REPLACE FUNCTION cleanup_old_data()
RETURNS void AS $$
BEGIN
-- 删除 90 天前的对话记录
DELETE FROM conversations WHERE created_at < NOW() - INTERVAL '90 days';
-- 删除 30 天前的审计日志
DELETE FROM audit_logs WHERE created_at < NOW() - INTERVAL '30 days';
-- 删除 7 天前的临时文件记录
DELETE FROM temp_files WHERE created_at < NOW() - INTERVAL '7 days';
END;
$$ LANGUAGE plpgsql;
-- 定时任务(每天凌晨 2 点执行)
CREATE EXTENSION IF NOT EXISTS pg_cron;
SELECT cron.schedule('cleanup-old-data', '0 2 * * *', 'SELECT cleanup_old_data()');
6.3 模型安全
6.3.1 输入验证
// backend/src/middleware/validation.js
/**
* 聊天输入验证
*/
export const validateChatInput = (req, res, next) => {
const { message } = req.body;
// 1. 检查输入长度
if (!message || message.length === 0) {
return res.status(400).json({ error: '消息不能为空' });
}
if (message.length > 10000) {
return res.status(400).json({ error: '消息长度不能超过 10000 字符' });
}
// 2. 检查恶意内容(SQL 注入、XSS)
const sqlInjectionPattern = /(\bDROP\b|\bDELETE\b|\bINSERT\b|\bUPDATE\b|\bEXEC\b)/i;
const xssPattern = /<script[^>]*>.*?<\/script>/gi;
if (sqlInjectionPattern.test(message) || xssPattern.test(message)) {
return res.status(400).json({ error: '检测到非法输入' });
}
// 3. 敏感词过滤(根据企业需求)
const bannedWords = ['敏感词1', '敏感词2']; // 实际使用时从配置文件加载
for (const word of bannedWords) {
if (message.includes(word)) {
return res.status(400).json({ error: '输入包含敏感内容' });
}
}
next();
};
app.post('/api/chat', validateChatInput, chatController);
6.3.2 输出过滤
// backend/src/utils/contentFilter.js
/**
* 过滤模型输出中的敏感信息
*/
export function filterOutput(text) {
// 1. 脱敏手机号
text = text.replace(/1[3-9]\d{9}/g, (match) => maskPhone(match));
// 2. 脱敏身份证号
text = text.replace(/\d{17}[\dXx]/g, (match) => maskIdCard(match));
// 3. 脱敏邮箱
text = text.replace(/[\w.-]+@[\w.-]+\.\w+/g, (match) => maskEmail(match));
// 4. 移除可能的 API Key
text = text.replace(/\b(sk-[a-zA-Z0-9]{32,})\b/g, '***API_KEY***');
return text;
}
7. 监控与运维
7.1 监控指标
7.1.1 系统指标
| 指标类型 | 监控项 | 告警阈值 |
|---|---|---|
| CPU | 使用率 | > 80% |
| 内存 | 使用率 | > 85% |
| 磁盘 | 使用率、IOPS | > 90%, < 100 IOPS |
| 网络 | 带宽、连接数 | > 80% 带宽 |
| GPU | 显存、利用率 | > 90% 显存 |
7.1.2 应用指标
| 指标类型 | 监控项 | 告警阈值 |
|---|---|---|
| QPS | 每秒请求数 | - |
| 延迟 | P50/P95/P99 响应时间 | P95 > 5s |
| 错误率 | 5xx 错误比例 | > 1% |
| 并发 | 活跃连接数 | - |
7.1.3 业务指标
| 指标类型 | 监控项 | 告警阈值 |
|---|---|---|
| 模型推理 | 平均耗时、Token 使用量 | - |
| RAG 检索 | 检索耗时、命中率 | P95 > 1s |
| 会话 | 活跃会话数、平均长度 | - |
| 文件上传 | 成功率、平均大小 | 成功率 < 95% |
7.2 日志管理
7.2.1 日志收集(ELK Stack)
# docker-compose.logging.yml
version: '3.8'
services:
# Elasticsearch(日志存储)
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.9.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
restart: unless-stopped
# Logstash(日志处理)
logstash:
image: docker.elastic.co/logstash/logstash:8.9.0
container_name: logstash
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
- ./logs:/logs:ro
ports:
- "5044:5044"
depends_on:
- elasticsearch
restart: unless-stopped
# Kibana(日志可视化)
kibana:
image: docker.elastic.co/kibana/kibana:8.9.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
restart: unless-stopped
volumes:
es_data:
Logstash 配置(logstash/pipeline/logstash.conf):
input {
file {
path => "/logs/*.log"
start_position => "beginning"
codec => json
}
}
filter {
# 解析应用日志
if [type] == "app" {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" }
}
date {
match => [ "timestamp", "ISO8601" ]
}
}
# 解析审计日志
if [type] == "audit" {
json {
source => "message"
}
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "ai-platform-%{+YYYY.MM.dd}"
}
stdout {
codec => rubydebug
}
}
7.2.2 日志轮转
# backend/logrotate.conf
/app/logs/*.log {
daily # 每天轮转
missingok # 文件丢失不报错
rotate 30 # 保留 30 天
compress # 压缩旧日志
delaycompress # 延迟压缩
notifempty # 空文件不轮转
create 0640 node node # 创建新文件的权限
sharedscripts
postrotate
docker exec ai-backend kill -USR1 $(cat /var/run/backend.pid) 2>/dev/null || true
endscript
}
7.3 备份策略
7.3.1 数据库备份
#!/bin/bash
# scripts/backup-database.sh
BACKUP_DIR="/backup/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.sql.gz"
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份 PostgreSQL
docker exec ai-postgres pg_dumpall -U aiuser | gzip > $BACKUP_FILE
# 上传到对象存储(可选)
# aws s3 cp $BACKUP_FILE s3://your-backup-bucket/postgres/
# 删除 30 天前的备份
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -delete
echo "数据库备份完成: $BACKUP_FILE"
7.3.2 向量数据库备份
#!/bin/bash
# scripts/backup-qdrant.sh
BACKUP_DIR="/backup/qdrant"
DATE=$(date +%Y%m%d_%H%M%S)
# 创建快照
curl -X POST 'http://localhost:6333/collections/documents/snapshots' \
-H 'Content-Type: application/json'
# 下载快照
SNAPSHOT_NAME=$(curl 'http://localhost:6333/collections/documents/snapshots' | jq -r '.result[0].name')
curl "http://localhost:6333/collections/documents/snapshots/$SNAPSHOT_NAME" \
--output "$BACKUP_DIR/snapshot_$DATE.snapshot"
echo "Qdrant 备份完成: $BACKUP_DIR/snapshot_$DATE.snapshot"
7.3.3 定时备份(Cron)
# 编辑 crontab
crontab -e
# 添加定时任务
# 每天凌晨 1 点备份数据库
0 1 * * * /path/to/scripts/backup-database.sh >> /var/log/backup.log 2>&1
# 每天凌晨 2 点备份 Qdrant
0 2 * * * /path/to/scripts/backup-qdrant.sh >> /var/log/backup.log 2>&1
# 每周日凌晨 3 点备份整个 Docker 卷
0 3 * * 0 /path/to/scripts/backup-volumes.sh >> /var/log/backup.log 2>&1
7.4 故障恢复
7.4.1 数据库恢复
# 恢复 PostgreSQL
docker exec -i ai-postgres psql -U aiuser < backup_20240126.sql
# 或者使用 pg_restore
docker exec -i ai-postgres pg_restore -U aiuser -d ai_platform < backup.dump
7.4.2 Qdrant 恢复
# 上传快照
curl -X POST 'http://localhost:6333/collections/documents/snapshots/upload' \
--form 'snapshot=@snapshot_20240126.snapshot'
# 从快照恢复
curl -X PUT 'http://localhost:6333/collections/documents/snapshots/snapshot_20240126/recover'
7.4.3 灾难恢复计划
| 故障类型 | 恢复时间目标(RTO) | 恢复点目标(RPO) | 恢复步骤 |
|---|---|---|---|
| 单容器故障 | < 5 分钟 | 0 | Docker 自动重启 |
| 数据库故障 | < 30 分钟 | 24 小时 | 从备份恢复 |
| 整体服务故障 | < 2 小时 | 24 小时 | 重新部署 + 数据恢复 |
| 物理服务器故障 | < 4 小时 | 24 小时 | 切换到备用服务器 |
8. 常见问题
8.1 部署相关
Q: Ollama 模型下载失败怎么办?
A: 如果网络不稳定,可以使用镜像或手动下载:
# 方法 1:使用国内镜像(如果有)
export OLLAMA_MIRRORS="https://your-mirror.com"
ollama pull qwen2.5:7b
# 方法 2:手动下载模型文件
# 1. 从 ModelScope 下载模型
# 2. 创建 Ollama Modelfile
cat > Modelfile << EOF
FROM /path/to/model
TEMPLATE """{{ .System }}
{{ .Prompt }}"""
PARAMETER stop "<|im_end|>"
EOF
# 3. 导入模型
ollama create qwen2.5:7b -f Modelfile
Q: GPU 显存不足怎么办?
A: 可以使用量化模型或调整参数:
# 方法 1:使用量化模型(Q4、Q5)
ollama pull qwen2.5:7b-q4_0 # 4-bit 量化,显存需求减半
# 方法 2:限制上下文长度
export OLLAMA_MAX_LOADED_MODELS=1
export OLLAMA_NUM_PARALLEL=2
# 方法 3:使用 CPU(较慢)
docker run -d -v ollama:/root/.ollama -p 11434:11434 \
--name ollama ollama/ollama
Q: Qdrant 内存占用过高?
A: 调整配置以使用磁盘存储:
qdrant:
environment:
- QDRANT__STORAGE__STORAGE_PATH=/qdrant/storage
- QDRANT__STORAGE__PERFORMANCE__MAX_SEGMENT_SIZE_KB=200000
- QDRANT__STORAGE__OPTIMIZERS__MEMMAP_THRESHOLD_KB=50000
8.2 性能相关
Q: 推理速度慢怎么优化?
A: 多方面优化:
-
模型层面:
- 使用更小的模型(7B → 3B)
- 使用量化模型(Q4_0、Q4_K_M)
- 启用 GPU 加速
-
系统层面:
- 增加
OLLAMA_NUM_PARALLEL - 使用 vLLM 替代 Ollama
- 启用批处理
- 增加
-
架构层面:
- 添加 Redis 缓存常见问题
- 使用负载均衡分发请求
- 部署多个模型实例
Q: RAG 检索准确率低?
A: 优化检索策略:
// 1. 优化 Chunk 大小
const chunkSize = 500; // 调整为 300-800 字符
// 2. 增加重叠窗口
const chunkOverlap = 100;
// 3. 混合检索(向量 + 关键词)
const results = await Promise.all([
vectorSearch(query, { limit: 10 }),
keywordSearch(query, { limit: 5 }),
]);
// 4. 重排序(Reranking)
const reranked = await rerankModel.rank(query, results);
// 5. 调整检索参数
const searchResults = await qdrant.search('documents', {
vector: queryEmbedding,
limit: 20, // 增加初始检索数量
score_threshold: 0.7, // 设置相似度阈值
});
8.3 安全相关
Q: 如何防止数据泄露?
A: 多层防护:
- 网络隔离:数据库仅内网访问
- 访问控制:JWT 鉴权 + IP 白名单
- 数据脱敏:日志和输出自动脱敏
- 审计日志:记录所有敏感操作
- 加密传输:全站 HTTPS
- 定期审计:安全扫描和渗透测试
Q: 如何防止模型被滥用?
A: 限流和监控:
// 1. 基于用户的限流
const rateLimiter = new RateLimiter({
tokensPerInterval: 100, // 每小时 100 次
interval: 'hour',
});
// 2. 基于 IP 的限流(Nginx)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
// 3. 异常检测
if (requestCount > threshold) {
sendAlert('可能存在滥用行为');
}
// 4. 内容审核
const auditResult = await contentAudit(userInput);
if (auditResult.risk > 0.8) {
rejectRequest();
}
8.4 运维相关
Q: 如何滚动更新服务?
A: 使用 Docker Compose 滚动更新:
# 1. 拉取最新镜像
docker compose pull backend frontend
# 2. 滚动更新(不中断服务)
docker compose up -d --no-deps --build backend
docker compose up -d --no-deps --build frontend
# 3. 验证新版本
curl http://localhost:3001/health
# 4. 如果有问题,回滚到旧版本
docker compose up -d --no-deps backend:old-tag
Q: 如何扩容?
A: 水平扩展:
# docker-compose.yml
services:
backend:
image: ai-backend:latest
deploy:
replicas: 3 # 部署 3 个实例
# ...
ollama:
image: ollama/ollama:latest
deploy:
replicas: 2 # 部署 2 个模型实例
# ...
配置 Nginx 负载均衡:
upstream backend {
least_conn; # 最少连接算法
server backend-1:3001 max_fails=3 fail_timeout=30s;
server backend-2:3001 max_fails=3 fail_timeout=30s;
server backend-3:3001 max_fails=3 fail_timeout=30s;
}
upstream ollama {
ip_hash; # 同一用户路由到同一实例(保持会话)
server ollama-1:11434;
server ollama-2:11434;
}
9. 总结与建议
9.1 推荐部署方案
根据不同规模的企业,推荐以下方案:
小型企业(< 50 人)
方案:轻量级部署
- 大模型:Ollama + Qwen2.5:7b
- 工作流:LangChain(已集成)
- 向量库:Qdrant
- 部署方式:单机 Docker Compose
- 硬件:1 台服务器(8核 32GB + RTX 3090)
- 成本:硬件 ¥30,000 + 运维 1 人
中型企业(50-500 人)
方案:标准化部署
- 大模型:Ollama + Qwen2.5:14b
- 工作流:Dify 社区版 + LangChain
- 向量库:Qdrant(集群模式)
- 部署方式:Docker Compose + Nginx
- 硬件:2 台服务器(主备)
- 成本:硬件 ¥80,000 + 运维 1-2 人
大型企业(> 500 人)
方案:企业级高可用部署
- 大模型:vLLM + Qwen2.5:32b(多实例)
- 工作流:Dify 企业版 / 自研
- 向量库:Milvus(分布式)
- 部署方式:Kubernetes + Helm
- 硬件:5+ 台服务器(集群)
- 成本:硬件 ¥200,000+ + 运维团队
9.2 实施路线图
第 1 周:基础环境搭建
- ✅ 硬件采购和机房部署
- ✅ 操作系统和 Docker 安装
- ✅ 网络配置和防火墙设置
第 2-3 周:核心组件部署
- ✅ Ollama 模型部署和测试
- ✅ Qdrant 向量数据库配置
- ✅ PostgreSQL 数据库初始化
- ✅ Dify 平台部署(可选)
第 4 周:应用迁移
- ✅ 后端配置迁移到私有化环境
- ✅ 前端 API 地址更新
- ✅ 功能测试和性能调优
第 5-6 周:监控和安全
- ✅ Prometheus + Grafana 监控部署
- ✅ 日志收集和分析系统
- ✅ 安全加固和渗透测试
- ✅ 备份和恢复演练
第 7 周:试运行
- ✅ 小范围用户试用
- ✅ 收集反馈和问题修复
- ✅ 性能优化和容量规划
第 8 周:正式上线
- ✅ 全员开放使用
- ✅ 培训和文档交付
- ✅ 运维交接
9.3 关键成功因素
-
选择合适的模型
- 根据业务需求选择模型大小
- 中文业务优先选择 Qwen 系列
- 平衡性能和资源消耗
-
充分的测试
- 压力测试(并发、长时间运行)
- 功能测试(各场景覆盖)
- 安全测试(渗透、数据泄露)
-
完善的监控
- 实时监控系统和应用指标
- 及时发现和处理异常
- 定期生成运营报告
-
持续优化
- 根据使用情况调整资源配置
- 定期更新模型和组件
- 收集用户反馈持续改进
9.4 下一步行动
-
立即可做:
- ✅ 评估现有硬件资源
- ✅ 确定部署规模和方案
- ✅ 申请预算和采购硬件
-
短期规划(1-2 个月):
- ✅ 搭建测试环境
- ✅ 完成核心功能迁移
- ✅ 小范围试用验证
-
中期规划(3-6 个月):
- ✅ 正式上线投产
- ✅ 优化性能和成本
- ✅ 扩展高级功能
-
长期规划(6-12 个月):
- ✅ 模型微调和定制化
- ✅ 多模态能力扩展
- ✅ 智能体和自动化工作流
10. 附录
10.1 相关文档
10.2 社区资源
- Dify 中文社区:https://github.com/langgenius/dify/discussions
- Ollama 中文教程:https://ollama.com/blog
- LangChain 中文社区:https://langchain.com.cn/
10.3 技术支持
如需专业技术支持,可联系:
- Dify 企业版支持:support@dify.ai
- 本地部署咨询:(根据实际情况填写)
文档版本:v1.0
最后更新:2026-01-26
维护者:马年行大运
🎉 祝您部署顺利!

浙公网安备 33010602011771号