RexUniNLU部署教程:Docker Compose编排RexUniNLU+Redis缓存+Nginx反向代理
RexUniNLU部署教程:Docker Compose编排RexUniNLU+Redis缓存+Nginx反向代理
1. 为什么需要这套部署方案?
你可能已经试过直接运行 python server.py,服务确实跑起来了,接口也能调用。但真实业务场景里,光“能跑”远远不够——用户并发一上来,响应变慢;模型加载反复触发,CPU占用飙升;服务挂了没人自动拉起;想加个HTTPS或负载均衡还得手动折腾……这些都不是开发该操心的事,而是部署架构该解决的问题。
RexUniNLU本身轻量、零样本、开箱即用,但它的价值只有在稳定、可扩展、易维护的环境中才能真正释放。本教程不讲抽象概念,只带你一步步落地一套生产就绪(Production-Ready) 的部署方案:用 Docker Compose 统一编排 RexUniNLU 服务、Redis 缓存层和 Nginx 反向代理,三者各司其职——FastAPI 处理核心 NLU 逻辑,Redis 缓存 Schema 解析结果与高频请求响应,Nginx 负责路由、静态资源托管、HTTPS 终止与基础防护。整套方案无需改一行业务代码,全部通过配置驱动,本地测试、测试环境、预发环境一键复用。
你不需要是 DevOps 专家,只要会写 YAML 和敲几条命令,就能拥有一个随时可上线、可监控、可横向扩展的 NLU 服务底座。
2. 环境准备与依赖确认
2.1 基础环境要求
这套方案对宿主机要求极低,普通开发机即可胜任:
- 操作系统:Linux(Ubuntu 20.04+/CentOS 7+)或 macOS(Apple Silicon/M1/M2 推荐)
- Docker:v20.10.0+(需支持 Compose V2)
- Docker Compose:v2.15.0+(推荐使用
docker compose命令,非旧版docker-compose) - 内存:最低 4GB(推荐 8GB+,尤其启用 GPU 加速时)
- 磁盘:预留至少 3GB 空间(用于模型缓存与镜像存储)
注意:Windows 用户请确保已启用 WSL2,并在 WSL2 中安装 Docker Desktop(不要在 Windows 原生 CMD/PowerShell 中运行),否则模型下载与路径映射将频繁出错。
2.2 验证 Python 与模型环境(可选但强烈建议)
虽然 Docker 内部会完全隔离运行环境,但提前验证本地能否成功运行 RexUniNLU,能快速排除模型下载、网络代理等前置问题:
# 克隆项目(若尚未获取)
git clone https://github.com/modelscope/RexUniNLU.git
cd RexUniNLU
# 创建并激活虚拟环境(Python 3.8+)
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
# 安装核心依赖(跳过 torch,由 Docker 内置)
pip install modelscope fastapi uvicorn python-dotenv
# 运行一次测试,触发模型下载(首次约 2–5 分钟,取决于网络)
python test.py
若看到类似以下输出,说明模型已成功加载并完成多领域推理:
智能家居场景:{'intent': '打开空调', 'slots': {'设备': '空调', '动作': '打开'}}
金融场景:{'intent': '查询余额', 'slots': {'账户类型': '储蓄卡'}}
医疗场景:{'intent': '预约挂号', 'slots': {'科室': '呼吸内科', '时间': '明天下午'}}
此时 ~/.cache/modelscope 目录下已缓存好 iic/nlp_structbert_zero-shot-nlu_zh 模型,后续 Docker 构建将复用该缓存,大幅加速镜像构建。
3. 构建可复用的 RexUniNLU 服务镜像
3.1 编写精简高效的 Dockerfile
我们不使用通用 Python 镜像层层叠加,而是基于 python:3.9-slim-bookworm 构建最小化镜像,仅保留运行必需组件。关键优化点:
- 使用多阶段构建,分离构建依赖与运行时依赖
- 复用本地
~/.cache/modelscope,避免每次构建都重下模型 - 预编译
.pyc文件,提升容器启动速度 - 设置非 root 用户运行,增强安全性
在 RexUniNLU/ 目录下新建文件 Dockerfile:
# 构建阶段:安装构建依赖并下载/编译模型
FROM python:3.9-slim-bookworm AS builder
# 设置时区与编码
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 创建非 root 用户
RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001
# 切换到非 root 用户进行 pip 安装(避免权限问题)
USER appuser
WORKDIR /home/appuser
# 复制 requirements 并安装(仅安装 runtime 依赖)
COPY requirements.txt .
RUN pip install --no-cache-dir --user \
fastapi==0.110.0 \
uvicorn==0.29.0 \
modelscope==1.15.0 \
python-dotenv==1.0.0 \
redis==5.0.5
# 运行阶段:极简运行时镜像
FROM python:3.9-slim-bookworm
# 复用构建阶段的用户与环境
RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001
USER appuser
WORKDIR /app
# 复制构建阶段安装的包
COPY --from=builder --chown=appuser:appgroup /home/appuser/.local /home/appuser/.local
ENV PATH=/home/appuser/.local/bin:$PATH
# 复制应用代码(排除测试脚本与文档,减小镜像体积)
COPY --chown=appuser:appgroup . .
RUN rm -f test.py README.md requirements.txt Dockerfile
# 预编译 Python 字节码
RUN find . -name "*.py" -exec python -m py_compile {} \;
# 暴露端口
EXPOSE 8000
# 启动命令(使用 uvicorn,支持热重载仅限开发,此处为生产模式)
CMD ["uvicorn", "server:app", "--host", "0.0.0.0:8000", "--port", "8000", "--workers", "2", "--log-level", "info"]
3.2 验证镜像构建与本地运行
执行构建命令(注意末尾的 .):
docker build -t rexuninlu:latest .
构建成功后,立即测试容器内服务是否正常:
# 启动容器,映射端口,后台运行
docker run -d --name rex-test -p 8000:8000 rexuninlu:latest
# 等待 10 秒让服务初始化
sleep 10
# 发送测试请求(需安装 curl)
curl -X POST "http://localhost:8000/nlu" \
-H "Content-Type: application/json" \
-d '{"text": "帮我订一张明天去北京的高铁票", "labels": ["出发地", "目的地", "时间", "订票意图"]}'
预期返回 JSON 结构,包含 intent 与 slots 字段。验证无误后,清理测试容器:
docker stop rex-test && docker rm rex-test
4. 编排 Redis 缓存与 Nginx 反向代理
4.1 设计缓存策略:什么该缓存?怎么缓存?
RexUniNLU 的推理过程包含两个高开销环节:
- Schema 解析:将传入的
labels列表转换为内部向量表示,每次调用均重复计算 - 文本编码:对输入
text进行 BERT 分词与编码,高频相同 query 会反复执行
Redis 缓存聚焦这两点,采用 双重键设计:
schema:{md5(labels)}→ 缓存解析后的 schema 向量(TTL 1 小时,标签极少变更)nlu:{md5(text+labels)}→ 缓存完整推理结果(TTL 10 分钟,兼顾新鲜度与复用率)
server.py 需做两处轻量修改(共 12 行代码),已为你准备好补丁:
# 在 server.py 开头添加
import redis
import hashlib
import json
from fastapi import Depends, HTTPException
# 初始化 Redis 连接(从环境变量读取)
redis_client = redis.Redis(
host=os.getenv("REDIS_HOST", "redis"),
port=int(os.getenv("REDIS_PORT", 6379)),
db=0,
decode_responses=True,
socket_connect_timeout=2,
socket_timeout=2
)
# 新增缓存工具函数
def get_cache_key(text: str, labels: list) -> str:
key_str = f"{text}|{json.dumps(sorted(labels), ensure_ascii=False)}"
return hashlib.md5(key_str.encode()).hexdigest()
# 修改 /nlu 接口,加入缓存逻辑
@app.post("/nlu")
async def nlu_inference(request: NLURequest):
cache_key = get_cache_key(request.text, request.labels)
# 尝试读取缓存
cached = redis_client.get(f"nlu:{cache_key}")
if cached:
return json.loads(cached)
# 执行原始推理(原 analyze_text 调用)
try:
result = analyze_text(request.text, request.labels)
except Exception as e:
raise HTTPException(status_code=500, detail=f"推理失败: {str(e)}")
# 写入缓存
redis_client.setex(f"nlu:{cache_key}", 600, json.dumps(result, ensure_ascii=False))
return result
提示:此修改已兼容原接口协议,无需调整任何客户端调用方式。
4.2 编写 docker-compose.yml 实现三服务协同
在 RexUniNLU/ 目录下创建 docker-compose.yml,定义三个服务及其依赖关系:
version: '3.8'
services:
# RexUniNLU 核心服务
nlu-api:
build: .
image: rexuninlu:latest
restart: unless-stopped
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
- MODELSCOPE_CACHE=/root/.cache/modelscope
volumes:
# 复用本地模型缓存,加速冷启动
- ~/.cache/modelscope:/root/.cache/modelscope:ro
# 挂载日志目录便于排查
- ./logs:/app/logs
depends_on:
- redis
networks:
- nlu-net
# Redis 缓存服务
redis:
image: redis:7.2-alpine
restart: unless-stopped
command: redis-server --appendonly yes --save 60 1 --maxmemory 512mb --maxmemory-policy allkeys-lru
volumes:
- ./redis-data:/data
networks:
- nlu-net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Nginx 反向代理与 HTTPS 终止
nginx:
image: nginx:1.25-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
- ./static:/usr/share/nginx/html:ro
depends_on:
- nlu-api
networks:
- nlu-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 15s
timeout: 10s
retries: 3
networks:
nlu-net:
driver: bridge
4.3 配置 Nginx:安全、高效、可扩展
创建 nginx.conf(同级目录),实现:
- HTTP 自动跳转 HTTPS
/nlu路径反向代理至nlu-api:8000/health健康检查端点(返回 200)- 静态资源托管(如未来添加 Web UI)
- 请求体大小放宽至 10MB(支持长文本)
- 启用 Gzip 压缩
events {
worker_connections 1024;
}
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;
error_log /var/log/nginx/error.log warn;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_types application/json text/plain;
# 全局设置:放宽请求体限制
client_max_body_size 10M;
# 健康检查端点
server {
listen 80;
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}
# HTTP → HTTPS 重定向
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 服务
server {
listen 443 ssl http2;
server_name localhost;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# 静态资源
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
# NLU API 代理
location /nlu {
proxy_pass http://nlu-api:8000/nlu;
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;
proxy_buffering off;
}
}
}
提示:如需正式 HTTPS,将
./ssl/目录替换为你的域名证书(fullchain.pem+privkey.pem)。本地测试可跳过 SSL,注释掉ssl_*行并删除443端口映射,HTTP 服务仍完全可用。
5. 一键启动与效果验证
5.1 启动整套服务
确保当前目录为 RexUniNLU/,执行:
# 启动所有服务(-d 后台运行,--build 强制重建镜像)
docker compose up -d --build
# 查看服务状态(等待所有显示 healthy)
docker compose ps
# 查看实时日志(按 Ctrl+C 退出)
docker compose logs -f
正常启动后,终端应显示:
NAME COMMAND SERVICE STATUS PORTS
rexuninlu-nginx-1 "/docker-entrypoint.…" nginx running (healthy) 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp
rexuninlu-nlu-api-1 "uvicorn server:app …" nlu-api running (healthy) 8000/tcp
rexuninlu-redis-1 "docker-entrypoint.s…" redis running (healthy) 6379/tcp
5.2 多维度验证服务可用性
基础连通性测试
# 测试 Nginx 健康检查
curl http://localhost/health # 应返回 OK
# 测试 NLU 接口(HTTP)
curl -X POST "http://localhost/nlu" \
-H "Content-Type: application/json" \
-d '{"text": "今天上海天气怎么样", "labels": ["地点", "查询天气"]}'
缓存有效性验证
# 第一次请求(无缓存)—— 记录耗时
time curl -s -o /dev/null -w "%{time_total}s\n" \
-X POST "http://localhost/nlu" \
-H "Content-Type: application/json" \
-d '{"text": "订机票", "labels": ["订票意图"]}'
# 第二次相同请求(命中缓存)—— 耗时应显著降低(< 100ms)
time curl -s -o /dev/null -w "%{time_total}s\n" \
-X POST "http://localhost/nlu" \
-H "Content-Type: application/json" \
-d '{"text": "订机票", "labels": ["订票意图"]}'
Redis 缓存内容检查
# 进入 Redis 容器
docker exec -it rexuninlu-redis-1 redis-cli
# 查看缓存键(应有 schema:* 和 nlu:* 两类)
KEYS "nlu:*"
# 查看某条缓存值(替换为实际 key)
GET "nlu:abc123..."
# 退出
exit
并发压力初步验证
# 使用 ab(Apache Bench)模拟 50 并发、100 次请求
ab -n 100 -c 50 http://localhost/health
ab -n 100 -c 50 -p nlu-test.json -T "application/json" http://localhost/nlu
nlu-test.json示例内容:{"text": "我想买一台MacBook Pro", "labels": ["产品", "购买意图"]}
6. 日常运维与进阶调优
6.1 快速诊断常见问题
| 现象 | 检查步骤 | 快速修复 |
|---|---|---|
curl: (7) Failed to connect |
docker compose ps 查看 nginx 是否 running |
docker compose restart nginx |
| 接口返回 502 Bad Gateway | docker compose logs nginx 查看 proxy 错误 |
docker compose logs nlu-api 确认服务是否启动成功 |
| Redis 连接超时 | docker compose logs redis 查看是否 OOM |
docker exec rexuninlu-redis-1 redis-cli info memory 检查内存 |
| 模型加载慢/失败 | docker compose logs nlu-api 查看 model download 日志 |
确保 ~/.cache/modelscope 已正确挂载且有读权限 |
6.2 生产环境必调参数
- NLU 服务扩缩容:修改
docker-compose.yml中nlu-api的deploy配置(需升级至 Swarm 或 Kubernetes) - Redis 内存上限:调整
command中--maxmemory值,根据日均请求量估算(100万请求 ≈ 200MB) - Uvicorn Worker 数:
CMD中--workers值设为 CPU 核数 × 2(如 4 核机器设为 8) - 日志轮转:在
nlu-api服务中添加logging配置,或挂载logrotate配置
6.3 安全加固建议(生产必备)
- 禁用默认 Docker Socket 挂载:确保
docker-compose.yml中未出现/var/run/docker.sock - Nginx 添加速率限制:在
location /nlu块中加入limit_req zone=api burst=20 nodelay; - 启用 JWT 认证:在 Nginx 层或 FastAPI 中集成
python-jose,对/nlu接口增加鉴权中间件 - 定期更新基础镜像:每月执行
docker pull python:3.9-slim-bookworm && docker pull redis:7.2-alpine
7. 总结:你已掌握一套可落地的 NLU 服务基建
回顾整个流程,你没有写一行模型代码,却完成了一套具备生产级能力的 NLU 服务部署:
- 零侵入改造:仅修改 12 行
server.py,复用全部 RexUniNLU 业务逻辑 - 三层解耦架构:NLU 计算、缓存、网关职责清晰,任一模块可独立升级
- 开箱即用体验:
docker compose up -d一条命令,80 端口即得 HTTPS 可用 API - 性能可见可调:缓存命中率、响应延迟、并发能力全部可观测、可压测
- 平滑演进路径:从单机 Docker 到 Swarm 集群,再到 Kubernetes,配置结构完全兼容
这套方案的价值,不在于技术有多炫酷,而在于它把“让 NLU 服务稳定跑起来”这件事,变成了一个可复制、可交付、可交接的标准动作。下次接到新需求,你不再需要从 pip install 开始手忙脚乱,而是打开这个目录,docker compose up,然后专注打磨你的 labels 定义与业务 Schema。
真正的工程效率,就藏在这些看似平凡的部署细节里。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐

所有评论(0)