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服务。想象一下,你有个智能助手被关在盒子里,我们得做三件事:

  1. 给它造个“房子”:用Docker容器,把模型、运行环境、所有依赖都打包进去。这个房子隔离性好,搬到任何支持Docker的机器上都能住。
  2. 开一扇“窗户”:用FastAPI(或者Flask)写一个Web应用。这扇窗户定义了外界如何与模型对话,比如发送一个POST请求,里面装着问题,模型就会把答案从窗户递出来。
  3. 配上“管家”和“说明书”:用Docker Compose来管理这个服务(比如设置环境变量、端口映射),再写个Dockerfile作为构建容器的“说明书”。

最终,你只需要一行命令 docker-compose up,一个完整的模型推理服务就启动了。你可以用curl、Postman或者自己写的程序去调用它。

2. 准备你的“施工场地”

工欲善其事,必先利其器。我们先确保手头有需要的工具。这里假设你用的是Linux或macOS系统,Windows用户建议使用WSL2以获得最佳体验。

2.1 基础工具检查

打开你的终端,依次检查并安装以下工具:

  1. Python:模型推理的核心环境。Phi-3系列建议使用Python 3.9或更高版本。

    python3 --version
    

    如果没安装,可以去Python官网下载,或者用系统包管理器(如aptbrew)安装。

  2. Docker:容器化的基石。它能让我们的应用环境保持一致性。

    docker --version
    docker-compose --version
    

    如果显示“command not found”,你需要去Docker官网根据你的操作系统下载并安装Docker Desktop或Docker Engine。安装后,记得把当前用户加入docker用户组,避免每次都要sudo

  3. 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
  • fastapiuvicorn是我们的Web框架和服务器。
  • transformerstorchaccelerate是加载和运行模型的核心。
  • 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)}")

这段代码做了几件关键事:

  1. 定义了标准的请求/响应数据结构(模仿了OpenAI的格式,方便适配)。
  2. 在服务启动时自动加载模型到内存(或GPU)。
  3. 提供了根路径//health用于健康检查。
  4. 创建了核心的/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做了以下工作:

  1. 从轻量级的Python镜像开始。
  2. 安装必要的系统编译工具(某些Python包可能需要)。
  3. 安装requirements.txt里列出的所有Python包。
  4. 将我们之前下载的模型文件和应用代码复制到镜像内。
  5. 设置环境变量,指定模型路径。
  6. 最后,定义启动命令,在容器内的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接口

服务运行后,我们来测试一下。

  1. 健康检查

    curl http://localhost:8000/health
    

    应该返回类似 {"status":"ready","device":"cuda"} 的JSON。

  2. 测试聊天接口(使用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字段包含了模型生成的答案。

  3. 使用交互式API文档: FastAPI自动生成了超棒的文档。打开你的浏览器,访问 http://localhost:8000/docs。你会看到一个Swagger UI界面,里面列出了所有API端点,甚至可以在这里直接点击“Try it out”进行测试,非常方便。

6.3 实现简单的负载均衡(可选进阶)

如果你的服务访问量变大,一个实例可能不够。你可以使用Docker Compose轻松扩展多个实例,并搭配一个反向代理(如Nginx)做负载均衡。

  1. 创建一个简单的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;
        }
    }
    
  2. 修改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
    
  3. 扩展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星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐