自动化本地LLM推理服务器健康监控的实践

自动化本地LLM推理服务器健康监控的实践

3 min read

2026.04 / Tech Blog / BASTION

本地LLM推理服务器健康监控自动化实践 #

GPUStack 存在”看起来在运行,但实际无法进行推理”的状态。我们构建了一套系统,以5分钟为间隔运行两阶段健康检查 + Slack 通知 + Zabbix 集成。

问题是什么? #

上一篇文章介绍的 BASTION(用本地LLM自动分析基础设施日志的机制)每15分钟使用本地LLM(Qwen2.5-14B)自动分析基础设施日志。

这套机制的单点故障是 GPUStack(LLM推理服务器)。GPUStack 一旦宕机,日志采集会继续进行,但分析和通知会全部停止。而且要注意到它已经停止需要花费时间。每15分钟到来的 Slack 通知”没有送达”,人工察觉到这一点可能需要好几个小时。

GPUStack 本身具备模型自动重启功能。如果进程崩溃,会自动重启。但仅凭这一点无法检测到某些情况。

GPUStack 自动重启无法检测到的情况:
• API进程存活,但模型因OOM被卸载
• API返回200,但模型对推理请求没有响应
• 从网络角度看,BASTION 服务器无法访问 GPUStack

换句话说,”进程存活”和”实际能够推理”是两回事。因此需要一套机制,能够定期从外部确认”实际是否能够执行推理”。

设计思路 #

任务很简单:每5分钟调用一次 GPUStack API,正常则什么都不做,异常则通知 Slack。不过在设计上有几个决策点。

两阶段检查 #

我们将健康检查分为2个阶段。

阶段检查内容检测目标失败时
CHECK1对 /v1/models API 发送HTTP请求GPUStack 进程的存活状态Exit 1(CRITICAL)
CHECK2对 chat/completions 发送实际推理请求模型是否真正已加载并能够响应Exit 2(WARNING)

CHECK1 通过但 CHECK2 失败的情况尤为重要。这就是”API存活,但模型无法进行推理”的状态。仅靠进程监控是永远无法检测到这一点的。

在 CHECK2 中,我们发送单个单词 "healthcheck",并以 max_tokens: 5 接收响应。由于只需确认推理是否可用,因此使用最少的 token 数。

退出码设计 #

我们将脚本的退出码设计为3种取值。

退出码含义Slack通知Zabbix触发器
0正常仅在恢复时—
1API 宕机CRITICAL(红色)严重故障
2模型宕机WARNING(橙色)警告

我们把1和2区分开,是因为应对方式不同。对于 Exit 1,需要检查 GPUStack 进程本身。对于 Exit 2,只需查看 GPUStack 控制台的模型状态即可。接收通知的人仅凭退出码就能知道接下来该做什么。

连续告警抑制 #

如果以5分钟为间隔运行,那么当 GPUStack 宕机30分钟时,同样的 CRITICAL 通知会被发送6次。到第三次时,已经没有人再去看 Slack 了。

为防止这种情况,我们将上一次的状态保存到文件中,只在状态发生变化时才发送通知。

状态迁移通知
ok → api_down发送 CRITICAL 通知
ok → model_down发送 WARNING 通知
api_down → ok发送 RECOVERED 通知
api_down → api_down不通知
model_down → model_down不通知

恢复时发送 RECOVERED 通知也很重要。收到 CRITICAL 通知的人无需打开控制台确认”是仍在宕机中,还是已经修复”。

实现 #

全部通过一个 shell 脚本完成。它与其他 BASTION 脚本(summarize.sh、analyze.sh 等)放在同一目录下,由 cron 每5分钟执行一次。

CHECK1:确认API响应 #

MODELS_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
    --max-time 15 \
    -H "Authorization: Bearer ${API_KEY}" \
    "${GPUSTACK_HOST}/v1/models")

由于 GPUStack 提供与 OpenAI 兼容的 API,因此只需向 /v1/models 端点发送 GET 请求即可。如果返回 HTTP 200,则说明 API 存活。超时设置为15秒。

CHECK2:实际推理测试 #

INFERENCE_RESPONSE=$(curl -s --max-time 15 \
    -H "Authorization: Bearer ${API_KEY}" \
    -H "Content-Type: application/json" \
    "${GPUSTACK_HOST}/v1/chat/completions" \
    -d '{"model":"qwen2.5-14b-instruct",
         "messages":[{"role":"user","content":"healthcheck"}],
         "max_tokens":5,"temperature":0}')

如果响应 JSON 中包含 "choices",则说明推理成功。否则将其判定为模型宕机进行处理。

状态持久化 #

# Read previous state
STATUS_FILE="/tmp/gpustack-health-status"
PREV_STATUS="ok"
[ -f "$STATUS_FILE" ] && PREV_STATUS=$(cat "$STATUS_FILE")

# Save current state
echo "ok" > "$STATUS_FILE"      # or "api_down" or "model_down"

只需将状态写入单个文件即可,不需要复杂的数据库。即使重启之后,首次运行时也因为没有文件而以 ok 为默认值,如果存在异常,则会在首次运行时发送通知。

cron 注册 #

*/5 * * * * bash /opt/oc-seclogs/gpustack-healthcheck.sh >> /var/log/gpustack-healthcheck.log 2>&1

我们将间隔设置为5分钟,比 BASTION 的定期分析间隔(15分钟)更短。如果在LLM宕机的情况下运行定期分析,会导致失败,因此我们希望在此之前就检测出来。

我们实际遇到的问题 #

遗漏了API密钥认证 #

第一次运行时,CHECK1 立即返回了 HTTP 401。GPUStack 已启用API密钥认证,但我在 curl 中没有包含 Authorization 请求头。这虽然是一个明显的失误,但我认为在测试环境(无需认证)下编写健康检查脚本,然后原样搬到生产环境的情况是很常见的。我们通过修改脚本从 .env 文件中读取API密钥解决了这个问题。

CHECK1通过但CHECK2失败的情况确实存在 #

GPUStack 的 API 进程在运行,但模型加载失败。/v1/models 返回200,但 chat/completions 返回500。如果只靠进程监控,会将其判定为”正常”。采用两阶段检查的决策在实际运维中得到了验证。

Slack通知内容应包含”接下来该怎么做” #

最初我们只写了”GPUStack API 无响应”,但接收通知的人(也就是我自己)接下来总是做同样的事情。因此我们在通知文本中加入了确认命令(curl -s http://<endpoint>/v1/models)。收到通知后,只需复制粘贴到终端即可快速确认状态。

Zabbix 集成 #

基于cron的Slack通知即时性很高,但缺乏”过去一个月的正常运行时间是多少””响应时间的趋势如何”这类长期视角。这正是Zabbix的强项,因此我们也注册了 UserParameter,以便从 Zabbix 进行监控。

UserParameter #

我们在 Zabbix Agent 的配置文件中添加了3个 UserParameter。

Key内容数据类型
gpustack.health健康检查脚本的退出码(0/1/2)整数
gpustack.response_timeAPI响应时间(毫秒)整数
gpustack.models模型列表 JSON文本

gpustack.health 会调用健康检查脚本本身并返回退出码。通过在cron和Zabbix中复用同一个脚本,避免了判断逻辑的重复。

触发器 #

我们设置了3个级别的触发器。

严重程度条件含义
严重故障gpustack.health = 1API 宕机。LLM推理完全停止。
警告gpustack.health = 2API 存活,但模型无法进行推理。
信息gpustack.response_time > 5000API 响应耗时5秒以上。是GPU高负载的征兆。

第三个”响应延迟”触发器起到了在变为 CRITICAL 之前进行前兆检测的作用。如果在图表中看到响应时间逐渐上升,就可以在发生OOM之前调整模型的批处理大小或并发请求数。

cron 与 Zabbix 的角色分工 #

cron(5分钟间隔):检测到异常时立即通知Slack。简单可靠。
Zabbix(5分钟间隔):历史记录存储、响应时间图表、告警升级、故障历史。

两者同时运行确保了冗余性。即使cron停止,Zabbix也能捕获;即使Zabbix宕机,cron仍会发出通知。

运维成果 #

自开始运维以来,我们已经检测到2起 GPUStack 模型停止响应的情况。两次都是 CHECK1 通过,CHECK2 检测到了异常(Exit 2)。查看 GPUStack 控制台,模型处于”Error”状态。GPUStack 的自动重启功能重新加载了模型,几分钟后收到了RECOVERED通知,确认已恢复。

如果没有健康检查,下一次定期分析(最长15分钟后)会失败,而人工察觉到Slack没有发送通知则需要花费更长时间。

总结 #

我们使用 shell 脚本 + cron + Zabbix,实现了本地LLM推理服务器(GPUStack)的健康监控自动化。

有3个设计要点值得关注。两阶段检查(将API响应与实际推理分开检测,提高检测精度)、连续告警抑制(仅在状态变化时通知,防止告警疲劳)、cron与Zabbix并用(同时实现即时通知与长期趋势把握)。

由于全部通过一个shell脚本完成,同样的方法也可用于监控 vLLM、Ollama 等其他提供OpenAI兼容API的LLM服务器。

BASTION 是一项在封闭环境中实现AI安全监控的服务。

BASTION 服务页面
联系我们

Updated on 2026年10月19日

What are your feelings

  • Happy
  • 常规
  • Sad

©2020 BESTNET.LLC . All Rights Reserved.