1、简介

随着 AI 技术的迅猛发展,英伟达(NVIDIA)的硬件设备也成为 AI 智算中心中的核心基础设施。在这个浪潮中,NVIDIA 不仅提供强大的 GPU 计算资源,其网络产品也在高性能计算(HPC)和 AI 数据中心中发挥着重要作用,尤其是 InfiniBand 和 RoCE 交换机。

其中,InfiniBand(IB)交换机 以 NVIDIA Quantum 系列为代表,而 RoCE(基于以太网的 RDMA)交换机 以 Spectrum 系列为核心,结合 Spectrum 网络操作系统,构建了一套高性能、低延迟的网络解决方案。

本篇文章将聚焦于 NVIDIA Spectrum 以太网交换机,结合 Cumulus Linux 系统,介绍如何通过 gNMI 协议实现标准化、高精度的 Telemetry 监控采集。

2、特点

什么是 gNMI?

gNMI(gRPC Network Management Interface) 是 Google 主导、OpenConfig 推动的一种基于 gRPC 的网络管理接口,采用 YANG 数据模型进行结构定义。

Telemetry 的优势

  • 推送模式(Push Model):无需轮询,实时性强,效率高。
  • YANG 模型结构化数据:标准统一,兼容性好。
  • 高频采样:采集精度可达亚秒级别,适合高密度网络监控。
  • 支持多编码格式:如 JSON、PROTO 等。

3、系统架构

Telemetry 架构(gNMI 模式)

交换机 (gNMI Agent)
        ↓
gRPC over TLS
        ↓
Categraf gNMI 插件
        ↓
时序数据库(如 VictoriaMetrics、Prometheus)
        ↓
可视化平台(夜莺、Grafana)

Nightingale 中的部署架构

部署 Nightingale 的开源采集器 Categraf,通过 gNMI 插件从交换机拉取数据,采集到的指标写入时序数据库,再通过 Grafana 等工具展示和告警。

4、设备配置

查看设备信息

# 查看硬件型号
decode-syseeprom

# 查看系统版本
cat /etc/os-release

输出示例:

NAME="Cumulus Linux"
VERSION="5.9.1"

启用 gNMI Agent 服务

# 开机自启
sudo systemctl enable netq-agent.service

# 启动服务
sudo systemctl start netq-agent.service

# 启用 gNMI
netq config add agent gnmi-enable true

# 重启服务以生效
netq config restart agent

#查看状态
netq config status agent

使用 gnmic 工具验证 gNMI 接口连通性

# 安装
wget https://github.com/openconfig/gnmic/releases/download/v0.41.0/gnmic_0.41.0_Linux_x86_64.deb
sudo dpkg -i gnmic_0.41.0_Linux_x86_64.deb

# 测试连通性
gnmic -a172.16.10.10:9339 -u cumulus -p cumulus@2025 --skip-verify capabilities

# 示例输出包含支持的模型和编码格式

采集 LLDP 邻居设备信息

gnmic -a172.16.10.10:9339 -u ops -p Overwatch@2025 --skip-verify subscribe --path"/lldp/interfaces/interface/neighbors/neighbor/state/system-description"--stream-mode on-change --target netq

5、服务配置

Categraf 下的 config.toml 配置文件

[global]
hostname ="$ip"

...

[[writers]]
url ="http://172.17.40.192:17000/prometheus/v1/write"

...

[heartbeat]
enable =true

# 上报操作系统版本、CPU 使用率、内存使用率以及相关元数据
url ="http://172.17.40.192:17000/v1/n9e/heartbeat"

夜莺 下的 etc/config.toml 配置文件

...

[HTTP]
# http listening address
Host ="0.0.0.0"
# http listening port
Port =17000

...

[[Pushgw.Writers]]
Url ="http://172.17.40.192:8428/api/v1/write"

...

夜莺界面配置 Prometheus 数据源

6、采集规范

在使用 Categraf 下的 SNMP 插件去采集网络设备的时候,我的最佳实践建议遵循一定的规范去操作采集:

首先是配置文件 SNMP 插件采集配置文件是放在 conf/input.snmp 目录下,我们把 Categraf 默认自带的配置文件删除掉,重新新建网络设备采集配置文件,这里是有一定的规范,建议按照我提供的方式来操作:

文件名 指标类型 说明
snmp_huawei_system.toml 华为系统指标 主要采集华为设备的CPU、内存、温度、电源、风扇、启动时间等
snmp_huawei_interface.toml 华为接口指标 主要采集华为设备的接口相关指标
snmp_huawei_optical.toml 华为光模块指标 主要采集华为设备的光模块相关指标
snmp_huawei_ipsec.toml 华为IPSec指标 主要采集华为设备的IPSec VPN相关指标
snmp_h3c_system.toml 华三系统指标 主要采集华三设备的CPU、内存、温度、电源、风扇、启动时间等
snmp_h3c_interface.toml 华三接口指标 主要采集华三设备的接口相关指标
snmp_h3c_optical.toml 华三光模块指标 主要采集华三设备的光模块相关指标
snmp_h3c_bgp.toml 华三BGP指标 主要采集华三设备的BGP相关指标
snmp_h3c_ospf.toml 华三OSPF指标 主要采集华三设备的OSPF相关指标

多品牌的采集配置文件按照这样的方式来规划,把这些配置文件放入到 conf/input.snmp 目录下,然后使用的时候可以一个文件一个文件测试成功保证语法和指标数据满足需求后在开启采集:

# 比如现在完成 snmp_huawei_system.toml 采集文件的编写
# 现在保证 conf/input.snmp 目录下只有这个文件 
# 这个目录下的其他文件全部重命名非 toml 后缀或者删除
# 开启对 snmp_huawei_system.toml 测试调试
/opt/categraf/categraf --test--debug--inputs snmp

执行这个测试观察输出的信息,并不断把输出的错误解决掉即可,最终观测输出的指标是否满足需求。

7、gnmi协议

∅ /opt/categraf ❯ cat conf/input.gnmi/lldp.toml                                                             
interval =1
# gNMI telemetry 输入插件
[[instances]]
interval_times =1
#addresses = ["10.240.18.28:9339"]
addresses = [
  "172.16.10.10:9339",
]
username ="ops"
password ="Overwatch@2025"
redial ="10s"
max_msg_size =4194304
enable_tls =true
tls_ca =""
tls_min_version ="TLS12"
insecure_skip_verify =true
encoding ="proto"
# origin = ""
# prefix = "/interfaces/interface[name=swp40s0]/"
target ="netq"
canonical_field_names =false
trim_field_names =false
guess_path_tag =false

# 自发现以来的秒数
[[instances.subscription]]
name ="gnmi_lldp_state_age"
origin =""
path ="/lldp/interfaces/interface/neighbors/neighbor/state/age"
subscription_mode ="on_change"
suppress_redundant =true
heartbeat_interval ="60s"
disable_concatenation =true

# # 自上次更新以来的秒数
# [[instances.subscription]]
# name = "gnmi_lldp_state_age"
# origin = ""
# path = "/lldp/interfaces/interface/neighbors/neighbor/state/last-update"
# subscription_mode = "on_change"
# suppress_redundant = true
# heartbeat_interval = "60s"
# disable_concatenation = true

# 本端系统名称
[[instances.tag_subscription]]
name ="local_sysname"
origin =""
path ="/lldp/state/system-name"
subscription_mode ="on_change"
match ="unconditional"
suppress_redundant =true
heartbeat_interval ="60s"
disable_concatenation =true

# 本端接口名称
[[instances.tag_subscription]]
name ="local_ifname"
origin =""
path ="/lldp/interfaces/interface/state/name"
subscription_mode ="on_change"
match ="elements"
elements = ["interface"]
suppress_redundant =true
heartbeat_interval ="60s"
disable_concatenation =true

# 远端系统名称
[[instances.tag_subscription]]
name ="remote_sysname"
origin =""
path ="/lldp/interfaces/interface/neighbors/neighbor/state/system-name"
subscription_mode ="on_change"
match ="unconditional"
suppress_redundant =true
heartbeat_interval ="60s"
disable_concatenation =true

# 远端接口名称
[[instances.tag_subscription]]
name ="remote_ifname"
origin =""
path ="/lldp/interfaces/interface/neighbors/neighbor/state/port-id"
subscription_mode ="on_change"
match ="elements"
elements = ["interface"]
suppress_redundant =true
heartbeat_interval ="60s"
disable_concatenation =true

# 远端接口描述
[[instances.tag_subscription]]
name ="remote_ifdesc"
origin =""
path ="/lldp/interfaces/interface/neighbors/neighbor/state/port-description"
subscription_mode ="on_change"
match ="elements"
elements = ["interface"]
suppress_redundant =true
heartbeat_interval ="60s"
disable_concatenation =true

# 删除 name 标签
[[instances.relabel_configs]]
action ="labeldrop"
regex ="name"

夜莺数据

granafa

8、snmp协议

存放MIN文件,SNMP协议使用

# 交换机snmp目录
ops@MDC-DH1W-H05-40U-POD3-ASW-004:mgmt:~$ cd /usr/share/snmp/mibs/
ops@MDC-DH1W-H05-40U-POD3-ASW-004:mgmt:/usr/share/snmp/mibs$ ls
AGENTX-MIB.txt                  DISMAN-EVENT-MIB.txt                 IANA-RTPROTO-MIB.txt       IPV6-TC.txt                NET-SNMP-VACM-MIB.txt     SNMP-MPD-MIB.txt             SNMPv2-CONF.txt              UCD-DISKIO-MIB.txt
BGP4-MIB.txt                    DISMAN-SCHEDULE-MIB.txt              IF-INVERTED-STACK-MIB.txt  IPV6-UDP-MIB.txt           NETWORK-SERVICES-MIB.txt  SNMP-NOTIFICATION-MIB.txt    SNMPv2-MIB.txt               UCD-DLMOD-MIB.txt
BRIDGE-MIB.txt                  DISMAN-SCRIPT-MIB.txt                IF-MIB.txt                 LM-SENSORS-MIB.txt         NOTIFICATION-LOG-MIB.txt  SNMP-PROXY-MIB.txt           SNMPv2-SMI.txt               UCD-IPFWACC-MIB.txt
Cumulus-BGPVRF-MIB.txt          EtherLike-MIB.txt                    INET-ADDRESS-MIB.txt       MTA-MIB.txt                RFC1155-SMI.txt           SNMP-TARGET-MIB.txt          SNMPv2-TC.txt                UCD-SNMP-MIB.txt
Cumulus-Counters-MIB.txt        HCNUM-TC.txt                         IP-FORWARD-MIB.txt         NET-SNMP-AGENT-MIB.txt     RFC1213-MIB.txt           SNMP-TLS-TM-MIB.txt          SNMPv2-TM.txt                UDP-MIB.txt
Cumulus-POE-MIB.txt             HOST-RESOURCES-MIB.txt               IP-MIB.txt                 NET-SNMP-EXAMPLES-MIB.txt  RMON-MIB.txt              SNMP-TSM-MIB.txt             SNMP-VIEW-BASED-ACM-MIB.txt
Cumulus-Resource-Query-MIB.txt  HOST-RESOURCES-TYPES.txt             IPV6-FLOW-LABEL-MIB.txt    NET-SNMP-EXTEND-MIB.txt    SCTP-MIB.txt              SNMP-USER-BASED-SM-MIB.txt   TCP-MIB.txt
Cumulus-Sensor-MIB.txt          IANA-ADDRESS-FAMILY-NUMBERS-MIB.txt  IPV6-ICMP-MIB.txt          NET-SNMP-MIB.txt           SMUX-MIB.txt              SNMP-USM-AES-MIB.txt         TRANSPORT-ADDRESS-MIB.txt
Cumulus-Snmp-MIB.txt            IANAifType-MIB.txt                   IPV6-MIB.txt               NET-SNMP-PASS-MIB.txt      SNMP-COMMUNITY-MIB.txt    SNMP-USM-DH-OBJECTS-MIB.txt  TUNNEL-MIB.txt
Cumulus-Status-MIB.txt          IANA-LANGUAGE-MIB.txt                IPV6-TCP-MIB.txt           NET-SNMP-TC.txt            SNMP-FRAMEWORK-MIB.txt    SNMP-USM-HMAC-SHA2-MIB.txt   UCD-DEMO-MIB.txt

# 放到采集群
∅ /opt/categraf ❯ ls mibs/nvidia                                                                             
AGENTX-MIB.txt            Cumulus-Resource-Query-MIB.txt  DISMAN-SCRIPT-MIB.txt   HOST-RESOURCES-TYPES.txt             IF-MIB.txt               IPV6-MIB.txt        NET-SNMP-AGENT-MIB.txt     NET-SNMP-VACM-MIB.txt     SCTP-MIB.txt               SNMP-PROXY-MIB.txt          SNMP-USM-DH-OBJECTS-MIB.txt  SNMPv2-TM.txt                UCD-DISKIO-MIB.txt
BGP4-MIB.txt              Cumulus-Sensor-MIB.txt          ENTITY-MIB.txt          IANA-ADDRESS-FAMILY-NUMBERS-MIB.txt  INET-ADDRESS-MIB.txt     IPV6-TCP-MIB.txt    NET-SNMP-EXAMPLES-MIB.txt  NETWORK-SERVICES-MIB.txt  SMUX-MIB.txt               SNMP-TARGET-MIB.txt         SNMP-USM-HMAC-SHA2-MIB.txt   SNMP-VIEW-BASED-ACM-MIB.txt  UCD-DLMOD-MIB.txt
BRIDGE-MIB.txt            Cumulus-Snmp-MIB.txt            ENTITY-SENSOR-MIB.txt   IANAifType-MIB.txt                   IP-FORWARD-MIB.txt       IPV6-TC.txt         NET-SNMP-EXTEND-MIB.txt    NOTIFICATION-LOG-MIB.txt  SNMP-COMMUNITY-MIB.txt     SNMP-TLS-TM-MIB.txt         SNMPv2-CONF.txt              TCP-MIB.txt                  UCD-IPFWACC-MIB.txt
Cumulus-BGPVRF-MIB.txt    Cumulus-Status-MIB.txt          EtherLike-MIB.txt       IANA-LANGUAGE-MIB.txt                IP-MIB.txt               IPV6-UDP-MIB.txt    NET-SNMP-MIB.txt           RFC1155-SMI.txt           SNMP-FRAMEWORK-MIB.txt     SNMP-TSM-MIB.txt            SNMPv2-MIB.txt               TRANSPORT-ADDRESS-MIB.txt    UCD-SNMP-MIB.txt
Cumulus-Counters-MIB.txt  DISMAN-EVENT-MIB.txt            HCNUM-TC.txt            IANA-RTPROTO-MIB.txt                 IPV6-FLOW-LABEL-MIB.txt  LM-SENSORS-MIB.txt  NET-SNMP-PASS-MIB.txt      RFC1213-MIB.txt           SNMP-MPD-MIB.txt           SNMP-USER-BASED-SM-MIB.txt  SNMPv2-SMI.txt               TUNNEL-MIB.txt               UDP-MIB.txt
Cumulus-POE-MIB.txt       DISMAN-SCHEDULE-MIB.txt         HOST-RESOURCES-MIB.txt  IF-INVERTED-STACK-MIB.txt            IPV6-ICMP-MIB.txt        MTA-MIB.txt         NET-SNMP-TC.txt            RMON-MIB.txt              SNMP-NOTIFICATION-MIB.txt  SNMP-USM-AES-MIB.txt        SNMPv2-TC.txt                UCD-DEMO-MIB.txt

接口状态信息

root@MDC-DH1E-G24-13U-NetOps02:/opt/categraf/conf/input.snmp# cat snmp_nvidia_system.toml 
interval =15

# 英伟达交换机 SN 系列
[[instances]]
agents = [
    "udp://172.16.10.10",   # SN5600
]

# metrics_name_prefix = "nvidia_"

interval_times =1
labels = { region ="shanghai", role ="switch", brand ="nvidia" }

# 每个请求的超时时间
timeout ="10s"
# SNMP Agent的版本
version =2
# SNMP团体名
community ="xhs@MettaDCr"
path = ["/opt/categraf/mibs/nvidia"]
translator ="gosmi"
# 代理主机标签
agent_host_tag ="instance"
# 尝试的重试次数
retries =3
# GETBULK 每次获取的最大对象数 如果指标越多 建议数值越大 可加速采集时间
max_repetitions =15

## 系统启动时间
#[[instances.field]]
#oid = "SNMPv2-MIB::sysUpTime.0"
#name = "uptime"

# 系统名称
[[instances.field]]
oid ="SNMPv2-MIB::sysName.0"
name ="device_name"
is_tag =true

# 新建虚拟表 interface_stats 获取接口状态信息
[[instances.table]]
name ="interface_stats"
inherit_tags = ["device_name"]
index_as_tag =true

# 接口名(标签)
[[instances.table.field]]
oid ="IF-MIB::ifDescr"
name ="interface"
is_tag =true

# 接口索引
[[instances.table.field]]
oid ="IF-MIB::ifIndex"
name ="if_index"

# 接口输入错误包数
[[instances.table.field]]
oid ="IF-MIB::ifInErrors"
name ="in_error"

# 接口输出错误包数
[[instances.table.field]]
oid ="IF-MIB::ifOutErrors"
name ="out_error"

# 接口输入丢弃包数
[[instances.table.field]]
oid ="IF-MIB::ifInDiscards"
name ="in_discard"

# 接口输出丢弃包数
[[instances.table.field]]
oid ="IF-MIB::ifOutDiscards"
name ="out_discard"

# 接口输入单播包数
[[instances.table.field]]
oid ="IF-MIB::ifInUcastPkts"
name ="in_ucast_pkts"

# 接口输出单播包数
[[instances.table.field]]
oid ="IF-MIB::ifOutUcastPkts"
name ="out_ucast_pkts"

夜莺数据

语句编写

#当前设备丢包率
(
  sum(increase(snmp_interface_stats_in_discard{instance="10.240.18.28"}[$__range]))
  +
  sum(increase(snmp_interface_stats_out_discard{instance="10.240.18.28"}[$__range]))
)
/
(
  sum(increase(snmp_interface_stats_in_ucast_pkts{instance="10.240.18.28"}[$__range]))
  +
  sum(increase(snmp_interface_stats_out_ucast_pkts{instance="10.240.18.28"}[$__range]))
)
* 100

#当前设备错包率
(
  sum(increase(snmp_interface_stats_in_error{instance="10.240.18.28"}[$__range]))
  +
  sum(increase(snmp_interface_stats_out_error{instance="10.240.18.28"}[$__range]))
)
/
(
  sum(increase(snmp_interface_stats_in_ucast_pkts{instance="10.240.18.28"}[$__range]))
  +
  sum(increase(snmp_interface_stats_out_ucast_pkts{instance="10.240.18.28"}[$__range]))
)
* 100

granafa

微信打赏:

支付宝打赏: