base-llm 4.1 模型部署實(shí)戰(zhàn) FastApi/docker-compose

FastAPI是基于Starlette和Pydantic構(gòu)建。pydantic能利用Python的類型提示(Type Hints)實(shí)現(xiàn)自動(dòng)的數(shù)據(jù)校驗(yàn)和轉(zhuǎn)換,極大減少了繁瑣的參數(shù)驗(yàn)證代碼。 能根據(jù)代碼自動(dòng)生成API文檔,方便開發(fā)者直接在頁面上調(diào)試。 同時(shí)充分利用了async/await等現(xiàn)代python特性。

一、 環(huán)境準(zhǔn)備

FastAPI[all]包含了FastAPI本身以及運(yùn)行它需要的ASGI服務(wù)器uvicorn

pip install `fastapi[all]`

uvicorn 是一個(gè)高性能的 ASGI (Asynchronous Server Gateway Interface) 服務(wù)器,用于在生產(chǎn)環(huán)境中運(yùn)行 FastAPI 應(yīng)用。ASGI 是現(xiàn)代 Python Web 框架用于與 Web 服務(wù)器通信的標(biāo)準(zhǔn)接口

二、 Ner任務(wù)部署

# code/C14/NerPredictor/main.py

import logging
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from predict import NerPredictor

# --- 全局配置 ---
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

MODEL_DIR = "./checkpoints"

# --- 數(shù)據(jù)模型定義 ---
class NerRequest(BaseModel):
    text: str

# --- FastAPI 應(yīng)用初始化 ---
app = FastAPI(
    title="命名實(shí)體識(shí)別 API",
    description="部署 NER 模型",
    version="1.0.0"
)

# --- 模型加載 ---
@app.on_event("startup")
async def startup_event():
    logger.info(f"開始加載模型,來源: {MODEL_DIR}")
    app.state.predictor = NerPredictor(model_dir=MODEL_DIR)
    logger.info("模型加載成功!")


# --- API 路由定義 ---
@app.post("/predict/ner")
async def predict_ner(request: NerRequest):
    """
    接收文本,返回命名實(shí)體識(shí)別結(jié)果。
    """
    try:
        text = request.text.strip()
        if not text:
            raise HTTPException(status_code=400, detail="輸入文本不能為空")

        logger.info(f"接收到NER請求: '{text}'")
        
        predictor = app.state.predictor
        entities = predictor.predict(text)

        logger.info(f"識(shí)別出實(shí)體: {entities}")

        return {
            "code": 0,
            "message": "成功",
            "data": {
                "text": text,
                "entities": entities
            }
        }
    except Exception as e:
        logger.error(f"NER預(yù)測時(shí)發(fā)生錯(cuò)誤: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"服務(wù)器內(nèi)部錯(cuò)誤: {e}")

@app.get("/health")
async def health_check():
    return {"status": "ok"}

@app.get("/")
async def root():
    return {"message": "歡迎使用命名實(shí)體識(shí)別 (NER) API"}

其中:

  1. 請求參數(shù)使用的Pydantic的BaseModel -> NerRequest
  2. 模型加載: 在app.on_event('startup')時(shí)候啟動(dòng), 初始化時(shí)會(huì)將NerPredictor實(shí)例存儲(chǔ)在app.state對象里。這種方式模型只會(huì)在啟動(dòng)時(shí)創(chuàng)建一次。

@app.on_event("startup") 啟動(dòng)前置方法; app.on_event("shutdown") 服務(wù)結(jié)束后置方法

  1. 實(shí)體識(shí)別api使用的是post方法, 參數(shù)是NerRequest.。異常通過HttpException拋出
  2. 服務(wù)健康狀態(tài) @app.get("/health")
  3. 根服務(wù) @app.get("/health")
  4. /docs 是FastAPI自動(dòng)生成的交互式API文檔

三、啟動(dòng)與測試

啟動(dòng),使用的uvicorn

uvicorn main:app --reload

測試,使用curl 測試

curl -X POST "http://127.0.0.1:8000/predict/ner" -H "Content-Type: application/json" -d "{\"text\":\"患者自述發(fā)熱、咳嗽,伴有輕微頭痛。\"}"

JSON響應(yīng):

{
    "code": 0,
    "message": "成功",
    "data": {
        "text": "患者自述發(fā)熱、咳嗽,伴有輕微頭痛。",
        "entities": [
            {
                "text": "發(fā)熱",
                "type": "sym",
                "start": 4,
                "end": 6
            },
            {
                "text": "咳嗽",
                "type": "sym",
                "start": 7,
                "end": 9
            },
            {
                "text": "頭",
                "type": "bod",
                "start": 14,
                "end": 15
            }
        ]
    }
}

四、生產(chǎn)服務(wù)

開發(fā)時(shí)使用的是uvicorn main:app --reload來啟動(dòng)服務(wù)。默認(rèn)只允許本地訪問。如果希望云服務(wù)器上測試,比如制定--host 0.0.0.0,這種方式不夠健壯。

Gunicorn 是一個(gè)成熟的 Python HTTP 服務(wù)器和進(jìn)程管理器,通過 -k uvicorn.workers.UvicornWorker 可以以多進(jìn)程方式托管 FastAPI 這樣的 ASGI 應(yīng)用。Uvicorn 負(fù)責(zé)異步處理請求,Gunicorn 負(fù)責(zé)監(jiān)聽端口、管理 worker 進(jìn)程和日志,這是常見且穩(wěn)定的生產(chǎn)部署組合。

gunicorn -w 3 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:8000
  • -w 3: 啟動(dòng) 3 個(gè)工作進(jìn)程(worker)。Gunicorn 官方文檔推薦的通用設(shè)置是 2 * CPU核心數(shù) + 1。但對于我們所使用的 UvicornWorker 這種異步工作進(jìn)程,由于其高效的并發(fā)處理能力,通常設(shè)置為 CPU核心數(shù) + 1 就足夠了。我們的服務(wù)器是 2 核 CPU,所以設(shè)置為 3。
  • -k uvicorn.workers.UvicornWorker: 指定 Gunicorn 使用 Uvicorn 的工作進(jìn)程類,以便支持 asyncio。
  • --bind 0.0.0.0:8000: 綁定到 0.0.0.0,意味著服務(wù)器將監(jiān)聽所有可用的 IP 地址上的 8000 端口,從而允許外網(wǎng)訪問。

使用Systemd 持久化服務(wù)

gunicorn 命令, 一旦關(guān)閉SSH連接,服務(wù)就會(huì)中斷。為了讓我們的API服務(wù)能在后臺(tái)長期運(yùn)行,并在服務(wù)器重啟后能自動(dòng)啟動(dòng),需要使用systemd --- Linux系統(tǒng)標(biāo)準(zhǔn)服務(wù)器管理。

(1)創(chuàng)建systemd 服務(wù)文件

sudo nano /etc/systemd/system/ner_api.service

(2) 編寫服務(wù)配置

[Unit]
Description=NER API Service
After=network.target

[Service]
User=root
Group=root
# 工作目錄
WorkingDirectory=/root/ner_deployment
# 啟動(dòng)服務(wù)的完整命令,務(wù)必使用gunicorn絕對路徑
ExecStart=/root/ner_deployment/.venv/bin/gunicorn -w 3 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:8000
# 失敗自動(dòng)重啟
Restart=on-failure
RestartSec=5s
# 定義服務(wù)安裝信息,表示服務(wù)器以多用戶模式啟動(dòng)時(shí)啟用
[Install]
WantedBy=multi-user.target

(3)管理服務(wù)

  • 重新加載systemd配置:
sudo systemctl daemon-reload
  • 啟動(dòng)服務(wù)
sudo systemctl start ner_api
  • 設(shè)置開機(jī)自啟
sudo systemctl enable ner_api
  • 查看服務(wù)狀態(tài)
sudo systemctl status ner_api
  • 查看實(shí)時(shí)日志
sudo journalctl -u ner_api -f

五、DockerCompose部署

5.1 Docker與DockerCompose

Docker 讓軟件變成集裝箱模式,允許開發(fā)者將應(yīng)用及其所有依賴項(xiàng)打包到一個(gè)輕量級、可抑制的容器中。

這個(gè)容器可以再任何安裝了Docker的機(jī)器上運(yùn)行。

DockerCompose 是通過定義和運(yùn)行多容器Docker應(yīng)用的工具,通過docker-compose.yaml的YAML文件,可以配置應(yīng)用所需的所有服務(wù)。
docker compose up -> 啟動(dòng)了所有服務(wù)

5.2 NER 服務(wù)的鏡像相關(guān)文件

Dockerfile

# ner_deployment/Dockerfile

# 基礎(chǔ)鏡像
FROM python:3.10-slim

#  關(guān)閉python輸出緩沖、禁止生成.pyc文件,讓容器中的日志更適合調(diào)試、鏡像內(nèi)容更簡潔
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# 容器內(nèi)榮做目錄
WORKDIR /app

# 設(shè)置 PyPI 鏡像源,加速依賴安裝
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
RUN pip install --no-cache-dir uv

# 文件復(fù)制到鏡像
COPY pyproject.toml ./

RUN uv pip install --system --no-cache .

COPY . .

# 用戶
RUN useradd -m appuser
USER appuser

# 暴露端口
EXPOSE 8000

CMD ["gunicorn", "-w", "3", "-k", "uvicorn.workers.UvicornWorker", "main:app", "--bind", "0.0.0.0:8000"]

docker-compose.yml

# ner_deployment/docker-compose.yml

services:
  ner_api:
    build: .
    container_name: ner_api_service
    ports:
      - "8000:8000"
    restart: always

5.3 部署與測試

啟動(dòng)

sudo docker compose up --build -d

查看日志

# 查看正在運(yùn)行的容器
sudo docker compose ps

# 實(shí)時(shí)查看服務(wù)日志
sudo docker compose logs -f

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容