RexUniNLU部署教程:Supervisor自启+日志管理+异常恢复完整方案
1. 引言:为什么需要完整的部署方案?
如果你用过一些AI模型,可能遇到过这样的烦恼:服务器重启后,服务没自动起来,得手动敲命令;想看个日志,得翻半天文件;服务偶尔崩溃,还得人工盯着。这些问题在RexUniNLU这种生产级模型上尤其让人头疼。
RexUniNLU是阿里巴巴达摩院开发的零样本通用自然语言理解模型,它能干很多事——从文本里抽人名地名、给文章分类、分析情感,甚至理解事件关系,而且不用你准备训练数据,给个任务描述就能干活。但这么好的工具,如果部署得磕磕绊绊,用起来就太可惜了。
今天这篇教程,我要带你解决的就是这些“部署后”的问题。我们不只讲怎么把服务跑起来,更要讲怎么让它稳定地跑、自动地跑、方便地监控。我会手把手教你用Supervisor做自启动、配置专业的日志管理、搭建异常恢复机制,让你部署一次,就能安心用很久。
学完这篇,你能得到:
- 一个重启后自动恢复的RexUniNLU服务
- 清晰的日志记录,问题排查不再抓瞎
- 服务异常时的自动恢复能力
- 整套可复用的部署管理方案
2. 环境准备与快速部署
2.1 系统要求检查
在开始之前,我们先确认一下环境是否合适。RexUniNLU对GPU有要求,但如果你只有CPU也能跑,只是速度会慢一些。
打开终端,运行这几个命令看看:
# 检查Python版本(需要3.8+) python3 --version # 检查CUDA是否可用(如果有GPU) python3 -c "import torch; print('CUDA可用:', torch.cuda.is_available())" # 检查内存和磁盘空间 free -h df -h如果看到Python版本是3.8以上,CUDA显示可用(或者你打算用CPU模式),磁盘有至少5GB空闲空间,那就可以继续了。
2.2 一键部署脚本
我准备了一个完整的部署脚本,你只需要复制粘贴就能完成基础安装。新建一个文件叫deploy_rexuninlu.sh:
#!/bin/bash echo "开始部署RexUniNLU..." # 创建项目目录 mkdir -p /root/workspace/rex-uninlu cd /root/workspace/rex-uninlu # 安装必要的系统依赖 apt-get update apt-get install -y supervisor nginx # 创建Python虚拟环境 python3 -m venv venv source venv/bin/activate # 安装Python依赖 pip install --upgrade pip pip install modelscope torch torchvision torchaudio pip install gradio transformers # 下载模型(这里用ModelScope的方式) python3 -c " from modelscope import snapshot_download model_dir = snapshot_download('iic/nlp_deberta_rex-uninlu_chinese-base') print(f'模型下载完成,路径: {model_dir}') " echo "基础部署完成!"给脚本执行权限并运行:
chmod +x deploy_rexuninlu.sh ./deploy_rexuninlu.sh这个过程可能会花点时间,主要是在下载模型(大概400MB)。你可以去泡杯茶,回来应该就好了。
3. 核心服务配置与启动
3.1 编写RexUniNLU服务脚本
模型下载好了,现在我们要写一个Python脚本来启动Web服务。这个脚本会用Gradio做一个简单的界面,让你能通过网页调用模型。
创建文件/root/workspace/rex-uninlu/app.py:
import gradio as gr from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasks import json import logging import sys # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('/root/workspace/rex-uninlu.log'), logging.StreamHandler(sys.stdout) ] ) logger = logging.getLogger(__name__) # 加载模型 logger.info("开始加载RexUniNLU模型...") try: uninlu_pipeline = pipeline( task=Tasks.zero_shot_nlu, model='iic/nlp_deberta_rex-uninlu_chinese-base' ) logger.info("模型加载成功!") except Exception as e: logger.error(f"模型加载失败: {e}") raise def ner_inference(text, schema_str): """命名实体识别""" try: schema = json.loads(schema_str) result = uninlu_pipeline(text, schema) return json.dumps(result, ensure_ascii=False, indent=2) except Exception as e: logger.error(f"NER推理错误: {e}") return f"错误: {str(e)}" def text_classification(text, schema_str): """文本分类""" try: schema = json.loads(schema_str) result = uninlu_pipeline(text, schema) return json.dumps(result, ensure_ascii=False, indent=2) except Exception as e: logger.error(f"文本分类错误: {e}") return f"错误: {str(e)}" # 创建Gradio界面 with gr.Blocks(title="RexUniNLU零样本NLU工具") as demo: gr.Markdown("# RexUniNLU零样本通用自然语言理解") gr.Markdown("支持命名实体识别、文本分类等10+种NLU任务,无需训练数据") with gr.Tab("命名实体识别(NER)"): with gr.Row(): with gr.Column(): ner_text = gr.Textbox( label="输入文本", value="1944年毕业于北大的名古屋铁道会长谷口清太郎等人在日本积极筹资,共筹款2.7亿日元。", lines=5 ) ner_schema = gr.Textbox( label="Schema定义", value='{"人物": null, "地理位置": null, "组织机构": null}', lines=3 ) ner_btn = gr.Button("抽取实体") with gr.Column(): ner_output = gr.Textbox(label="抽取结果", lines=10) ner_btn.click( fn=ner_inference, inputs=[ner_text, ner_schema], outputs=ner_output ) with gr.Tab("文本分类"): with gr.Row(): with gr.Column(): cls_text = gr.Textbox( label="输入文本", value="这款手机拍照效果很好,电池也耐用,值得购买", lines=5 ) cls_schema = gr.Textbox( label="分类标签", value='{"正面评价": null, "负面评价": null, "中性评价": null}', lines=3 ) cls_btn = gr.Button("分类") with gr.Column(): cls_output = gr.Textbox(label="分类结果", lines=10) cls_btn.click( fn=text_classification, inputs=[cls_text, cls_schema], outputs=cls_output ) with gr.Tab("使用说明"): gr.Markdown(""" ## Schema格式说明 ### 命名实体识别 ```json {"实体类型": null} 示例: {"人物": null, "地点": null, "组织机构": null} ``` ### 文本分类 ```json {"分类标签": null} 示例: {"科技": null, "体育": null, "娱乐": null} ``` ## 支持的任务类型 - 命名实体识别 (NER) - 关系抽取 (RE) - 事件抽取 (EE) - 文本分类 - 情感分析 - 自然语言推理 - 属性情感抽取 - 共指消解 - 文本匹配 """) if __name__ == "__main__": # 启动服务,监听7860端口 demo.launch( server_name="0.0.0.0", server_port=7860, share=False )这个脚本做了几件重要的事:
- 配置了日志,记录到文件同时输出到控制台
- 加载RexUniNLU模型
- 创建了两个核心函数:实体识别和文本分类
- 用Gradio做了个网页界面,分标签页展示不同功能
3.2 手动测试服务
在配置自动启动之前,我们先手动跑一下看看是否正常:
cd /root/workspace/rex-uninlu source venv/bin/activate python app.py如果一切正常,你会看到类似这样的输出:
Running on local URL: http://0.0.0.0:7860打开浏览器,访问http://你的服务器IP:7860,应该能看到Web界面。试试输入一些文本,看看实体抽取和分类功能是否正常。
测试没问题后,按Ctrl+C停止服务。接下来我们要让它能自动运行。
4. Supervisor自启动配置
4.1 理解Supervisor的作用
Supervisor是个进程管理工具,它能帮我们:
- 开机自动启动服务
- 服务崩溃时自动重启
- 集中管理多个服务
- 查看服务状态和日志
简单说,有了Supervisor,你就不用担心服务器重启后服务没起来,也不用半夜爬起来重启崩溃的服务。
4.2 配置RexUniNLU的Supervisor服务
创建配置文件/etc/supervisor/conf.d/rex-uninlu.conf:
[program:rex-uninlu] # 启动命令 command=/root/workspace/rex-uninlu/venv/bin/python /root/workspace/rex-uninlu/app.py # 工作目录 directory=/root/workspace/rex-uninlu # 启动用户 user=root # 自动启动 autostart=true autorestart=true # 启动等待时间(模型加载需要时间) startsecs=60 startretries=3 # 停止信号 stopsignal=INT stopwaitsecs=30 # 日志配置 stdout_logfile=/root/workspace/rex-uninlu.log stdout_logfile_maxbytes=50MB stdout_logfile_backups=10 stdout_capture_maxbytes=1MB stdout_events_enabled=false stderr_logfile=/root/workspace/rex-uninlu-error.log stderr_logfile_maxbytes=50MB stderr_logfile_backups=10 stderr_capture_maxbytes=1MB stderr_events_enabled=false # 环境变量 environment=PYTHONPATH="/root/workspace/rex-uninlu",PATH="/root/workspace/rex-uninlu/venv/bin:%(ENV_PATH)s" # 进程管理 process_name=%(program_name)s numprocs=1这个配置里有几个关键点:
autostart=true:系统启动时自动运行autorestart=true:程序退出时自动重启startsecs=60:给60秒时间启动(模型加载需要时间)startretries=3:如果启动失败,重试3次- 日志分开记录:正常日志和错误日志分开文件
4.3 启动并验证Supervisor服务
让Supervisor重新加载配置并启动我们的服务:
# 重新加载Supervisor配置 supervisorctl reread supervisorctl update # 启动rex-uninlu服务 supervisorctl start rex-uninlu # 查看状态 supervisorctl status rex-uninlu如果一切正常,你会看到类似这样的输出:
rex-uninlu RUNNING pid 12345, uptime 0:01:30现在服务已经在后台运行了。你可以重启服务器试试:
# 重启服务器 reboot # 重启后查看服务状态 supervisorctl status rex-uninlu应该看到服务仍然是RUNNING状态,这就是自启动生效了。
5. 日志管理与监控方案
5.1 结构化日志配置
我们之前在app.py里配置了基础日志,但生产环境需要更细致的日志管理。创建专门的日志配置文件/root/workspace/rex-uninlu/logging_config.py:
import logging import logging.handlers import os def setup_logging(): """配置结构化日志""" # 创建日志目录 log_dir = "/root/workspace/rex-uninlu/logs" os.makedirs(log_dir, exist_ok=True) # 创建logger logger = logging.getLogger("rex-uninlu") logger.setLevel(logging.INFO) # 清除已有的handler logger.handlers.clear() # 控制台handler console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_format = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) console_handler.setFormatter(console_format) # 文件handler - 按天滚动 file_handler = logging.handlers.TimedRotatingFileHandler( filename=os.path.join(log_dir, "rex-uninlu.log"), when="midnight", interval=1, backupCount=30, encoding="utf-8" ) file_handler.setLevel(logging.INFO) file_format = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s' ) file_handler.setFormatter(file_format) # 错误日志单独文件 error_handler = logging.handlers.TimedRotatingFileHandler( filename=os.path.join(log_dir, "rex-uninlu-error.log"), when="midnight", interval=1, backupCount=30, encoding="utf-8" ) error_handler.setLevel(logging.ERROR) error_handler.setFormatter(file_format) # 添加所有handler logger.addHandler(console_handler) logger.addHandler(file_handler) logger.addHandler(error_handler) return logger # 创建主logger logger = setup_logging() # 辅助函数 def log_inference_start(task_type, text_length, schema): """记录推理开始""" logger.info(f"推理开始 - 任务: {task_type}, 文本长度: {text_length}, Schema: {schema}") def log_inference_end(task_type, success, duration_ms, result_summary): """记录推理结束""" if success: logger.info(f"推理成功 - 任务: {task_type}, 耗时: {duration_ms}ms, 结果摘要: {result_summary}") else: logger.error(f"推理失败 - 任务: {task_type}, 耗时: {duration_ms}ms, 错误: {result_summary}") def log_model_loading(phase, status, details=""): """记录模型加载状态""" logger.info(f"模型加载 - 阶段: {phase}, 状态: {status}, 详情: {details}")然后在app.py里使用这个日志配置:
# 在app.py开头添加 from logging_config import logger, log_inference_start, log_inference_end # 修改ner_inference函数 def ner_inference(text, schema_str): """命名实体识别""" start_time = time.time() try: schema = json.loads(schema_str) log_inference_start("NER", len(text), schema_str) result = uninlu_pipeline(text, schema) duration = int((time.time() - start_time) * 1000) # 提取结果摘要 result_summary = {} if "抽取实体" in result: for entity_type, entities in result["抽取实体"].items(): result_summary[entity_type] = len(entities) log_inference_end("NER", True, duration, result_summary) return json.dumps(result, ensure_ascii=False, indent=2) except Exception as e: duration = int((time.time() - start_time) * 1000) log_inference_end("NER", False, duration, str(e)) return f"错误: {str(e)}"5.2 日志查看与分析工具
有了结构化的日志,我们还需要方便的工具来查看和分析。创建几个实用的脚本:
实时日志查看脚本monitor_logs.sh:
#!/bin/bash LOG_DIR="/root/workspace/rex-uninlu/logs" echo "选择要查看的日志:" echo "1. 实时查看最新日志" echo "2. 查看错误日志" echo "3. 按日期查看日志" echo "4. 搜索特定内容" read -p "请输入选项 (1-4): " choice case $choice in 1) tail -f $LOG_DIR/rex-uninlu.log ;; 2) tail -100 $LOG_DIR/rex-uninlu-error.log ;; 3) echo "可用的日志文件:" ls -la $LOG_DIR/*.log* read -p "输入要查看的文件名: " filename less $LOG_DIR/$filename ;; 4) read -p "输入要搜索的关键词: " keyword grep -r "$keyword" $LOG_DIR --include="*.log*" ;; *) echo "无效选项" ;; esac日志分析脚本analyze_logs.py:
import re from collections import Counter from datetime import datetime, timedelta import glob def analyze_recent_logs(hours=24): """分析最近指定小时内的日志""" log_files = glob.glob("/root/workspace/rex-uninlu/logs/rex-uninlu.log*") stats = { "total_requests": 0, "successful_requests": 0, "failed_requests": 0, "avg_response_time": 0, "task_types": Counter(), "error_types": Counter() } response_times = [] cutoff_time = datetime.now() - timedelta(hours=hours) for log_file in log_files: with open(log_file, 'r', encoding='utf-8') as f: for line in f: # 解析时间戳 time_match = re.match(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line) if time_match: log_time = datetime.strptime(time_match.group(1), "%Y-%m-%d %H:%M:%S") if log_time < cutoff_time: continue # 统计请求 if "推理开始" in line: stats["total_requests"] += 1 # 提取任务类型 task_match = re.search(r'任务: (\w+)', line) if task_match: stats["task_types"][task_match.group(1)] += 1 # 统计成功/失败 elif "推理成功" in line: stats["successful_requests"] += 1 # 提取响应时间 time_match = re.search(r'耗时: (\d+)ms', line) if time_match: response_times.append(int(time_match.group(1))) elif "推理失败" in line: stats["failed_requests"] += 1 # 提取错误类型 error_match = re.search(r'错误: (.+)$', line) if error_match: error_msg = error_match.group(1) # 简化错误信息 if "JSON" in error_msg: stats["error_types"]["JSON解析错误"] += 1 elif "内存" in error_msg or "Memory" in error_msg: stats["error_types"]["内存不足"] += 1 elif "超时" in error_msg or "timeout" in error_msg: stats["error_types"]["超时"] += 1 else: stats["error_types"]["其他错误"] += 1 # 计算平均响应时间 if response_times: stats["avg_response_time"] = sum(response_times) / len(response_times) return stats def print_report(stats): """打印分析报告""" print("\n" + "="*50) print("RexUniNLU 服务运行报告") print("="*50) print(f"\n 请求统计:") print(f" 总请求数: {stats['total_requests']}") print(f" 成功请求: {stats['successful_requests']}") print(f" 失败请求: {stats['failed_requests']}") if stats['total_requests'] > 0: success_rate = (stats['successful_requests'] / stats['total_requests']) * 100 print(f" 成功率: {success_rate:.1f}%") print(f"\n⏱ 性能指标:") print(f" 平均响应时间: {stats['avg_response_time']:.0f}ms") print(f"\n 任务分布:") for task_type, count in stats['task_types'].most_common(): percentage = (count / stats['total_requests'] * 100) if stats['total_requests'] > 0 else 0 print(f" {task_type}: {count}次 ({percentage:.1f}%)") if stats['failed_requests'] > 0: print(f"\n 错误分析:") for error_type, count in stats['error_types'].most_common(): percentage = (count / stats['failed_requests'] * 100) print(f" {error_type}: {count}次 ({percentage:.1f}%)") print("\n" + "="*50) if __name__ == "__main__": stats = analyze_recent_logs(hours=24) print_report(stats)5.3 监控告警设置
对于生产环境,我们还需要设置简单的监控告警。创建监控脚本monitor_service.sh:
#!/bin/bash # 监控RexUniNLU服务状态 LOG_FILE="/root/workspace/rex-uninlu/monitor.log" ERROR_THRESHOLD=10 # 10分钟内最多允许的错误数 CHECK_INTERVAL=300 # 5分钟检查一次 # 检查服务状态 check_service() { # 检查Supervisor状态 supervisor_status=$(supervisorctl status rex-uninlu 2>/dev/null | grep -o "RUNNING\|STOPPED\|FATAL") if [ "$supervisor_status" != "RUNNING" ]; then echo "$(date) - 服务状态异常: $supervisor_status" >> $LOG_FILE # 尝试自动恢复 supervisorctl restart rex-uninlu echo "$(date) - 已尝试重启服务" >> $LOG_FILE return 1 fi # 检查端口是否监听 if ! netstat -tlnp | grep :7860 | grep -q python; then echo "$(date) - 端口7860未监听" >> $LOG_FILE return 1 fi # 检查最近错误数 recent_errors=$(grep -c "推理失败" /root/workspace/rex-uninlu/logs/rex-uninlu.log 2>/dev/null || echo 0) if [ "$recent_errors" -gt "$ERROR_THRESHOLD" ]; then echo "$(date) - 错误数过多: $recent_errors" >> $LOG_FILE return 1 fi echo "$(date) - 服务正常" >> $LOG_FILE return 0 } # 主循环 while true; do check_service sleep $CHECK_INTERVAL done把这个监控脚本也交给Supervisor管理,创建配置文件/etc/supervisor/conf.d/rex-uninlu-monitor.conf:
[program:rex-uninlu-monitor] command=/bin/bash /root/workspace/rex-uninlu/monitor_service.sh directory=/root/workspace/rex-uninlu user=root autostart=true autorestart=true startsecs=10 stdout_logfile=/root/workspace/rex-uninlu/monitor.log stdout_logfile_maxbytes=10MB stderr_logfile=/root/workspace/rex-uninlu/monitor-error.log6. 异常恢复与健康检查
6.1 健康检查接口
为了让外部系统能检查服务状态,我们添加一个健康检查接口。修改app.py,添加健康检查路由:
# 在Gradio demo定义之前添加 import time from fastapi import FastAPI from fastapi.responses import JSONResponse import uvicorn # 创建FastAPI应用(Gradio基于FastAPI) app = FastAPI() # 健康检查端点 @app.get("/health") async def health_check(): """健康检查接口""" try: # 检查模型是否加载 if uninlu_pipeline is None: return JSONResponse( status_code=503, content={"status": "unhealthy", "reason": "model_not_loaded"} ) # 简单测试推理 test_text = "测试文本" test_schema = {"测试": None} result = uninlu_pipeline(test_text, test_schema) return { "status": "healthy", "timestamp": time.time(), "model": "rex-uninlu", "version": "1.0" } except Exception as e: logger.error(f"健康检查失败: {e}") return JSONResponse( status_code=503, content={"status": "unhealthy", "reason": str(e)} ) # 将Gradio应用挂载到FastAPI app = gr.mount_gradio_app(app, demo, path="/") # 修改启动方式 if __name__ == "__main__": uvicorn.run( app, host="0.0.0.0", port=7860, log_config=None )现在你可以通过访问http://服务器IP:7860/health来检查服务状态。
6.2 自动恢复策略
有时候服务可能会因为内存泄漏、GPU内存不足等问题而变慢或崩溃。我们需要更智能的恢复策略。创建恢复脚本recovery_manager.py:
import subprocess import time import psutil import logging logger = logging.getLogger(__name__) class RecoveryManager: def __init__(self): self.service_name = "rex-uninlu" self.max_restarts_per_hour = 3 self.restart_times = [] def check_service_health(self): """检查服务健康状态""" checks = [] # 检查Supervisor状态 try: result = subprocess.run( ["supervisorctl", "status", self.service_name], capture_output=True, text=True ) if "RUNNING" in result.stdout: checks.append(("supervisor", True, "服务运行中")) else: checks.append(("supervisor", False, result.stdout.strip())) except Exception as e: checks.append(("supervisor", False, str(e))) # 检查端口监听 try: result = subprocess.run( ["netstat", "-tlnp"], capture_output=True, text=True ) if ":7860" in result.stdout and "python" in result.stdout: checks.append(("port", True, "端口监听正常")) else: checks.append(("port", False, "端口未监听")) except Exception as e: checks.append(("port", False, str(e))) # 检查内存使用 try: process = None for proc in psutil.process_iter(['pid', 'name', 'memory_info']): if "python" in proc.info['name'] and "app.py" in ' '.join(proc.cmdline()): process = proc break if process: memory_mb = process.memory_info().rss / 1024 / 1024 checks.append(("memory", True, f"内存使用: {memory_mb:.1f}MB")) # 如果内存超过2GB,可能需要重启 if memory_mb > 2000: checks.append(("memory_high", False, f"内存使用过高: {memory_mb:.1f}MB")) else: checks.append(("memory", False, "未找到进程")) except Exception as e: checks.append(("memory", False, str(e))) return checks def should_restart(self, health_checks): """判断是否需要重启""" failures = [check for check in health_checks if not check[1]] if len(failures) >= 2: # 至少两个检查失败 logger.warning(f"服务健康检查失败: {failures}") return True # 检查内存是否过高 for check in health_checks: if check[0] == "memory_high" and not check[1]: logger.warning(f"内存使用过高,建议重启") return True return False def can_restart(self): """检查是否允许重启(防止频繁重启)""" now = time.time() hour_ago = now - 3600 # 清理一小时前的重启记录 self.restart_times = [t for t in self.restart_times if t > hour_ago] if len(self.restart_times) >= self.max_restarts_per_hour: logger.error(f"一小时内重启次数已达上限: {self.max_restarts_per_hour}") return False return True def restart_service(self): """重启服务""" if not self.can_restart(): return False try: logger.info("开始重启服务...") # 停止服务 subprocess.run(["supervisorctl", "stop", self.service_name], check=True) time.sleep(5) # 启动服务 subprocess.run(["supervisorctl", "start", self.service_name], check=True) # 记录重启时间 self.restart_times.append(time.time()) logger.info("服务重启完成") return True except subprocess.CalledProcessError as e: logger.error(f"重启服务失败: {e}") return False def run_monitoring(self, interval=300): """运行监控循环""" logger.info("启动服务监控...") while True: try: health_checks = self.check_service_health() if self.should_restart(health_checks): logger.warning("检测到服务异常,尝试恢复...") self.restart_service() else: # 记录健康状态 healthy_checks = [c for c in health_checks if c[1]] if len(healthy_checks) == len(health_checks): logger.debug("服务状态正常") time.sleep(interval) except Exception as e: logger.error(f"监控循环异常: {e}") time.sleep(interval) if __name__ == "__main__": # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) manager = RecoveryManager() manager.run_monitoring()6.3 完整的服务管理脚本
最后,我们创建一个统一的管理脚本,方便日常操作。创建manage_service.sh:
#!/bin/bash SERVICE_NAME="rex-uninlu" LOG_DIR="/root/workspace/rex-uninlu/logs" case "$1" in start) echo "启动RexUniNLU服务..." supervisorctl start $SERVICE_NAME ;; stop) echo "停止RexUniNLU服务..." supervisorctl stop $SERVICE_NAME ;; restart) echo "重启RexUniNLU服务..." supervisorctl restart $SERVICE_NAME ;; status) echo "服务状态:" supervisorctl status $SERVICE_NAME echo -e "\n端口监听:" netstat -tlnp | grep :7860 || echo "端口未监听" echo -e "\n最近日志:" tail -20 $LOG_DIR/rex-uninlu.log ;; logs) case "$2" in app) tail -f $LOG_DIR/rex-uninlu.log ;; error) tail -f $LOG_DIR/rex-uninlu-error.log ;; monitor) tail -f /root/workspace/rex-uninlu/monitor.log ;; *) echo "用法: $0 logs {app|error|monitor}" ;; esac ;; analyze) echo "分析服务日志..." cd /root/workspace/rex-uninlu source venv/bin/activate python analyze_logs.py ;; backup) echo "备份服务配置..." BACKUP_DIR="/root/backup/rex-uninlu_$(date +%Y%m%d_%H%M%S)" mkdir -p $BACKUP_DIR # 备份配置文件 cp /etc/supervisor/conf.d/rex-uninlu.conf $BACKUP_DIR/ cp /root/workspace/rex-uninlu/app.py $BACKUP_DIR/ cp /root/workspace/rex-uninlu/logging_config.py $BACKUP_DIR/ # 备份模型(如果存在) if [ -d "/root/.cache/modelscope/hub" ]; then cp -r /root/.cache/modelscope/hub $BACKUP_DIR/models/ fi echo "备份完成: $BACKUP_DIR" ;; *) echo "RexUniNLU 服务管理工具" echo "用法: $0 {start|stop|restart|status|logs|analyze|backup}" echo "" echo "命令说明:" echo " start 启动服务" echo " stop 停止服务" echo " restart 重启服务" echo " status 查看服务状态" echo " logs 查看日志 (app|error|monitor)" echo " analyze 分析日志" echo " backup 备份配置" exit 1 ;; esac给脚本执行权限:
chmod +x manage_service.sh现在你可以用简单的命令管理服务了:
./manage_service.sh start- 启动服务./manage_service.sh status- 查看状态./manage_service.sh logs app- 查看应用日志./manage_service.sh analyze- 分析日志
7. 总结
7.1 部署成果回顾
走到这里,你已经完成了一个生产级的RexUniNLU部署方案。让我们回顾一下都实现了什么:
稳定可靠的服务:通过Supervisor实现了服务自启动和自动重启,服务器重启也不用担心服务挂掉。
专业的日志管理:
- 结构化日志记录,方便问题排查
- 日志按天滚动,自动清理旧日志
- 错误日志单独记录,快速定位问题
- 提供了日志分析工具,了解服务运行状况
智能的异常恢复:
- 健康检查接口,外部系统可以监控服务状态
- 自动检测服务异常(进程退出、端口未监听、内存过高等)
- 智能重启策略,防止频繁重启
- 监控脚本持续检查服务健康
便捷的管理工具:
- 统一的管理脚本,一条命令完成各种操作
- 实时日志查看,不用记复杂的命令
- 服务状态一目了然,包括运行状态、端口监听、最近日志
7.2 实际使用建议
基于我的使用经验,给你几个实用建议:
对于刚部署好的情况:
- 先用
./manage_service.sh status确认服务状态 - 访问Web界面(http://服务器IP:7860)测试基本功能
- 运行
./manage_service.sh analyze看看初始运行情况
日常维护时:
- 每周查看一次日志分析,了解服务健康度
- 如果发现错误率升高,检查是不是输入数据格式有问题
- 定期备份配置,特别是如果你修改了app.py或配置文件
遇到问题时:
- 先看状态:
./manage_service.sh status - 再看日志:
./manage_service.sh logs error - 如果服务挂了,尝试重启:
./manage_service.sh restart - 如果频繁重启,检查内存使用:
free -h和nvidia-smi(如果有GPU)
7.3 后续优化方向
这个方案已经能满足大多数生产需求,但如果你有更高的要求,还可以考虑:
- 多实例负载均衡:如果请求量很大,可以部署多个实例,用Nginx做负载均衡
- 容器化部署:把整个环境打包成Docker镜像,部署更简单
- 监控告警集成:把健康检查接入Prometheus+Grafana,设置告警规则
- API限流:防止恶意请求或意外的高并发打垮服务
- 模型版本管理:当模型更新时,平滑切换到新版本
7.4 最后的提醒
部署只是第一步,更重要的是持续观察和维护。建议你:
- 养成定期检查日志的习惯
- 关注服务的响应时间和成功率
- 备份重要的配置和脚本
- 记录遇到的问题和解决方法,形成自己的知识库
RexUniNLU是个很强大的工具,现在你有了稳定的部署方案,可以放心地把它用在各种自然语言理解任务上了。无论是抽实体、分类文本,还是分析情感,它都能帮你省下大量人工标注的时间。
如果在使用过程中遇到问题,或者有新的需求,欢迎随时调整这个方案。好的部署方案不是一成不变的,而是随着使用需求不断优化的。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。