跳转至

Windows 服务器监控实战:wmi_exporter + Prometheus + Grafana 完整落地链路 + 5 个真实故障

适用场景:需要把 Windows 服务器(IIS、SQL Server、文件服务器、域控)接入 Prometheus 监控体系的团队。一句话结论:Windows 接入的难点不在 Prometheus,而在 wmi_exporter 的 collector 开关、服务权限和 9182 端口连通性。


干运维这些年,我的 Prometheus 里清一色是 Linux 节点,Windows 服务器一直是"监控黑户"。不是不想接,是每次查资料都被一句"装个 wmi_exporter 就行"打发,等真装完,发现端口不通、指标为空、告警乱报——中文圈把 Windows 监控讲得太薄了,真正能落地的链路没人给全。

这周我把三台 Windows 服务器(一台 IIS 应用、一台 SQL Server、一台文件服务器)接进了监控。踩了五个坑,全部记下来。这篇不是"安装教程",是"从零到告警生效"的完整链路,外加 5 个我亲手处理的真实故障。

一、为什么 Windows 是监控体系的"失管区"

先说痛点。Linux 有 node_exporter,文档多、教程全,接进 Prometheus 十分钟搞定。Windows 呢?wmi_exporter 能装,但它依赖 WMI(Windows Management Instrumentation),WMI 查询是要吃系统资源的——不是你想开哪个 collector 就能开哪个。

更麻烦的是三点:

  1. Windows 防火墙默认拦入站,9182 端口装上也不通
  2. 服务账户权限:wmi_exporter 默认用 LocalSystem,访问某些 WMI 类(比如 IIS、MSFT_* 类)会因权限不足返回空
  3. collector 开关全在注册表,不是改个配置文件就完事,很多人装完默认状态跑,结果 process 类指标全没有

我见过最典型的翻车:同事装完 wmi_exporter,curl localhost:9182/metrics 有输出,高兴地加进 Prometheus,结果 Grafana 面板全是 No data——因为看板模板要的 windows_* 指标,默认 collector 压根没采集。安装完成只是 5%,剩下 95% 在下面。

二、wmi_exporter 与 collector 清单:哪些默认开、哪些有坑

wmi_exporter 的 collector 是按需加载的,默认只开一小部分。先记清楚这张表,后面所有配置都围绕它:

Collector 默认 采集内容 性能/权限坑
cpu 开 CPU 各模式时间 无
cs 开 计算机系统(主机名、核数) 无
logical_disk 开 磁盘空间、队列、读写时间 含空闲盘/虚拟盘,易误报
net 开 网络收发字节/包 无
os 开 内存可用、系统信息 无
service 开 服务运行状态 默认全量服务,量大
system 开 进程数、线程数、运行时长 无
process 关 按进程:句柄、线程、CPU 高基数元凶,慎开
iis 关 IIS 站点请求、流量 需管理员权限 + IIS 已装
msft_* 关 Hyper-V/存储等 MSFT 类 需显式开启 + 权限

关键认知:process 和 iis 默认是关的。process 关是因为它按进程粒度输出几十个指标,一台 500 进程的服务器能产生上万时间序列,Prometheus 内存直接飙升(见故障五);iis 关是因为它要求管理员权限且有额外开销。默认开的那 7 个够看主机健康,别一上来全开。

三、安装方式一:MSI 安装 + 服务配置

MSI 是官方推荐的装法,服务由安装器自动注册。但很多人装完发现指标不全,因为collector 要在安装时通过属性传进去:

# 下载后执行(0.16.0 为例,按实际版本改)
wmi_exporter-0.16.0-amd64.msi ^
  ENABLED_COLLECTORS="cpu,cs,logical_disk,net,os,service,system" ^
  IIS_ENABLED="true"

注意 ENABLED_COLLECTORS 是逗号分隔、不含空格。漏掉 iis 或 process 就是默认状态,后面指标缺一截。

装完验证服务与端口:

sc.exe query wmi_exporter        # 服务状态应为 RUNNING
Get-NetTCPConnection -LocalPort 9182 -State Listen   # 应有一条 LISTEN
Invoke-WebRequest http://localhost:9182/metrics | Select-Object -First 5

如果服务没起或端口没监听,查两个地方:一是服务账户(默认 LocalSystem,别乱改),二是注册表 collector 配置:

# 查看已启用的 collector(注册表是 wmi_exporter 的"配置文件")
reg query "HKLM\SOFTWARE\wmi_exporter" /v ENABLED_COLLECTORS

改 collector 后要重启服务才生效:

reg add "HKLM\SOFTWARE\wmi_exporter" /v ENABLED_COLLECTORS /t REG_SZ ^
  /d "cpu,cs,logical_disk,net,os,service,system,process" /f
Restart-Service wmi_exporter

四、安装方式二:zip 解压 + NSSM 自建服务

不想装 MSI、或要绿色部署时,用 zip 版 + NSSM。zip 版不会自动建服务,这是最大的坑——很多人解压直接跑 wmi_exporter.exe,一关窗口进程就没了。

# 1. 解压到 C:\wmi_exporter,用 NSSM 注册为服务
C:\nssm\nssm.exe install wmi_exporter "C:\wmi_exporter\wmi_exporter.exe"

# 2. collector 通过启动参数传入(zip 版没有安装属性,只能走参数)
C:\nssm\nssm.exe set wmi_exporter AppParameters ^
  "--collectors.enabled=cpu,cs,logical_disk,net,os,service,system"

# 3. 启动并设为开机自启
C:\nssm\nssm.exe start wmi_exporter
C:\nssm\nssm.exe set wmi_exporter Start SERVICE_AUTO_START

如果不想用 NSSM,sc.exe create 也能建,但 NSSM 的好处是崩溃自动重启,监控进程不能裸奔。用 sc.exe 建的话记得加 binPath 并配好恢复选项。

一个对比结论:批量部署建议 MSI(能用 GPO/脚本推、自带服务),单机或测试环境用 zip + NSSM(可控、可带参数)。两者采集能力完全一样,别纠结。

五、防火墙放行 9182:从本机通到远端通

本机 curl localhost:9182 通,不代表远端能通。Windows 防火墙默认拦入站,这是"装完不通"的第一大元凶:

# 放行 9182 入站(按 TCP 端口)
netsh advfirewall firewall add rule name="wmi_exporter" ^
  dir=in action=allow protocol=TCP localport=9182

# 放行指定程序(更精确,但注意 exe 路径)
netsh advfirewall firewall add rule name="wmi_exporter_exe" ^
  dir=in action=allow program="C:\wmi_exporter\wmi_exporter.exe"

两个坑:一是如果机器在域环境,除了本机防火墙,可能还有域防火墙策略在中间拦;二是防火墙有"域/专用/公用"三个配置文件,规则要加到对应配置文件(profile=domain,private)。验证从 Prometheus 服务器侧:

# 在 Prometheus 所在机器执行(换成 Windows 服务器 IP)
Test-NetConnection 192.168.1.10 -Port 9182

TcpTestSucceeded : True 才是真的通。不通就 netsh advfirewall firewall show rule name="wmi_exporter" 查规则是否在正确 profile。

六、Prometheus 抓取配置

连通后写进 prometheus.yml。Windows 和 Linux 的抓取大同小异,但建议给 Windows 单独一个 job 并加标签,方便区分:

scrape_configs:
  - job_name: windows-servers
    scrape_interval: 15s
    static_configs:
      - targets:
          - '192.168.1.10:9182'   # IIS 应用
          - '192.168.1.11:9182'   # SQL Server
          - '192.168.1.12:9182'   # 文件服务器
        labels:
          os: 'windows'
          env: 'prod'
    relabel_configs:
      # 把 IP 单独抽成 instance,方便 Grafana 按主机过滤
      - source_labels: [__address__]
        regex: '([^:]+):.*'
        target_label: instance
        replacement: '${1}'

改完 prometheus.yml 用 promtool check config 校验,再热加载:

promtool check config prometheus.yml
curl -X POST http://localhost:9090/-/reload

验证抓取是否成功:

# 确认 target 是 UP
curl 'http://localhost:9090/api/v1/targets' | grep -o '"health":"[a-z]*"' | sort | uniq -c
# 确认 windows_ 指标进来了
curl 'http://localhost:9090/api/v1/query?query=up{job="windows-servers"}'

七、Grafana 看板落地

指标进来了,接下来是看板。别手搓,直接导入社区模板,这是效率最高的路:

  1. Grafana → Dashboards → Import → 输入模板 ID(搜 "Windows")
  2. 选数据源(你的 Prometheus)
  3. 右上角选 instance,切到对应 Windows 主机

最大的坑:社区多数 Windows 模板(如 10467)默认匹配 node_* 前缀,而 wmi_exporter 输出的是 windows_*。导入后如果面板 No data,去面板的 Query 里把 node_ 批量替换成 windows_。我自己做的四个核心面板:

面板 PromQL(替换前缀后)
CPU 使用率 100 - avg(rate(windows_cpu_time_total{mode="idle"}[5m])) * 100
内存可用 % windows_os_physical_memory_free_bytes / windows_os_visible_memory_bytes * 100
磁盘使用 % (windows_logical_disk_size_bytes - windows_logical_disk_free_bytes) / windows_logical_disk_size_bytes * 100
网络流量 sum(rate(windows_net_bytes_received_total[5m]) + rate(windows_net_bytes_sent_total[5m]))

八、Windows 关键指标表 + PromQL

监控 Windows,先分清"哪些指标真正反映故障"。附一张我用得最顺手的表,每行给 PromQL:

监控项 指标 PromQL 正常参考
CPU cpu_time_total 100 - avg(rate(windows_cpu_time_total{mode="idle"}[5m])) * 100 < 80%
内存可用字节 os_physical_memory_free_bytes windows_os_physical_memory_free_bytes > 总内存 20%
磁盘队列 logical_disk_queue_length windows_logical_disk_queue_length 持续 > 2 警惕
磁盘忙时比 logical_disk_*_seconds_total avg(rate(windows_logical_disk_read_seconds_total[5m]) + rate(windows_logical_disk_write_seconds_total[5m])) < 80%
句柄数 process_handles sum(windows_process_handles) 看趋势
进程数 system_processes windows_system_processes 看趋势
网络流量 net_bytes_*_total rate(windows_net_bytes_received_total[5m]) + rate(windows_net_bytes_sent_total[5m]) 按带宽

一个关键提醒:Windows 没有 Linux 那种"load average",取而代之的是 windows_system_processor_queue_length(处理器队列长度)。队列长度持续超过核数,才是 Windows 的"CPU 排队"信号,别拿 Linux 的 load 套。

九、告警规则:Windows 专用 PromQL

告警规则和 Linux 类似,但阈值和采集口径要按 Windows 调。尤其磁盘,Windows 的 logical_disk 会包含虚拟盘、只读盘,直接 queue_length > 2 会误报(见故障四):

groups:
  - name: windows-server
    rules:
      - alert: WindowsCpuHigh
        expr: 100 - avg(rate(windows_cpu_time_total{mode="idle"}[5m])) * 100 > 90
        for: 5m
        labels: { severity: critical }
        annotations:
          summary: "Windows CPU 过高 {{ $labels.instance }}"

      - alert: WindowsMemoryLow
        expr: windows_os_physical_memory_free_bytes / windows_os_visible_memory_bytes * 100 < 10
        for: 10m
        labels: { severity: warning }
        annotations:
          summary: "Windows 内存可用低于 10% {{ $labels.instance }}"

      - alert: WindowsDiskQueueHigh
        expr: |
          windows_logical_disk_queue_length{volume=~"C:.*"}
            and on(instance, volume)
          windows_logical_disk_idle_seconds_total{volume=~"C:.*"} == 0
        for: 5m
        labels: { severity: warning }
        annotations:
          summary: "C 盘磁盘队列过长 {{ $labels.instance }}"

      - alert: WindowsServiceDown
        expr: windows_service_state{state="stopped"} == 1
        for: 3m
        labels: { severity: critical }
        annotations:
          summary: "Windows 服务已停止 {{ $labels.service }}"

注意磁盘那条:volume=~"C:.*" 先过滤只监控 C 盘,idle_seconds_total == 0 表示盘忙。两个条件用 and 连,避免空闲盘或只读盘的假队列触发告警——这是我被误报折磨后的血泪总结。

十、5 个真实故障案例

故障一:MSI 装完,9182 端口不通

现象:MSI 安装显示成功,sc.exe query wmi_exporter 是 RUNNING,但 Prometheus 抓不到,Test-NetConnection -Port 9182 返回 False。

排查链:查防火墙规则 → 无入站规则;查服务 → 正常;查进程监听 → Get-NetTCPConnection 无 9182 LISTEN。

根因:Windows 防火墙默认没有放行 9182。MSI 安装器不会自动加防火墙规则,入站被拦。

修复:加 netsh advfirewall 规则(见第五节),Test-NetConnection 变 True。

经验:Windows 装完任何监听端口服务,第一件事永远先放行防火墙,别急着改 Prometheus。

故障二:防火墙放行了,远端还是不通

现象:本机 curl localhost:9182 通,加完防火墙规则后,从 Prometheus 机器 Test-NetConnection 仍 False。

排查链:查本机防火墙 profile → 规则加到了 domain,但当前网络是 private;再查域策略 → 上级 GPO 有一条阻止 9182。

根因:域环境的防火墙是"双层"。本机规则配对了,但域组策略在域控层又拦了一道;且防火墙规则默认只对当前 profile 生效,网络配置文件切换后规则不匹配。

修复:把规则同时加到 profile=domain,private,并协调域管理员在 GPO 里放行,或改走 HTTPS/代理。

经验:域内 Windows,先确认"网络配置文件是哪个"(Get-NetConnectionProfile 看 NetworkCategory),再决定规则加哪个 profile。

故障三:Windows 时间跳变,rate() 飙高误报

现象:凌晨告警系统突然收到 CPU 100% 告警,但 windows_cpu_time_total 看值正常,告警却持续。

排查链:查 rate() 计算 → 时间差为负;查系统时钟 → 与 NTP 相差 3 分钟;查告警触发时间 → 恰在时间校正后。

根因:Windows 时钟未同步,NTP 校正时时间跳变。Prometheus 的 rate() 用时间戳差值算速率,时钟跳变会让采样间隔异常,rate() 瞬间虚高甚至为负,触发误报。

修复:配置可靠 NTP 源并启用同步(w32tm /config /manualpeerlist:"ntp.aliyun.com" /syncfromflags:manual /reliable:yes,w32tm /resync),告警规则加 for: 5m 缓解瞬时跳变。

经验:Windows 服务器时间不同步是"隐性监控杀手"。加监控前先 w32tm /query /status 确认时钟正常,否则 rate 类指标全不可信。

故障四:磁盘队列告警天天误报

现象:WindowsDiskQueueHigh 告警每天触发,但 C 盘 queue_length 看其实不高,业务也无异常。

排查链:看告警时的 queue_length 值 → 来自 D 盘(备份盘);看该盘 idle_seconds_total → 长期为 0;看挂载类型 → 是网络映射盘。

根因:logical_disk collector 默认扫全部盘,包括网络映射、只读、虚拟盘。这些盘的队列指标长期异常,而原始告警没过滤盘符,把非系统盘也纳进来了。

修复:告警只对 C:.*(或业务盘)生效(见第九节),并在 Grafana 面板上 regex 过滤掉映射盘。

经验:Windows 磁盘监控必须先"选盘"再"设阈值",别默认全盘告警。Linux 可以全盘,Windows 不行——盘太多了。

故障五:process collector 开启后 Prometheus 内存暴涨

现象:为了排查某进程内存,我给文件服务器开启了 process collector,第二天 Prometheus 容器内存从 2G 涨到 9G,告警查询明显变慢。

排查链:查 Prometheus 内存 → rate(process_resident_memory_bytes) 持续涨;查该服务器 windows_process_handles 序列数 → 几万条;看进程数 → 500+ 进程。

根因:process collector 是"高基数"重灾区。一台 500 进程的服务器,每个进程输出句柄、线程、工作集等几十个指标,瞬时产生数万时间序列。Prometheus 内存随序列数线性增长,直接被打爆。

修复:临时排查完立刻关闭 process collector,恢复默认 7 个;若必须长期开,用 --collectors.process.include 只匹配关键进程(如 sqlservr,msftesql),把序列数压下来。

经验:任何"按进程/按标签"的 collector 都要评估基数。Windows 进程多,process 类指标是"能不开就不开、要开必须过滤"的典型。

FAQ

Q1:wmi_exporter 的 MSI 装完 9182 端口不通,最常见原因是什么?

A1:八成是防火墙。9182 是 TCP 入站,Windows 防火墙默认拦截,MSI 不会自动放行,需手动 netsh advfirewall 加规则。其次是服务没起——查 sc.exe query wmi_exporter,若 RUNNING 但无 9182 监听,再查注册表 ENABLED_COLLECTORS 是否配置完整。

Q2:process collector 要不要默认开启?

A2:不建议。它按进程粒度输出几十个指标,一台几百进程的服务器会产生数万时间序列,Prometheus 内存暴涨、查询变慢。只在定位单进程时临时开,长期开必须用 --collectors.process.include 过滤关键进程。

Q3:磁盘队列告警老误报,为什么?

A3:logical_disk 默认扫全部盘,含网络映射、只读、虚拟盘。用 volume=~"C:.*" 过滤只监控业务盘,并配合 idle_seconds_total == 0(盘忙)做 and 条件,再加 for: 5m 持续时长,误报基本消除。

Q4:Windows 时间跳变会影响监控吗?

A4:会,且隐蔽。wmi_exporter 采集依赖系统时钟,rate() 用时间差算速率,NTP 未同步导致时钟跳变会让 rate 瞬间飙高或为负,触发误报。先用 w32tm /query /status 确认同步,加 for 持续时长缓解瞬时跳变。

Q5:Grafana 有没有现成的 Windows 看板?

A5:有。官方模板搜 "Windows",常用 10467。但多数模板默认匹配 node_* 前缀,wmi_exporter 输出 windows_*,导入后要在 Query 里批量替换前缀,否则面板 No data。


相关文章