Phi-3-Mini-128K模型服务化部署:使用Docker容器化与API封装
本文介绍了如何在星图GPU平台上自动化部署Phi-3-Mini-128K镜像,实现大模型服务化。通过Docker容器化与FastAPI封装,可将该轻量级大模型快速转化为标准Web API服务,典型应用于智能问答、内容生成等场景,便于集成到各类应用中。
Phi-3-Mini-128K模型服务化部署:使用Docker容器化与API封装
最近在折腾大模型本地部署,发现微软开源的Phi-3-Mini-128K是个挺有意思的“小钢炮”。模型不大,但能力不弱,128K的超长上下文更是亮点。不过,每次想用都得手动启动脚本,调试参数,实在麻烦。要是能把它封装成服务,随时随地通过API调用,那可就方便多了。
这篇文章,我就来聊聊怎么给Phi-3-Mini-128K“安个家”——用Docker把它容器化,再用FastAPI做个简单的推理服务。整个过程不算复杂,跟着步骤走,你也能拥有一个随时待命的模型API服务。无论是想集成到自己的应用里,还是为后续的微服务架构做准备,这套方法都能给你打个不错的基础。
1. 先理清思路:我们要做什么?
在动手敲代码之前,咱们先花两分钟把整个流程想明白。这样后面每一步做起来,心里都有谱。
简单来说,我们的目标是把Phi-3-Mini-128K模型变成一个标准的Web服务。想象一下,你有个智能助手被关在盒子里,我们得做三件事:
- 给它造个“房子”:用Docker容器,把模型、运行环境、所有依赖都打包进去。这个房子隔离性好,搬到任何支持Docker的机器上都能住。
- 开一扇“窗户”:用FastAPI(或者Flask)写一个Web应用。这扇窗户定义了外界如何与模型对话,比如发送一个POST请求,里面装着问题,模型就会把答案从窗户递出来。
- 配上“管家”和“说明书”:用Docker Compose来管理这个服务(比如设置环境变量、端口映射),再写个Dockerfile作为构建容器的“说明书”。
最终,你只需要一行命令 docker-compose up,一个完整的模型推理服务就启动了。你可以用curl、Postman或者自己写的程序去调用它。
2. 准备你的“施工场地”
工欲善其事,必先利其器。我们先确保手头有需要的工具。这里假设你用的是Linux或macOS系统,Windows用户建议使用WSL2以获得最佳体验。
2.1 基础工具检查
打开你的终端,依次检查并安装以下工具:
-
Python:模型推理的核心环境。Phi-3系列建议使用Python 3.9或更高版本。
python3 --version如果没安装,可以去Python官网下载,或者用系统包管理器(如
apt、brew)安装。 -
Docker:容器化的基石。它能让我们的应用环境保持一致性。
docker --version docker-compose --version如果显示“command not found”,你需要去Docker官网根据你的操作系统下载并安装Docker Desktop或Docker Engine。安装后,记得把当前用户加入
docker用户组,避免每次都要sudo。 -
Git:用来克隆模型仓库和我们的项目代码。
git --version同样,未安装的话请通过系统包管理器安装。
2.2 获取模型文件
Phi-3-Mini-128K的模型文件可以从Hugging Face Hub获取。我们创建一个项目目录,并在里面下载模型。
# 创建一个项目文件夹
mkdir phi3-mini-service && cd phi3-mini-service
# 使用Hugging Face的huggingface-cli工具下载(需先安装)
pip install huggingface-hub
huggingface-cli download microsoft/Phi-3-mini-128k-instruct --local-dir ./model
# 或者,如果你更喜欢用Git(注意仓库较大)
# git lfs install
# git clone https://huggingface.co/microsoft/Phi-3-mini-128k-instruct ./model
下载可能需要一些时间,毕竟模型有几个GB。完成后,你的目录里会有一个model文件夹,里面就是模型的所有文件。
3. 编写核心服务:FastAPI应用
现在来造那扇“窗户”。我们选择FastAPI,因为它现代、快速,而且能自动生成交互式API文档,特别适合这种场景。
在你的项目根目录(phi3-mini-service)下,创建一个名为app的文件夹,然后开始编写应用。
3.1 创建应用文件结构
mkdir app && cd app
touch main.py requirements.txt
3.2 编写依赖文件 requirements.txt
这个文件告诉Docker和Python需要安装哪些包。
fastapi>=0.104.0
uvicorn[standard]>=0.24.0
transformers>=4.36.0
torch>=2.0.0
accelerate>=0.25.0
sentencepiece>=0.1.99 # Phi-3的分词器需要
pydantic>=2.0.0
fastapi和uvicorn是我们的Web框架和服务器。transformers、torch、accelerate是加载和运行模型的核心。sentencepiece是Phi-3分词器必需的。
3.3 编写主应用逻辑 main.py
这是服务的大脑,定义了API接口和模型加载逻辑。
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional, List
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
import logging
import os
# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 定义API请求/响应格式
class ChatMessage(BaseModel):
role: str # “user” or “assistant”
content: str
class ChatRequest(BaseModel):
messages: List[ChatMessage]
max_new_tokens: Optional[int] = 512
temperature: Optional[float] = 0.7
top_p: Optional[float] = 0.9
do_sample: Optional[bool] = True
class ChatResponse(BaseModel):
response: str
model: str
usage: dict
# 初始化FastAPI应用
app = FastAPI(
title="Phi-3 Mini 128K Instruct API",
description="一个基于FastAPI封装的Phi-3-Mini-128K-Instruct模型推理服务。",
version="1.0.0"
)
# 全局变量,用于缓存加载的模型和分词器
_model = None
_tokenizer = None
_pipe = None
def load_model():
"""加载模型和分词器到内存(或GPU)"""
global _model, _tokenizer, _pipe
if _model is not None:
return
model_path = os.getenv("MODEL_PATH", "/app/model") # 从环境变量读取路径,默认为容器内路径
logger.info(f"正在从 {model_path} 加载模型...")
try:
# 加载分词器
_tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
# 加载模型,根据环境决定设备
device = 0 if torch.cuda.is_available() else -1 # 0代表GPU,-1代表CPU
_model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype=torch.float16 if device == 0 else torch.float32, # GPU上用半精度节省显存
device_map="auto" if device == 0 else None,
trust_remote_code=True
)
# 创建文本生成管道,方便调用
_pipe = pipeline(
"text-generation",
model=_model,
tokenizer=_tokenizer,
device=device
)
logger.info("模型加载成功!")
except Exception as e:
logger.error(f"模型加载失败: {e}")
raise
@app.on_event("startup")
async def startup_event():
"""FastAPI启动时自动加载模型"""
load_model()
@app.get("/")
async def root():
"""健康检查端点"""
return {"status": "healthy", "model": "Phi-3-Mini-128K-Instruct"}
@app.get("/health")
async def health_check():
"""更详细的健康检查,确认模型已加载"""
if _model is None:
raise HTTPException(status_code=503, detail="Model not loaded")
return {"status": "ready", "device": "cuda" if torch.cuda.is_available() else "cpu"}
@app.post("/v1/chat/completions", response_model=ChatResponse)
async def chat_completion(request: ChatRequest):
"""主要的聊天补全接口,遵循OpenAI API部分格式"""
if _pipe is None:
raise HTTPException(status_code=503, detail="Service unavailable")
# 将消息列表格式化为模型所需的提示文本
# Phi-3-mini-instruct 使用 `<|user|>\n` 和 `<|assistant|>\n` 格式
formatted_text = ""
for msg in request.messages:
if msg.role == "user":
formatted_text += f"<|user|>\n{msg.content}<|end|>\n"
elif msg.role == "assistant":
formatted_text += f"<|assistant|>\n{msg.content}<|end|>\n"
formatted_text += "<|assistant|>\n"
# 使用管道生成文本
try:
outputs = _pipe(
formatted_text,
max_new_tokens=request.max_new_tokens,
temperature=request.temperature,
top_p=request.top_p,
do_sample=request.do_sample,
pad_token_id=_tokenizer.eos_token_id,
eos_token_id=_tokenizer.eos_token_id,
)
generated_text = outputs[0]['generated_text']
# 只提取助理的新回复部分
response_text = generated_text[len(formatted_text):].strip()
# 简单计算token使用量(近似)
input_tokens = len(_tokenizer.encode(formatted_text))
output_tokens = len(_tokenizer.encode(response_text))
return ChatResponse(
response=response_text,
model="Phi-3-Mini-128K-Instruct",
usage={
"prompt_tokens": input_tokens,
"completion_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens
}
)
except Exception as e:
logger.error(f"推理过程中出错: {e}")
raise HTTPException(status_code=500, detail=f"Generation error: {str(e)}")
这段代码做了几件关键事:
- 定义了标准的请求/响应数据结构(模仿了OpenAI的格式,方便适配)。
- 在服务启动时自动加载模型到内存(或GPU)。
- 提供了根路径
/和/health用于健康检查。 - 创建了核心的
/v1/chat/completions接口,接收对话历史和参数,返回模型生成的回复。
4. 构建容器“说明书”:Dockerfile
有了应用代码,我们需要告诉Docker如何构建镜像。在项目根目录创建Dockerfile。
# 使用带有Python的官方镜像作为基础
FROM python:3.10-slim
# 设置工作目录
WORKDIR /app
# 安装系统依赖(如果需要,例如git)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# 首先复制依赖文件,利用Docker缓存层
COPY ./app/requirements.txt .
# 安装Python依赖
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# 复制模型文件(假设模型已下载到项目根目录的`model`文件夹)
# 注意:由于模型文件很大,这可能会使镜像体积庞大。
# 生产环境中,可以考虑将模型文件放在卷(volume)或通过网络存储挂载。
COPY ./model /app/model
# 复制应用代码
COPY ./app .
# 声明容器运行时监听的端口
EXPOSE 8000
# 设置环境变量(模型路径)
ENV MODEL_PATH=/app/model
# 启动命令:使用uvicorn运行FastAPI应用
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
这个Dockerfile做了以下工作:
- 从轻量级的Python镜像开始。
- 安装必要的系统编译工具(某些Python包可能需要)。
- 安装
requirements.txt里列出的所有Python包。 - 将我们之前下载的模型文件和应用代码复制到镜像内。
- 设置环境变量,指定模型路径。
- 最后,定义启动命令,在容器内的8000端口运行我们的FastAPI应用。
注意:将数GB的模型文件直接打包进镜像会导致镜像非常庞大。对于原型或固定环境,这没问题。但在生产环境,你可能需要考虑使用Docker卷(Volume)或网络文件系统(如NFS)来挂载模型文件,这样镜像会更轻量,更新模型也更方便。
5. 使用Docker Compose编排服务
单个服务用Docker run命令也能跑,但用Docker Compose管理起来更优雅,特别是未来需要添加数据库、缓存等其他服务时。在项目根目录创建docker-compose.yml。
version: '3.8'
services:
phi3-api:
build: .
container_name: phi3-mini-api
ports:
- "8000:8000" # 将宿主机的8000端口映射到容器的8000端口
environment:
- MODEL_PATH=/app/model
# 可以添加其他环境变量,如日志级别
- LOG_LEVEL=INFO
volumes:
# 挂载模型目录(可选):如果模型文件在宿主机上,可以挂载进来,避免复制进镜像
# - ./model:/app/model
# 挂载日志目录(可选)
# - ./logs:/app/logs
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
# 健康检查,确保服务真正就绪
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s # 给模型加载留出足够时间
restart: unless-stopped
networks:
- phi3-network
# 定义一个网络,方便未来扩展其他服务
networks:
phi3-network:
driver: bridge
这个配置文件的关键点:
build: .:告诉Compose基于当前目录的Dockerfile构建镜像。ports:端口映射,让你能在宿主机通过localhost:8000访问服务。environment:设置环境变量。deploy.resources:这是配置GPU支持的关键。它告诉Docker Compose(需要Docker with NVIDIA Container Toolkit)将宿主机的GPU资源分配给容器。如果你的机器没有NVIDIA GPU,可以移除这部分。healthcheck:定义了健康检查策略,Docker会定期调用/health接口,确保服务正常运行。这对于生产环境编排和负载均衡器很重要。restart: unless-stopped:确保容器在意外退出时自动重启。
6. 构建、运行与测试
一切就绪,让我们启动这个服务。
6.1 构建并启动服务
在项目根目录(有docker-compose.yml的目录)下,运行:
# 使用docker-compose构建镜像并启动服务(-d 代表后台运行)
docker-compose up -d --build
第一次运行会花费较长时间,因为它需要下载基础镜像、安装依赖、构建镜像。看到phi3-mini-api容器状态变为healthy就表示成功了。
你可以用以下命令查看日志和状态:
# 查看实时日志
docker-compose logs -f phi3-api
# 查看容器状态
docker-compose ps
6.2 测试API接口
服务运行后,我们来测试一下。
-
健康检查:
curl http://localhost:8000/health应该返回类似
{"status":"ready","device":"cuda"}的JSON。 -
测试聊天接口(使用curl):
curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "用简单的语言解释一下什么是人工智能?"} ], "max_new_tokens": 150 }'如果一切正常,你会收到一个JSON响应,其中
response字段包含了模型生成的答案。 -
使用交互式API文档: FastAPI自动生成了超棒的文档。打开你的浏览器,访问
http://localhost:8000/docs。你会看到一个Swagger UI界面,里面列出了所有API端点,甚至可以在这里直接点击“Try it out”进行测试,非常方便。
6.3 实现简单的负载均衡(可选进阶)
如果你的服务访问量变大,一个实例可能不够。你可以使用Docker Compose轻松扩展多个实例,并搭配一个反向代理(如Nginx)做负载均衡。
-
创建一个简单的
nginx.conf配置文件:upstream phi3_backend { # 指向docker-compose中定义的服务名和端口 server phi3-api-1:8000; server phi3-api-2:8000; # 可以添加更多... } server { listen 80; server_name localhost; location / { proxy_pass http://phi3_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } -
修改
docker-compose.yml,添加Nginx服务和扩展phi3-api:version: '3.8' services: phi3-api: build: . # ... 其他配置不变 ... networks: - phi3-network nginx: image: nginx:alpine container_name: phi3-nginx-lb ports: - "8080:80" # 通过宿主机的8080端口访问负载均衡器 volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - phi3-api networks: - phi3-network networks: phi3-network: driver: bridge -
扩展phi3-api服务实例:
docker-compose up -d --scale phi3-api=2现在,访问
http://localhost:8080的请求会被Nginx分发到两个phi3-api实例上。Docker Compose会自动将服务名(如phi3-api-1)解析为对应的容器IP。
7. 部署到生产环境的一些思考
把服务跑在本地笔记本上只是第一步。要部署到真正的生产服务器(比如云主机),还需要考虑更多:
- 安全性:API接口需要添加认证(如API Key、JWT令牌)。可以在FastAPI应用中使用中间件(Middleware)来实现。
- 性能监控:集成像Prometheus这样的监控工具,收集请求延迟、错误率、GPU使用率等指标。
- 日志管理:将容器日志集中收集到ELK(Elasticsearch, Logstash, Kibana)或Loki等系统中,方便排查问题。
- 模型更新:如何在不中断服务的情况下更新模型?可以考虑蓝绿部署或使用模型版本管理,通过环境变量切换模型路径。
- 资源限制:在
docker-compose.yml中为服务设置CPU和内存限制(cpus,mem_limit),防止单个容器耗尽主机资源。 - 使用生产级服务器:将uvicorn的
--reload参数移除,并使用更高效的ASGI服务器,如gunicorn配合uvicorn工作进程。
8. 写在最后
走完这一趟,你应该已经拥有了一个在Docker容器中运行的、可通过API调用的Phi-3-Mini-128K模型服务。从编写清晰的FastAPI应用,到用Dockerfile定义环境,再到用Docker Compose编排和配置健康检查,这套流程是很多AI模型服务化的标准打法。
最大的好处就是环境隔离和一键部署。你现在可以把整个项目文件夹打包,扔到任何有Docker的机器上,执行docker-compose up -d,服务就能跑起来。再结合一些CI/CD工具,就能实现自动化部署和更新。
当然,这只是一个起点。根据你的实际需求,可能还需要添加批处理接口、流式响应(SSE)、更复杂的对话历史管理等功能。但有了这个基础框架,后续的扩展都会容易很多。希望这个教程能帮你省去一些摸索的时间,快去试试把你的模型服务跑起来吧!
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)