【运维实战】构建基于 IPMI + Shell + 钉钉的服务器硬件自动化监控系统

前言

在数据中心管理中,服务器的物理环境安全(如环境温度、电源冗余)是业务稳定运行的“底座”。一旦机房空调故障导致温度过高,或冗余电源发生损坏,若不及时处理,极易引发硬件物理损坏或业务非计划停机。

为了实现低成本、高效率的机房监控,本文将分享一套轻量级的自动化方案:

  • 数据采集:通过 IPMI 协议直接读取硬件传感器原始数据。
  • 逻辑处理:利用 Shell 脚本进行阈值判断与状态翻转逻辑控制。
  • 持久化:将采集到的温度、电源状态实时写入 MySQL 数据库,便于后续追溯。
  • 即时告警:集成 钉钉机器人 (Webhook),实现故障秒级告警与恢复通知。

环境说明:

硬件平台:DELL PowerEdge R740XD

操作系统:CentOS 7.9

注意:不同厂商(如华为、浪潮、HP)或不同型号的服务器,其 ipmitool sensor 返回的字符串格式可能略有差异。本文旨在提供一个闭环的监控思路,读者在实操时请根据自身硬件环境调整 awk 过滤参数。


一、 系统架构图

该方案主要由四个模块组成:

  1. 数据采集层:利用 ipmitool 穿透系统内核,直接读取 BMC 传感器的物理数值。
  2. 数据持久化:将每次采集的温度、电源状态存入 MySQL,为后续趋势分析(如 Grafana)做准备。
  3. 告警逻辑处理:脚本内置“状态机”逻辑,通过标记文件实现异常即告警、恢复即通知,避免重复轰炸。
  4. 通知层:调用 PHP 封装的钉钉 Webhook 接口推送消息。

二、 核心代码实现

1. 监控采集与逻辑脚本

这是系统的主大脑,负责所有核心逻辑。

Bash

# 核心逻辑解析:
# 1. 自动提取 CPU、进风口 (Inlet)、出风口 (Exhaust) 温度
# 2. 监测 PSU (电源) 状态,将 "ok" 转换为布尔值 1/0
# 3. 状态翻转判断:使用 flag 文件记录当前状态,仅在状态变化时触发钉钉消息
[root@gateway-hr ~]# cat collect_server_status.sh
#!/bin/bash

# ================= 配置区 =================
DB_HOST="localhost"
DB_USER="root"
DB_PASS='XXXXXXXXXX'  #改成你自己的数据库密码
DB_NAME="server"

ALERT_TEMP=29                                 # Inlet 告警阈值
RECOVER_FLAG="/root/inlet_temp_recover.flag" # 恢复通知标记文件
PSU_ALERT_FLAG="/root/psu_alert.flag"        # 电源告警标记文件

HOSTNAME=$(hostname)
IP_ADDR=$(hostname -I | awk '{print $1}')

# ================= 采集温度 =================
read CPU1 CPU2 INLET EXHAUST <<< $(ipmitool sensor list | awk -F'|' '
$1~/^Temp/{
    cpu++
    gsub(/ /,"",$2)
    print $2
}
$1~/Inlet Temp/{
    gsub(/ /,"",$2)
    print $2
}
$1~/Exhaust Temp/{
    gsub(/ /,"",$2)
    print $2
}
')

# ================= 采集电源状态 =================
PSU_RAW=$(ipmitool sdr type "Power Supply")

# 第二行 → PSU1
PSU1_LINE=$(echo "$PSU_RAW" | sed -n '2p')

# 第三行 → PSU2
PSU2_LINE=$(echo "$PSU_RAW" | sed -n '3p')

# 默认正常
PSU1_STATUS=1
PSU2_STATUS=1

# 只要出现 AC lost 就认为异常
echo "$PSU1_LINE" | grep -qi "AC lost" && PSU1_STATUS=0
echo "$PSU2_LINE" | grep -qi "AC lost" && PSU2_STATUS=0


# ================= 简单校验 =================
if [[ -z "$CPU1" || -z "$CPU2" || -z "$INLET" || -z "$EXHAUST" ]]; then
    echo "Temperature read failed"
    exit 1
fi

# ================= 写入数据库 =================
/usr/bin/mysql -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" <<EOF
INSERT INTO server_status
(hostname, ip_addr, cpu1_temp, cpu2_temp, inlet_temp, exhaust_temp, psu1_status, psu2_status)
VALUES
('$HOSTNAME', '$IP_ADDR', $CPU1, $CPU2, $INLET, $EXHAUST, $PSU1_STATUS, $PSU2_STATUS);
EOF

# ================= 当前时间 =================
CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S")

# ================= 钉钉告警逻辑 =================
# 温度告警
if (( $(echo "$INLET >= $ALERT_TEMP" | /usr/bin/bc -l) )); then
    /usr/bin/php /root/sendmess2dd.php "⚠️ [${CURRENT_TIME}] 机房温度告警:温度 ${INLET}℃"
    [ -f "$RECOVER_FLAG" ] && rm -f "$RECOVER_FLAG"
else
    if [ ! -f "$RECOVER_FLAG" ]; then
        /usr/bin/php /root/sendmess2dd.php "✅ [${CURRENT_TIME}] 温机房度恢复正常:温度 ${INLET}℃"
        touch "$RECOVER_FLAG"
    fi
fi

# 电源告警:只要有一个 PSU 0 就告警
if (( PSU1_STATUS==0 || PSU2_STATUS==0 )); then
    if [ ! -f "$PSU_ALERT_FLAG" ]; then
        /usr/bin/php /root/sendmess2dd.php "⚠️ [${CURRENT_TIME}] 机房电源故障: PSU1=${PSU1_STATUS}, PSU2=${PSU2_STATUS}"
        touch "$PSU_ALERT_FLAG"
    fi
else
    # PSU 恢复
    if [ -f "$PSU_ALERT_FLAG" ]; then
        /usr/bin/php /root/sendmess2dd.php "✅ [${CURRENT_TIME}] 机房电源恢复正常"
        rm -f "$PSU_ALERT_FLAG"
    fi
fi

2. 钉钉推送组件

使用 PHP 处理 Webhook 请求,更加稳定且方便处理 JSON 数据格式。

[root@gateway-hr ~]# cat sendmess2dd.php
<?php
// 钉钉机器人的 Webhook 地址,access_token改成你自己的
$webhookUrl = 'https://oapi.dingtalk.com/robot/send?access_token=bb8cd5b494bd9197636a49f27530cf85a2ad865c9cXXXXXXXXXXXXXX';

// 钉钉机器人安全关键词
$keyword = 'cqxbot';

if ($argc < 2) {
    fwrite(STDERR, "用法: php sendmess2dd.php \"消息内容\"\n");
    exit(1);
}

$content = $keyword . ' ' . $argv[1];


// =========================
// 2️⃣ 组装消息
// =========================
$message = [
    'msgtype' => 'text',
    'text' => [
        'content' => $content,
    ],
];

// 转换为 JSON
$messageJson = json_encode($message, JSON_UNESCAPED_UNICODE);

// =========================
// 3️⃣ 发送请求
// =========================
$ch = curl_init($webhookUrl);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json;charset=utf-8']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $messageJson);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

// 输出结果(方便 shell 判断)
echo $response;

三、 关键技术点解析

1. 高效的数据提取

脚本中使用了 awk 配合正则表达式处理 ipmitool 的输出。例如: $1~/Inlet Temp/{ gsub(/ /,"",$2); print $2 } 这种方式比多次调用 grep 效率更高,能够一次性完成字段匹配和空格清洗。

2. 告警风暴抑制

为了防止在临界点(如温度在 28.9℃ 和 29.0℃ 之间波动)产生大量重复垃圾信息,脚本引入了 Flag 标记机制

  • 温度告警:超过阈值立即告警并删除恢复标记。
  • 恢复逻辑:只有当系统处于“已告警”状态且当前数值回归正常时,才发送一次恢复通知。

3. 电源状态数字化

服务器电源通常是冗余的。脚本将字符串形式的 ok 状态转换为数据库友好的 1(正常)和 0(故障),这不仅方便 SQL 统计,也简化了 Shell 的条件判断。


四、 部署建议

1. 数据库准备

在 MySQL 中创建对应的表结构,以便脚本记录:

CREATE DATABASE server;
USE server;

CREATE TABLE server_status (
    id INT AUTO_INCREMENT PRIMARY KEY,
    hostname VARCHAR(50),
    ip_addr VARCHAR(20),
    cpu1_temp INT,
    cpu2_temp INT,
    inlet_temp INT,
    exhaust_temp INT,
    psu1_status TINYINT,
    psu2_status TINYINT,
    collect_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2. 定时任务配置

建议每 1-5 分钟运行一次。执行 crontab -e

# 每3分钟检查一次硬件状态
*/3 * * * * /bin/bash /root/collect_server_status.sh > /dev/null 2>&1

五、 总结与展望

这套方案的优点在于轻量零成本,不需要部署复杂的 Zabbix 或 Prometheus 即可实现核心硬件的实时监控。

青兴

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注