本节导读:从开发环境到生产环境的完整Embedding服务部署指南,涵盖架构设计、容器化、API服务和监控运维
生产环境的Embedding服务需要满足以下关键需求:
典型的Embedding服务架构包含以下层次:
from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from typing import List, Optional from sentence_transformers import SentenceTransformer import numpy as np import asyncio import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI( title="Embedding Service", description="文本Embedding推理服务", version="1.0.0" ) class EmbedRequest(BaseModel): texts: List[str] = Field(..., min_length=1, max_length=64, description="待编码文本列表") model_name: Optional[str] = Field(None, description="模型名称") normalize: bool = Field(True, description="是否归一化向量") class EmbedResponse(BaseModel): embeddings: List[List[float]] dim: int model: str count: int # 全局模型管理 model_cache: dict = {} def get_model(model_name: str = "all-MiniLM-L6-v2") -> SentenceTransformer: """懒加载模型""" if model_name not in model_cache: logger.info(f"Loading model: {model_name}") model_cache[model_name] = SentenceTransformer(model_name) return model_cache[model_name] @app.on_event("startup") async def warmup(): """服务启动时预热模型""" logger.info("Warming up model...") model = get_model() model.encode(["warmup"]) logger.info("Model warmup complete") @app.post("/v1/embed", response_model=EmbedResponse) async def embed(request: EmbedRequest): """文本编码接口""" model_name = request.model_name or "all-MiniLM-L6-v2" try: model = get_model(model_name) # 在线程池中执行同步的模型推理 loop = asyncio.get_event_loop() embeddings = await loop.run_in_executor( None, lambda: model.encode( request.texts, normalize_embeddings=request.normalize, show_progress_bar=False ) ) return EmbedResponse( embeddings=embeddings.tolist(), dim=embeddings.shape[1], model=model_name, count=len(request.texts) ) except Exception as e: logger.error(f"Embedding failed: {e}") raise HTTPException(status_code=500, detail=str(e)) @app.get("/health") async def health_check(): """健康检查""" return {"status": "healthy", "models_loaded": list(model_cache.keys())} @app.get("/models") async def list_models(): """列出可用模型""" return {"available": ["all-MiniLM-L6-v2", "paraphrase-multilingual-MiniLM-L12-v2"]}
生产环境中批量请求可以显著提高GPU利用率:
import time from collections import deque class BatchProcessor: """自动批量聚合请求""" def __init__(self, model, max_batch_size: int = 32, max_wait_ms: int = 10): self.model = model self.max_batch_size = max_batch_size self.max_wait_ms = max_wait_ms self.queue = deque() self.results = {} async def process_single(self, text: str) -> List[float]: """单文本请求,自动聚合为批量""" future = asyncio.Future() request_id = id(future) self.queue.append({"id": request_id, "text": text, "future": future}) if len(self.queue) >= self.max_batch_size: await self._flush() # 等待结果 return await asyncio.wait_for(future, timeout=5.0) async def _flush(self): """执行批量推理""" if not self.queue: return batch = [self.queue.popleft() for _ in range(len(self.queue))] texts = [item["text"] for item in batch] loop = asyncio.get_event_loop() embeddings = await loop.run_in_executor( None, lambda: self.model.encode(texts, normalize_embeddings=True) ) for i, item in enumerate(batch): item["future"].set_result(embeddings[i].tolist())
FROM python:3.10-slim WORKDIR /app # 安装系统依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ libgomp1 && \ rm -rf /var/lib/apt/lists/* # 安装Python依赖 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY app/ ./app/ COPY models/ ./models/ # 预下载模型(构建时下载,避免运行时下载) RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')" EXPOSE 8000 # 启动命令 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
requirements.txt:
fastapi>=0.100.0 uvicorn[standard]>=0.20.0 sentence-transformers>=2.2.0 torch>=2.0.0 numpy>=1.24.0 pydantic>=2.0.0
version: '3.8' services: embedding-service: build: . ports: - "8000:8000" environment: - MODEL_NAME=all-MiniLM-L6-v2 - MAX_BATCH_SIZE=32 - LOG_LEVEL=INFO volumes: - ./models:/app/models - ./logs:/app/logs deploy: resources: reservations: devices: - capabilities: [gpu] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 restart: unless-stopped nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf depends_on: - embedding-service
import time from prometheus_client import Counter, Histogram, generate_latest from fastapi import Response # Prometheus指标 REQUEST_COUNT = Counter('embedding_requests_total', 'Total requests', ['model', 'status']) REQUEST_LATENCY = Histogram('embedding_request_duration_seconds', 'Request latency', ['model']) BATCH_SIZE = Histogram('embedding_batch_size', 'Batch size per request') @app.middleware("http") async def metrics_middleware(request, call_next): start = time.time() response = await call_next(request) duration = time.time() - start REQUEST_LATENCY.labels(model="default").observe(duration) REQUEST_COUNT.labels(model="default", status=response.status_code).inc() return response @app.get("/metrics") async def metrics(): """Prometheus指标端点""" return Response(generate_latest(), media_type="text/plain")
import signal import os @app.on_event("shutdown") async def shutdown_event(): """优雅关闭:卸载模型,释放GPU显存""" logger.info("Shutting down, releasing models...") model_cache.clear() import torch if torch.cuda.is_available(): torch.cuda.empty_cache() logger.info("Shutdown complete")
生产环境推荐以下部署架构:
负载均衡 + 多实例:
缓存层:
自动伸缩:
Q:CPU部署和GPU部署的性能差距有多大?
A:GPU部署通常比CPU快5-20倍(取决于模型大小和批次大小)。小模型(如MiniLM)在CPU上也能达到可接受的延迟(<200ms),大规模服务建议使用GPU。
Q:如何估算所需的GPU资源?
A:需要考虑模型大小、并发量和延迟要求。例如,一个all-MiniLM-L6-v2模型约占GPU显存500MB,单张T4可以轻松运行多个实例。建议进行压测确定实际需求。
Q:服务冷启动时间如何优化?
A:在Docker镜像构建时预下载模型;使用Kubernetes的预热机制;保持最少实例数避免完全冷启动;将模型文件放在高速存储上。
Q:如何处理模型更新?
A:采用蓝绿部署或金丝雀发布策略。新版本与旧版本并行运行,逐步切换流量。确保新模型经过充分测试后再全量上线。
本节详细介绍了Embedding服务从开发到生产部署的完整方案。包括基于FastAPI的API服务构建、Docker容器化部署、服务监控与指标采集、高可用架构设计等关键环节。通过这些实践,开发者可以将Embedding模型稳定可靠地部署到生产环境。
关键词:服务部署, FastAPI, Docker, 容器化, 负载均衡, 监控告警, 高可用, 优雅关闭, Prometheus, GPU推理
难度:中高级
预计阅读:45分钟