MGeo开源镜像教程:Docker Compose编排ModelScope+Gradio+Redis缓存架构
MGeo开源镜像教程:Docker Compose编排ModelScope+Gradio+Redis缓存架构
1. 引言
你有没有遇到过这样的场景?用户提交了一个地址“北京市海淀区中关村大街27号”,你的系统需要自动把它拆解成“省:北京市”、“市:北京市”、“区:海淀区”、“街道:中关村大街”、“门牌号:27号”。或者反过来,给你一堆零散的地址要素,需要组合成一个标准、完整的地址文本。
这就是地址结构化解析要做的事情。听起来简单,做起来却不容易。中文地址的表达太灵活了,“中关村大街27号”和“27号中关村大街”说的可能是同一个地方。传统基于规则的方法,面对这种多样性往往力不从心。
今天要介绍的MGeo模型,就是专门为解决这类问题而生的。它来自达摩院和高德的联合研究,是一个针对中文地址领域预训练的多模态模型。简单来说,它通过学习海量的地图数据和文本数据,能更“懂”地址。
但模型再好,也得能方便地用起来才行。这篇文章,我就带你手把手部署一个基于MGeo的地址解析服务。我们不用复杂的Kubernetes,就用最接地气的Docker Compose,把ModelScope模型服务、Gradio可视化界面,还有提升性能的Redis缓存,全部编排在一起。学完这篇教程,你就能在自己的服务器上,拥有一个随时可用的专业地址解析服务。
2. 环境准备与项目结构
在开始编排之前,我们先确保手头有趁手的工具,并理清整个项目的结构。
2.1 你需要准备什么
- 一台Linux服务器:Ubuntu 20.04/22.04或CentOS 7/8都可以。个人电脑用虚拟机或WSL2也行。
- Docker和Docker Compose:这是我们的核心工具。如果还没安装,用下面这几条命令搞定(以Ubuntu为例):
# 安装Docker sudo apt-get update sudo apt-get install docker.io -y sudo systemctl start docker sudo systemctl enable docker # 安装Docker Compose sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose # 验证安装 docker --version docker-compose --version - 基本的命令行操作知识:会
cd,ls,vim/nano编辑文件就够用了。 - 网络:服务器需要能正常访问互联网,以下载Docker镜像。
2.2 项目目录结构
我们先创建一个清晰的项目目录,所有文件都放在里面,管理起来方便。
mkdir mgeo-service && cd mgeo-service创建好的目录结构应该是这样的:
mgeo-service/ ├── docker-compose.yml # Docker Compose编排文件(核心) ├── model-server/ # ModelScope模型服务相关 │ ├── Dockerfile # 构建模型服务镜像 │ └── app.py # 模型推理API ├── gradio-web/ # Gradio Web界面相关 │ ├── Dockerfile # 构建Web界面镜像 │ └── webui.py # Gradio应用主文件(从镜像中复制或挂载) ├── redis/ # Redis配置(可选) │ └── redis.conf └── .env # 环境变量配置文件(可选,用于管理端口、密码等)别担心,这些文件我们接下来会一个一个创建。这个结构的好处是逻辑清晰,每个服务独立,以后修改或升级都很方便。
3. 核心服务配置与编排
现在,我们来搭建三个核心服务:模型推理服务、Web界面和缓存。
3.1 模型推理服务 (Model Server)
模型服务是大脑,负责加载MGeo模型并执行地址解析的推理计算。我们把它封装在一个独立的Docker容器里。
首先,创建model-server目录和Dockerfile:
mkdir -p model-server cd model-server文件:model-server/Dockerfile
# 使用一个包含Python和常用深度学习库的基础镜像 FROM registry.cn-hangzhou.aliyuncs.com/modelscope-repo/modelscope:ubuntu20.04-py38-torch1.11.0-cu113-1.6.1 # 设置工作目录 WORKDIR /app # 复制模型推理代码到容器内 COPY app.py . # 安装额外的依赖(如果需要,例如用于API的fastapi/uvicorn) # RUN pip install fastapi uvicorn -i https://pypi.tuna.tsinghua.edu.cn/simple # 本例中我们使用ModelScope内置的pipeline,通过HTTP服务暴露,这里先简单化。 # 声明服务端口(与后续compose文件对应) EXPOSE 8000 # 启动命令:这里我们用一个简单的HTTP服务器来提供模型调用 # 更生产化的做法是使用FastAPI,但为简化,我们先直接运行模型加载脚本。 # 实际启动命令将在app.py中实现,这里先占位。 CMD ["python", "app.py"]接下来是核心的模型推理脚本app.py。我们使用ModelScope的pipeline来轻松加载MGeo模型。
文件:model-server/app.py
#!/usr/bin/env python # -*- coding: utf-8 -*- """ MGeo 模型推理服务 提供HTTP API接口,接收文本,返回地址结构化解析结果。 """ from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasks from http.server import HTTPServer, BaseHTTPRequestHandler import json import logging # 配置日志 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # 全局加载模型pipeline # 注意:首次运行会从ModelScope下载模型,需要一定时间和网络 logger.info("正在加载 MGeo 模型...") try: # 使用达摩院开源的 MGeo 模型 model_id = 'damo/mgeo_backbone_chinese_base' pipe = pipeline(Tasks.address_parsing, model=model_id) logger.info("MGeo 模型加载成功!") except Exception as e: logger.error(f"模型加载失败: {e}") exit(1) class ModelRequestHandler(BaseHTTPRequestHandler): """处理模型推理请求的Handler""" def do_POST(self): """处理POST请求,路径为 /parse""" if self.path != '/parse': self.send_error(404) return content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) try: data = json.loads(post_data.decode('utf-8')) text = data.get('text', '') if not text: raise ValueError("请求中未提供 'text' 字段") # 调用模型进行推理 result = pipe(text) logger.info(f"解析成功: 输入='{text}', 输出={result}") # 构建响应 response = { 'code': 0, 'msg': 'success', 'data': result } response_body = json.dumps(response, ensure_ascii=False).encode('utf-8') self.send_response(200) self.send_header('Content-Type', 'application/json; charset=utf-8') self.send_header('Content-Length', str(len(response_body))) self.end_headers() self.wfile.write(response_body) except Exception as e: logger.error(f"请求处理失败: {e}") error_response = json.dumps({'code': -1, 'msg': str(e)}).encode('utf-8') self.send_response(500) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(error_response))) self.end_headers() self.wfile.write(error_response) def do_GET(self): """健康检查端点""" if self.path == '/health': self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps({'status': 'healthy'}).encode('utf-8')) else: self.send_error(404) def run_server(port=8000): """启动HTTP服务器""" server_address = ('', port) httpd = HTTPServer(server_address, ModelRequestHandler) logger.info(f'模型推理服务启动,监听端口 {port}...') httpd.serve_forever() if __name__ == '__main__': run_server()这个脚本做了几件事:
- 加载指定的MGeo模型(
damo/mgeo_backbone_chinese_base)。 - 启动一个简单的HTTP服务器,监听
8000端口。 - 提供
/parse接口接收JSON格式的地址文本,返回结构化结果。 - 提供
/health接口用于健康检查。
3.2 Web界面服务 (Gradio)
模型服务有了,我们还需要一个友好的界面让用户能方便地输入和查看结果。Gradio非常适合快速构建机器学习演示界面。
创建gradio-web目录和文件:
cd .. mkdir -p gradio-web cd gradio-web文件:gradio-web/Dockerfile
# 使用轻量级的Python镜像 FROM python:3.9-slim WORKDIR /app # 复制Web界面代码 COPY webui.py . COPY requirements.txt . # 安装依赖 RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 暴露Gradio默认端口 EXPOSE 7860 # 启动Gradio应用 CMD ["python", "webui.py"]文件:gradio-web/requirements.txt
gradio>=3.50.0 requests redis现在来创建核心的Web界面文件webui.py。这里我们会集成Redis缓存,避免重复解析相同地址,提升响应速度。
文件:gradio-web/webui.py
#!/usr/bin/env python # -*- coding: utf-8 -*- """ MGeo 地址解析 Web UI 使用Gradio构建,并集成Redis缓存。 """ import gradio as gr import requests import json import redis import os import logging # 配置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # 从环境变量读取配置,方便Docker Compose管理 MODEL_SERVER_URL = os.getenv('MODEL_SERVER_URL', 'http://model-server:8000') REDIS_HOST = os.getenv('REDIS_HOST', 'redis') REDIS_PORT = int(os.getenv('REDIS_PORT', 6379)) REDIS_DB = int(os.getenv('REDIS_DB', 0)) # 生产环境建议设置密码 REDIS_PASSWORD # 初始化Redis连接 try: # 如果无密码:r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB) r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, decode_responses=True) r.ping() logger.info("Redis连接成功") except redis.ConnectionError as e: logger.warning(f"无法连接Redis,将不使用缓存: {e}") r = None def parse_address_with_cache(address_text): """ 解析地址,优先从Redis缓存中获取结果。 """ if not address_text.strip(): return {"error": "地址文本不能为空"} cache_key = f"mgeo:parse:{address_text}" result = None # 1. 尝试从缓存读取 if r: cached_result = r.get(cache_key) if cached_result: logger.info(f"缓存命中: {address_text}") return json.loads(cached_result) # 2. 缓存未命中,调用模型服务 try: payload = {'text': address_text} response = requests.post(f'{MODEL_SERVER_URL}/parse', json=payload, timeout=30) response.raise_for_status() result = response.json() # 3. 将成功结果存入缓存,设置过期时间(例如1小时) if r and result.get('code') == 0: r.setex(cache_key, 3600, json.dumps(result, ensure_ascii=False)) logger.info(f"结果已缓存: {address_text}") return result except requests.exceptions.RequestException as e: logger.error(f"调用模型服务失败: {e}") return {"code": -1, "msg": f"服务调用失败: {e}", "data": None} except json.JSONDecodeError as e: logger.error(f"解析响应失败: {e}") return {"code": -1, "msg": "响应格式错误", "data": None} def format_result(result): """将模型返回的结果格式化为易读的字符串""" if not isinstance(result, dict): return str(result) if result.get('code') != 0: return f"错误: {result.get('msg')}" data = result.get('data', {}) if not data: return "解析结果为空" # 假设MGeo返回的结构包含‘output’字段,其中是解析出的实体列表 # 实际格式需根据模型输出调整 output = data.get('output', []) formatted_lines = [] for entity in output: # 示例: entity = {'type': 'province', 'span': '北京市', 'start': 0, 'end': 3} e_type = entity.get('type', '未知') e_span = entity.get('span', '') formatted_lines.append(f"{e_type}: {e_span}") return "\n".join(formatted_lines) if formatted_lines else "未识别到结构化要素" # 构建Gradio界面 demo = gr.Blocks(title="MGeo 地址结构化解析服务") with demo: gr.Markdown(""" # 🗺️ MGeo 地址结构化解析服务 输入一段包含地址的文本,模型将自动识别并提取其中的结构化要素(如省、市、区、街道、门牌号等)。 """) with gr.Row(): with gr.Column(scale=2): input_text = gr.Textbox( label="请输入地址文本", placeholder="例如:北京市海淀区中关村大街27号", lines=3, value="帮我查一下北京市海淀区中关村大街27号附近有什么好吃的。" ) with gr.Row(): submit_btn = gr.Button("提交解析", variant="primary") clear_btn = gr.Button("清空") gr.Examples( examples=[ ["上海市浦东新区张江高科技园区祖冲之路899号"], ["广东省深圳市南山区深南大道10000号腾讯大厦"], ["收货地址:浙江省杭州市余杭区文一西路969号阿里巴巴西溪园区"], ["我要去广州市天河区天河路208号天河城购物中心"] ], inputs=input_text, label="示例地址(点击试试)" ) with gr.Column(scale=3): output_json = gr.JSON(label="原始解析结果(JSON)") output_text = gr.Textbox(label="格式化结果", lines=10, interactive=False) # 按钮事件绑定 def parse_and_display(text): result = parse_address_with_cache(text) formatted = format_result(result) return result, formatted submit_btn.click( fn=parse_and_display, inputs=input_text, outputs=[output_json, output_text] ) clear_btn.click( fn=lambda: ("", None, ""), inputs=[], outputs=[input_text, output_json, output_text] ) gr.Markdown(""" --- **说明**: - 服务集成了Redis缓存,重复查询相同地址会更快。 - 首次加载模型或查询新地址可能需要几秒钟。 - 模型支持多种地址要素识别,具体类型见原始JSON结果。 """) if __name__ == "__main__": # 获取端口,方便在Compose中覆盖 server_port = int(os.getenv('GRADIO_SERVER_PORT', 7860)) # 允许被其他容器访问 demo.launch(server_name="0.0.0.0", server_port=server_port, share=False)这个Web界面提供了输入框、示例、提交按钮,并展示原始JSON和格式化后的结果。关键点在于parse_address_with_cache函数,它实现了先查Redis缓存,没有再调模型服务的逻辑。
3.3 缓存服务 (Redis)
Redis作为内存数据库,用来缓存解析结果非常合适,能极大减轻模型服务的压力,提升响应速度。
创建Redis配置文件目录(使用默认配置也可):
cd .. mkdir -p redis文件:redis/redis.conf(可选,用于自定义配置)
# 绑定所有网络接口,允许其他容器连接 bind 0.0.0.0 # 保护模式关闭,因为我们通常在容器内网使用 protected-mode no # 设置密码(建议在生产环境设置) # requirepass your_strong_password_here # 启用AOF持久化(可选) appendonly yes3.4 使用Docker Compose编排一切
好了,三个核心组件都准备好了。现在,我们用docker-compose.yml这个“乐高说明书”,把它们拼装成一个能协同工作的整体。
回到项目根目录,创建docker-compose.yml:
cd .. vim docker-compose.yml文件:docker-compose.yml
version: '3.8' services: # 1. Redis 缓存服务 redis: image: redis:7-alpine container_name: mgeo-redis restart: unless-stopped ports: - "6379:6379" # 主机端口:容器端口,可按需修改主机端口 volumes: - ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro - redis-data:/data command: redis-server /usr/local/etc/redis/redis.conf networks: - mgeo-network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 # 2. ModelScope 模型推理服务 model-server: build: ./model-server container_name: mgeo-model-server restart: unless-stopped # ports: # 通常不需要直接暴露给主机,仅供内部网络访问 # - "8000:8000" volumes: # 可选:挂载模型缓存目录,避免每次重启重新下载 - model-cache:/root/.cache/modelscope networks: - mgeo-network depends_on: redis: condition: service_healthy environment: - PYTHONUNBUFFERED=1 # 模型加载可能较慢,给予更长的启动超时时间 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 20s retries: 5 start_period: 60s # 给模型加载留出时间 # 3. Gradio Web 界面服务 gradio-web: build: ./gradio-web container_name: mgeo-gradio-web restart: unless-stopped ports: - "7860:7860" # 将Gradio的7860端口映射到主机 volumes: # 如果webui.py在开发中需要频繁修改,可以使用挂载,否则用构建的镜像即可 # - ./gradio-web/webui.py:/app/webui.py - ./gradio-web/requirements.txt:/app/requirements.txt networks: - mgeo-network depends_on: model-server: condition: service_healthy environment: - MODEL_SERVER_URL=http://model-server:8000 - REDIS_HOST=redis - REDIS_PORT=6379 - GRADIO_SERVER_PORT=7860 # - REDIS_PASSWORD=your_password # 如果Redis设置了密码 # 定义网络,让三个服务在同一个内部网络,通过服务名通信 networks: mgeo-network: driver: bridge # 定义数据卷,持久化Redis数据和模型缓存 volumes: redis-data: model-cache:这个编排文件定义了三个服务,它们通过名为mgeo-network的Docker网络互联。depends_on和healthcheck确保了服务按正确的顺序启动(Redis -> 模型服务 -> Web界面)。
4. 一键部署与验证
所有文件准备就绪,现在可以启动我们的服务了。
4.1 启动服务
在项目根目录(mgeo-service/)下,运行一条命令:
docker-compose up -d-d参数表示在后台运行。你会看到Docker开始拉取镜像、构建镜像、启动容器。首次运行需要下载Redis镜像、Python基础镜像,并构建两个自定义镜像,可能需要几分钟。
4.2 查看服务状态
使用以下命令检查服务是否正常启动:
# 查看所有容器状态 docker-compose ps # 查看实时日志(可以观察模型下载进度) docker-compose logs -f model-server当看到模型服务日志显示“MGeo 模型加载成功!”以及“模型推理服务启动,监听端口 8000...”时,说明模型已加载完毕。
4.3 访问与使用服务
- 打开Web界面:在浏览器中访问
http://你的服务器IP地址:7860。你会看到我们构建的Gradio界面。 - 测试功能:在输入框中输入一个地址文本,例如“北京市海淀区中关村大街27号”,点击“提交解析”。
- 查看结果:界面下方会显示模型返回的原始JSON数据,以及格式化后的结构化地址要素(如省、市、区等)。
4.4 服务管理常用命令
# 停止所有服务 docker-compose down # 停止并删除所有容器、网络、数据卷(谨慎使用,会清空Redis数据) docker-compose down -v # 重启服务 docker-compose restart # 查看服务日志 docker-compose logs [service-name] # 如 gradio-web # 进入某个容器的shell(用于调试) docker-compose exec model-server /bin/bash5. 总结
通过这篇教程,我们完成了一个从零到一的MGeo地址解析服务部署。我们不仅启动了模型,还为其搭配了一个友好的Web界面和提升性能的Redis缓存,并且所有组件都通过Docker Compose实现了编排,管理起来非常方便。
回顾一下我们搭建的架构:
- 模型服务 (
model-server):承载核心的MGeo模型,提供HTTP API。 - Web界面 (
gradio-web):提供交互式界面,并集成了缓存逻辑。 - 缓存服务 (
redis):存储解析结果,加速重复请求。
这个方案的优势在于:
- 一体化:一条命令即可启动完整服务栈。
- 易扩展:如果想升级模型、修改界面或更换缓存策略,只需修改对应目录的文件,然后
docker-compose build和up即可。 - 便于移植:整个
mgeo-service文件夹可以轻松复制到任何支持Docker的机器上运行。 - 性能提升:Redis缓存能有效应对相同地址的频繁查询。
你可以在此基础上继续深化,比如为模型服务换上性能更好的FastAPI框架,为Redis设置密码,或者使用Nginx做反向代理和负载均衡。希望这个实战项目能帮你快速将优秀的AI模型转化为可用的服务。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
