如何实现自动重启?DeepSeek-R1-Distill-Qwen-1.5B守护脚本编写
1. 引言:模型服务稳定性的重要性
在部署大型语言模型(LLM)如DeepSeek-R1-Distill-Qwen-1.5B的生产环境中,服务的持续可用性至关重要。尽管该模型具备强大的数学推理、代码生成和逻辑推导能力,但在实际运行中仍可能因内存溢出、CUDA异常、网络中断或系统负载过高导致进程意外终止。
本文将围绕DeepSeek-R1-Distill-Qwen-1.5B模型 Web 服务的实际部署场景,介绍如何构建一个高可靠性的守护脚本,实现服务崩溃后的自动检测与重启,保障服务7×24小时稳定运行。
2. 守护机制设计思路
2.1 为什么需要守护脚本?
虽然可以通过nohup或systemd启动后台服务,但这些方式无法动态感知进程是否真正健康。例如:
- Python 进程仍在,但 GPU 显存已耗尽导致响应超时
- Gradio 接口返回 500 错误,但主进程未退出
- CUDA runtime 抛出不可恢复异常后程序卡死
因此,仅依赖“进程存在”判断是不够的,需结合进程状态 + 健康检查实现精准监控。
2.2 守护策略选择对比
| 方案 | 是否支持自动重启 | 健康检查能力 | 配置复杂度 | 适用场景 |
|---|---|---|---|---|
| nohup + 手动重启 | ❌ | ❌ | 低 | 临时测试 |
| systemd 服务管理 | ✅ | ⚠️(仅进程级) | 中 | 系统级常驻 |
| Docker + restart policy | ✅ | ⚠️(依赖容器退出码) | 中 | 容器化部署 |
| 自定义守护脚本 | ✅✅ | ✅✅(可集成HTTP探测) | 低 | 快速落地 |
本文采用自定义守护脚本 + HTTP健康探测的方式,在灵活性与实用性之间取得最佳平衡。
3. 守护脚本实现详解
3.1 脚本功能目标
- 监控指定端口(默认7860)上的服务是否可访问
- 若服务不可达,则杀掉残留进程并重新启动
- 记录日志便于排查问题
- 支持配置重试间隔、最大尝试次数等参数
3.2 核心代码实现
#!/usr/bin/env python3 # monitor.py - DeepSeek-R1-Distill-Qwen-1.5B 守护脚本 import os import time import subprocess import requests from pathlib import Path # =============== 配置参数 =============== MODEL_NAME = "DeepSeek-R1-Distill-Qwen-1.5B" APP_DIR = "/root/DeepSeek-R1-Distill-Qwen-1.5B" APP_SCRIPT = "app.py" PORT = 7860 HEALTH_URL = f"http://localhost:{PORT}" CHECK_INTERVAL = 30 # 检查间隔(秒) RESTART_DELAY = 10 # 重启等待时间 MAX_RETRIES = 3 # 最大连续重启次数 LOG_FILE = "/tmp/deepseek_monitor.log" PID_FILE = "/tmp/deepseek_web.pid" def log(msg): timestamp = time.strftime("%Y-%m-%d %H:%M:%S") print(f"[{timestamp}] {msg}") with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(f"[{timestamp}] {msg}\n") def is_port_in_use(): try: result = subprocess.run( ["lsof", "-i:{}".format(PORT)], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return len(result.stdout) > 0 except Exception: return False def kill_process_on_port(): if not is_port_in_use(): return try: subprocess.run( f"lsof -i:{PORT} | grep LISTEN | awk '{{print $2}}' | xargs kill -9 2>/dev/null || true", shell=True ) log(f"已释放端口 {PORT} 上的占用进程") except Exception as e: log(f"清理端口失败: {e}") def start_app(): cmd = f"cd {APP_DIR} && nohup python3 {APP_SCRIPT} > /tmp/deepseek_web.log 2>&1 & echo $! > {PID_FILE}" try: subprocess.run(cmd, shell=True, check=True) time.sleep(5) # 给启动留出时间 pid = "" if Path(PID_FILE).exists(): pid = Path(PID_FILE).read_text().strip() log(f"成功启动 {MODEL_NAME} 服务 (PID: {pid})") return True except Exception as e: log(f"启动应用失败: {e}") return False def is_service_healthy(): try: response = requests.get(HEALTH_URL, timeout=10) return response.status_code == 200 except Exception: return False def main(): log(f"开始监控 {MODEL_NAME} 服务 (端口: {PORT})") retry_count = 0 while True: if is_service_healthy(): if retry_count > 0: log("服务已恢复正常") retry_count = 0 time.sleep(CHECK_INTERVAL) continue log(f"服务未响应 (尝试 #{retry_count + 1})") kill_process_on_port() if retry_count >= MAX_RETRIES: log("达到最大重试次数,停止自动重启,请手动检查!") break success = start_app() if success: time.sleep(15) # 等待服务初始化 if is_service_healthy(): log("服务重启成功") retry_count = 0 else: log("服务启动但未通过健康检查") retry_count += 1 else: log("启动失败,将在下次检查时重试") retry_count += 1 time.sleep(RESTART_DELAY) if __name__ == "__main__": main()3.3 关键逻辑说明
健康检查机制
使用requests.get()请求根路径/,验证服务是否返回 HTTP 200。Gradio 默认首页为 UI 页面,若能加载说明服务基本正常。
PID 文件记录
通过echo $! > pidfile记录子进程 ID,便于后续追踪。但由于nohup子进程特性,此值可能不准确,故主要依赖端口检测。
多次重试保护
设置MAX_RETRIES=3,防止因模型加载失败导致无限循环重启,避免系统资源耗尽。
日志分离
- 应用日志:
/tmp/deepseek_web.log(来自nohup输出) - 守护日志:
/tmp/deepseek_monitor.log(脚本自身行为)
4. 使用方法与部署流程
4.1 权限设置与脚本准备
# 将 monitor.py 放入项目目录 cp monitor.py /root/DeepSeek-R1-Distill-Qwen-1.5B/ # 添加可执行权限 chmod +x /root/DeepSeek-R1-Distill-Qwen-1.5B/monitor.py4.2 后台运行守护脚本
# 启动守护进程 nohup python3 /root/DeepSeek-R1-Distill-Qwen-1.5B/monitor.py > /tmp/monitor.log 2>&1 & # 查看守护日志 tail -f /tmp/monitor.log4.3 测试守护效果
模拟服务崩溃:
# 手动杀死进程 ps aux | grep "python3 app.py" | grep -v grep | awk '{print $2}' | xargs kill观察日志输出:
[2025-04-05 10:23:15] 服务未响应 (尝试 #1) [2025-04-05 10:23:15] 已释放端口 7860 上的占用进程 [2025-04-05 10:23:15] 成功启动 DeepSeek-R1-Distill-Qwen-1.5B 服务 (PID: 12345) [2025-04-05 10:23:30] 服务重启成功表明守护脚本成功捕获异常并完成重启。
5. 进阶优化建议
5.1 结合 systemd 实现开机自启
创建系统服务文件/etc/systemd/system/deepseek-monitor.service:
[Unit] Description=DeepSeek-R1-Distill-Qwen-1.5B Monitor Service After=network.target [Service] Type=simple User=root WorkingDirectory=/root/DeepSeek-R1-Distill-Qwen-1.5B ExecStart=/usr/bin/python3 /root/DeepSeek-R1-Distill-Qwen-1.5B/monitor.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target启用服务:
systemctl daemon-reexec systemctl enable deepseek-monitor.service systemctl start deepseek-monitor.service5.2 添加邮件/钉钉告警通知
可在log()函数基础上扩展告警通道:
def send_alert(msg): # 示例:调用钉钉机器人 webhook webhook = "https://oapi.dingtalk.com/robot/send?access_token=xxx" data = { "msgtype": "text", "text": {"content": f"[DeepSeek 服务告警]\n{msg}"} } try: requests.post(webhook, json=data, timeout=5) except: pass在关键事件处调用send_alert("服务连续重启3次,请立即检查!")
5.3 GPU 资源监控集成
使用nvidia-smi检测显存使用情况:
def get_gpu_memory_used(): try: result = subprocess.run( ["nvidia-smi", "--query-gpu=memory.used", "--format=csv,noheader,nounits"], stdout=subprocess.PIPE, text=True ) return int(result.stdout.strip().split('\n')[0]) except: return 0当显存持续高于阈值时提前预警或触发重启。
6. 总结
本文针对DeepSeek-R1-Distill-Qwen-1.5B模型服务的实际运行需求,设计并实现了一套完整的守护脚本方案,具备以下核心价值:
- 自动化恢复能力:服务一旦中断即可自动重启,显著提升可用性;
- 健康感知更精准:不仅检测进程是否存在,还验证接口是否可正常响应;
- 工程可落地性强:代码简洁、依赖少、易于集成到现有部署流程;
- 扩展性良好:支持对接告警系统、资源监控、日志分析等运维体系。
对于所有基于 HuggingFace + Gradio 架构部署的大模型服务,该方案均可快速迁移复用,是构建鲁棒性 AI 服务基础设施的重要一环。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。