Server Telemetry Deployment

上一篇总结了SONiC Switch Telemetry的方法:Sonic Telemetry Deployment,由于测试RDMA时不仅需要监控交换机,还要监控服务器,因此此篇就是在原有架构基础之上融入Server测的相关监控。Server测主要通过node exporter采集本地信息,然后吐给Prometheus,本地信息主要包含如下信息:

  • 支持RDMA相关的各种硬件Counter信息(CNP/ECN、重传、QP 状态等),专有计数
  • 支持Ethtools中Per-Priority相关的counter,这个是通用计数

另外更新之前自动生成配置的shell脚本,覆盖服务器这边的配置。

架构概览

整体架构图

┌─────────────────────────────────────────────────────────────────────────────┐
│                              Server Telemetry                               │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                    auto-refresh.sh v3                               │    │
│  │  ┌──────────────┐              ┌──────────────┐                     │    │
│  │  │ switches.conf│              │ servers.conf │  ← new              │    │
│  │  │ switch list  │              │ server list  │                     │    │
│  │  └──────┬───────┘              └──────┬───────┘                     │    │
│  │         ▼                             ▼                             │    │
│  │  ┌──────────────────────────────────────────────────────────┐       │    │
│  │  │              prometheus.yml (auto generated)             │       │    │
│  │  │  ┌────────────────────┬────────────────────────────┐     │       │    │
│  │  │  │ job: gnmic         │ job: rdma_servers          │     │       │    │
│  │  │  │ (switch metric)    │ (server RDMA metric) ← new │     │       │    │
│  │  │  └────────────────────┴────────────────────────────┘     │       │    │
│  │  └──────────────────────────────────────────────────────────┘       │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                          │
│  │   gNMIc     │  │ Prometheus  │  │  Grafana    │                          │
│  │   :9804     │→ │   :9090     │→ │   :3000     │                          │
│  └──────┬──────┘  └──────┬──────┘  └─────────────┘                          │
└─────────┼────────────────┼──────────────────────────────────────────────────┘
          │ gNMI           │ HTTP scrape
          ▼                ▼
┌─────────────────┐  ┌───────────┐
│  SONiC Switch   │  │RDMA Server│
│  :8080 (gNMI)   │  │┌─────────┐│
└─────────────────┘  ││ node_   ││
                     ││ exporter││
                     ││ :9100   ││
                     │└────┬────┘│
                     │     │     │
                     │┌────▼────┐│
                     ││ textfile││
                     ││ rdma.   ││
                     ││ prom    ││
                     │└────┬────┘│
                     │     │     │
                     │┌────▼────┐│
                     ││ rdma_   ││
                     ││collector││
                     ││ (1s)    ││
                     │└─────────┘│
                     │     │     │
                     │┌────▼────┐│
                     ││ mlx5_0  ││
                     ││ mlx5_1  ││
                     ││ (NIC)   ││
                     │└─────────┘│
                     └───────────┘

Server架构图

┌─────────────────────────────────────────────────────────────────────────────┐
│                             RDMA Server                                     │
│  ┌─────────────────────┐    ┌─────────────────────────────────────────────┐ │
│  │  Source Data1: RDMA │    │              node_exporter (:9100)          │ │
│  │                     │    │  ┌─────────────────────────────────────┐    │ │
│  │ /sys/class/         │    │  │  textfile collector                 │    │ │
│  │   infiniband/       │───▶│  │  /var/lib/node_exporter/textfile/   │    │ │
│  │     mlx5_x/ports/1/ │    │  │    └── rdma.prom                    │    │ │
│  │       ├── counters/ │    │  └─────────────────────────────────────┘    │ │
│  │       └── hw_counters/   │                                             │ │
│  │                     │    └───▲──────────────┬──────────────────────────┘ │
│  │ rdma res show qp    │        │              │                            │
│  └─────────────────────┘        │              │ HTTP /metrics              │
│  ┌─────────────────────┐        │              │                            │
│  │ Source Data2: ethtool        │              │                            │
│  │                     │────────┘              │                            │
│  │ ethtool -S {iface}  │                       │                            │
│  └─────────────────────┘                       │                            │  
│  ┌─────────────────────┐                       │                            │
│  │ rdma_collector.sh   │──────────────────────▶│                            │
│  │ (systemd timer 1s)  │   write to .prom      │                            │
│  └─────────────────────┘                       │                            │
└────────────────────────────────────────────────┼────────────────────────────┘
                                                 │
                                                 ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           Prometheus Server                                 │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  prometheus.yml                                                     │    │
│  │  - job_name: 'rdma_servers'                                         │    │
│  │    scrape_interval: 1s                                              │    │
│  │    static_configs:                                                  │    │
│  │      - targets: ['10.26.4.91:9100', '10.26.4.92:9100', ...]         │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                    │                                        │
│                                    ▼                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  Prometheus TSDB                                                    │    │
│  │  - Store all metrics                                                │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
└────────────────────────────────────────────────┬────────────────────────────┘
                                                 │
                                                 ▼ PromQL Search
┌─────────────────────────────────────────────────────────────────────────────┐
│                              Grafana                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  Server RDMA Monitor Dashboard                                      │    │
│  │  - var: $server, $device                                            │    │
│  │  - pannel: QP, CNP, ECN, Retran, Traffics...                        │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘

数据源与指标

指标分类

指标类型数据源Prometheus 指标名说明
端口计数器/sys/class/infiniband/{dev}/ports/{port}/counters/rdma_port_counterTX/RX 字节、包数、错误
硬件计数器/sys/class/infiniband/{dev}/ports/{port}/hw_counters/rdma_hw_counterCNP、ECN、重传等
QP 统计rdma res show qprdma_qp_count活跃 QP 数量
Priority 流量ethtool -S {iface}nic_prio_*按优先级统计的流量
PFC 统计ethtool -S {iface}nic_pfc_*按优先级统计PFC

关键硬件计数器-拥塞相关

counter 名称含义增长原因
np_cnp_sent本机作为 NP 发出的 CNP 包数收到 ECN 标记的数据包
np_ecn_marked_roce_packets收到的被 ECN 标记的 RoCE 包数路径上有交换机拥塞
rp_cnp_handled本机作为 RP 收到并处理的 CNP 包数本机发送导致拥塞,收到 CNP 后降速
rp_cnp_ignored收到但忽略的 CNP 包数QP 不存在或 CNP 格式错误

关键硬件计数器-重传相关

counter 名称含义增长原因
roce_adp_retransRoCE 自适应重传次数检测到丢包
roce_adp_retrans_to重传超时次数ACK 超时,严重问题
roce_slow_restart慢启动重传次数连续多次重传失败

关键硬件计数器-乱序与错误

counter 名称含义增长原因
out_of_sequence收到的乱序包数量ECMP 多路径或路由问题
packet_seq_err包序列号错误丢包后收到后续包
out_of_buffer接收缓冲区溢出接收端处理不过来(严重),RQ WQE 耗尽,应用层丢包
rnr_nak_retry_errRNR NAK 重试错误接收端没有准备好 WQE
local_ack_timeout_err本地 ACK 超时对端无响应或链路故障
duplicate_request收到的重复请求ACK 丢失导致重传

注:下面是乱序错误Counter产生的顺序展示,packet_seq_err 就是 PSN NAK 导致的结果,只是计数器名字不叫 NAK:

接收端检测乱序 → out_of_sequence++
         ↓
    发送 PSN NAK
         ↓
发送端收到 NAK → packet_seq_err++
         ↓
    触发重传 → roce_adp_retrans++

Per-Priority 流量

指标名增长原因
nic_prio_rx_bytes{interface, priority}按优先级接收字节数
nic_prio_tx_bytes{interface, priority}按优先级发送字节数
nic_prio_rx_packets{interface, priority}按优先级接收包数
nic_prio_tx_packets{interface, priority}按优先级发送包数

Per-Priority PFC

指标名增长原因
nic_pfc_rx_pause{interface, priority}按优先级接收PFC
nic_pfc_rx_pause{interface, priority}按优先级发送PFC
nic_pfc_rx_pause_duration{interface, priority}按优先级接收PFC pause duration
nic_pfc_tx_pause_duration{interface, priority}按优先级发送PFC pause duration

PFC pause和pause duration单位和量级不一样,不建议在grafana中展示在一个pannel中。另外服务器主动发送PFC的场景如下:

  • PCIe 瓶颈(NUMA 不对齐)
  • QP/应用层 post WQE 速度不足
  • IRQ 处理不及时

Discards

指标名增长原因
nic_prio_rx_discards{interface, priority}按优先级统计RX Discards,per-priority reserved buffer 耗尽后的丢包,是 HRM 被打穿的体现
nic_prio_rx_buf_discard{interface, priority}按优先级统计RX Buf Discards,per-priority shared buffer 耗尽后的丢包
nic_rx_discards_phy{interface}按端口统计FEC 后 RX Discards
nic_tx_discards_phy{interface}按端口统计TX Buffer Overload导致的丢包

QP 统计 (rdma_qp_count)

标签说明
deviceRDMA 设备名 (mlx5_0, mlx5_1)
stateQP 状态 (RTS, RTR, INIT, ERR, total)

QP 状态说明:

  • RTS (Ready To Send): 正常工作状态,可以收发数据
  • RTR (Ready To Receive): 只能接收
  • INIT: 初始化中
  • ERR: 错误状态
  • total: 总数

服务器端部署

安装Node Exporter

cd /opt
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xvf node_exporter-1.7.0.linux-amd64.tar.gz
mv node_exporter-1.7.0.linux-amd64 node_exporter
mkdir -p /var/lib/node_exporter/textfile

创建采集脚本

cat > /opt/node_exporter/rdma_collector.sh << 'EOF'
#!/bin/bash
OUTPUT="/var/lib/node_exporter/textfile/rdma.prom"
TEMP="${OUTPUT}.tmp"
{
    # ============================================================
    # RDMA Port Counters
    # ============================================================
    echo "# HELP rdma_port_counter RDMA port counter"
    echo "# TYPE rdma_port_counter counter"
    echo "# HELP rdma_hw_counter RDMA hardware counter"
    echo "# TYPE rdma_hw_counter counter"
    
    for dev in /sys/class/infiniband/*; do
        [ -d "$dev" ] || continue
        devname=$(basename "$dev")
        
        for port in "$dev"/ports/*; do
            [ -d "$port" ] || continue
            portnum=$(basename "$port")
            
            # 批量读取 counters
            for counter in "$port"/counters/*; do
                [ -f "$counter" ] || continue
                name=$(basename "$counter")
                value=$(cat "$counter" 2>/dev/null)
                [[ "$value" =~ ^[0-9]+$ ]] && echo "rdma_port_counter{device=\"$devname\",port=\"$portnum\",counter=\"$name\"} $value"
            done
            
            # 批量读取 hw_counters
            for counter in "$port"/hw_counters/*; do
                [ -f "$counter" ] || continue
                name=$(basename "$counter")
                value=$(cat "$counter" 2>/dev/null)
                [[ "$value" =~ ^[0-9]+$ ]] && echo "rdma_hw_counter{device=\"$devname\",port=\"$portnum\",counter=\"$name\"} $value"
            done
        done
    done
    
    # ============================================================
    # QP Count
    # ============================================================
    echo ""
    echo "# HELP rdma_qp_count Number of Queue Pairs by state"
    echo "# TYPE rdma_qp_count gauge"
    
    rdma res show qp 2>/dev/null | awk '
    {
        dev=""; state=""
        for(i=1; i<=NF; i++) {
            if($i == "link") { split($(i+1), parts, "/"); dev=parts[1] }
            if($i == "state") state=$(i+1)
        }
        if(dev != "" && state != "") { count[dev","state]++; total[dev]++ }
    }
    END {
        for(k in count) { split(k, a, ","); print "rdma_qp_count{device=\""a[1]"\",state=\""a[2]"\"} "count[k] }
        for(d in total) { print "rdma_qp_count{device=\""d"\",state=\"total\"} "total[d] }
    }'

    # ============================================================
    # Per-Priority: Traffic + PFC + Discards (每网口只调用一次 ethtool)
    # ============================================================
    echo ""
    echo "# HELP nic_prio_rx_bytes Received bytes by priority"
    echo "# TYPE nic_prio_rx_bytes counter"
    echo "# HELP nic_prio_tx_bytes Transmitted bytes by priority"
    echo "# TYPE nic_prio_tx_bytes counter"
    echo "# HELP nic_prio_rx_packets Received packets by priority"
    echo "# TYPE nic_prio_rx_packets counter"
    echo "# HELP nic_prio_tx_packets Transmitted packets by priority"
    echo "# TYPE nic_prio_tx_packets counter"
    echo "# HELP nic_pfc_rx_pause PFC pause frames received by priority"
    echo "# TYPE nic_pfc_rx_pause counter"
    echo "# HELP nic_pfc_tx_pause PFC pause frames transmitted by priority"
    echo "# TYPE nic_pfc_tx_pause counter"
    echo "# HELP nic_pfc_rx_pause_duration PFC pause duration received by priority (512-bit time quanta)"
    echo "# TYPE nic_pfc_rx_pause_duration counter"
    echo "# HELP nic_pfc_tx_pause_duration PFC pause duration transmitted by priority (512-bit time quanta)"
    echo "# TYPE nic_pfc_tx_pause_duration counter"
    echo "# HELP nic_prio_rx_discards RX discards by priority"
    echo "# TYPE nic_prio_rx_discards counter"
    echo "# HELP nic_prio_rx_buf_discard RX buffer discards by priority"
    echo "# TYPE nic_prio_rx_buf_discard counter"
    echo "# HELP nic_rx_discards_phy RX physical layer discards (total) after FEC"
    echo "# TYPE nic_rx_discards_phy counter"
    echo "# HELP nic_tx_discards_phy TX buffer overload"
    echo "# TYPE nic_tx_discards_phy counter"

    for iface in $(ls /sys/class/net/ | grep -E '^(ens|eth|enp)'); do
        ethtool -S "$iface" 2>/dev/null | awk -v dev="$iface" '
        /rx_prio[0-7]_bytes:/          { print "nic_prio_rx_bytes{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /tx_prio[0-7]_bytes:/          { print "nic_prio_tx_bytes{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /rx_prio[0-7]_packets:/        { print "nic_prio_rx_packets{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /tx_prio[0-7]_packets:/        { print "nic_prio_tx_packets{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /rx_prio[0-7]_pause:/          { print "nic_pfc_rx_pause{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /tx_prio[0-7]_pause:/          { print "nic_pfc_tx_pause{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /rx_prio[0-7]_pause_duration:/ { print "nic_pfc_rx_pause_duration{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /tx_prio[0-7]_pause_duration:/ { print "nic_pfc_tx_pause_duration{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /rx_prio[0-7]_discards:/       { print "nic_prio_rx_discards{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /rx_prio[0-7]_buf_discard:/    { print "nic_prio_rx_buf_discard{interface=\""dev"\",priority=\""substr($1,8,1)"\"} "$2 }
        /rx_discards_phy:/             { print "nic_rx_discards_phy{interface=\""dev"\"} "$2 }
        /tx_discards_phy:/             { print "nic_tx_discards_phy{interface=\""dev"\"} "$2 }
        '
    done

} > "$TEMP" && mv "$TEMP" "$OUTPUT"
EOF
chmod +x /opt/node_exporter/rdma_collector.sh

创建 systemd 服务

node_exporter.service

cat > /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
After=network.target

[Service]
Type=simple
ExecStart=/opt/node_exporter/node_exporter \
    --collector.textfile.directory=/var/lib/node_exporter/textfile \
    --web.listen-address=:9100
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

rdma-collector.service

cat > /etc/systemd/system/rdma-collector.service << 'EOF'
[Unit]
Description=RDMA Metrics Collector

[Service]
Type=oneshot
ExecStart=/opt/node_exporter/rdma_collector.sh
EOF

rdma-collector.timer

cat > /etc/systemd/system/rdma-collector.timer << 'EOF'
[Unit]
Description=Run RDMA Collector every second

[Timer]
OnBootSec=5
OnUnitActiveSec=1s
AccuracySec=100ms

[Install]
WantedBy=timers.target
EOF

启动服务

systemctl daemon-reload
systemctl enable --now node_exporter
systemctl enable --now rdma-collector.timer

验证

# 检查采集时间(应 < 0.3s)
time /opt/node_exporter/rdma_collector.sh

# 检查数据
cat /var/lib/node_exporter/textfile/rdma.prom | head -30

# 检查 node_exporter 暴露的指标
curl -s http://localhost:9100/metrics | grep -E "^(rdma_|nic_prio)" | head -20

# 检查服务状态
systemctl status node_exporter
systemctl status rdma-collector.timer

Prometheus 配置

在 prometheus.yml 中添加

scrape_configs:
  - job_name: 'rdma_servers'
    scrape_interval: 1s
    static_configs:
      - targets:
        - '10.26.4.91:9100'
        - '10.26.4.92:9100'
        - '10.26.4.93:9100'
        # ... 添加更多服务器
        labels:
          group: 'rdma_servers'
    relabel_configs:       # ← 追加在这里,过滤掉:9100
      - source_labels: [__address__]
        regex: '(.*):9100'
        target_label: instance
        replacement: '$1'

重启 Prometheus并验证

docker compose restart prometheus
# 或
systemctl restart prometheus
# 检查 target 状态
curl -s "http://localhost:9090/api/v1/targets" | jq '.data.activeTargets[] | select(.job=="rdma_servers") | {instance, health}'

# 测试查询
curl -s "http://localhost:9090/api/v1/query?query=rdma_qp_count" | jq '.data.result[0]'

Grafana Dashboard

Dashboard 变量

变量名标签作用影响的面板
$serverServer过滤服务器所有面板
$deviceRDMA Device过滤 mlx5_0/mlx5_1CNP/ECN/Retrans/QP
$interfaceNIC Interface过滤网卡Per-Priority 流量
$priorityPriority过滤优先级 0-7Per-Priority 流量

面板布局

┌─────────────────────────────────────────────────────────────────────┐
│ Row 1: QP Overview                                                  │
│ ┌──────────────────────┐ ┌──────────────────────────────────────┐   │
│ │ Active QPs (Table)   │ │ QP Count Over Time (TimeSeries)      │   │
│ │ - Server             │ │                                      │   │
│ │ - Device             │ │                                      │   │
│ │ - QP Count           │ │                                      │   │
│ └──────────────────────┘ └──────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────┤
│ Row 2: Per-Priority traffics (control by $interface, $priority)     │
│ ┌────────────────────────────┐ ┌────────────────────────────────┐   │
│ │ Per-Priority RX Traffic    │ │ Per-Priority TX Traffic        │   │
│ │ (Gbps)                     │ │ (Gbps)                         │   │
│ └────────────────────────────┘ └────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────┤
│ Row 3: CNP                                                          │
│ ┌────────────────────────────┐ ┌────────────────────────────────┐   │
│ │ CNP Sent (NP → Switch)     │ │ CNP Received (RP ← Switch)     │   │
│ └────────────────────────────┘ └────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────┤
│ Row 4: ECN & Retran                                                 │
│ ┌────────────────────────────┐ ┌────────────────────────────────┐   │
│ │ ECN Marked Packets         │ │ Retransmissions                │   │
│ └────────────────────────────┘ └────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────┤
│ Row 5: Out of Sequence & NAK                                        │
│ ┌────────────────────────────┐ ┌────────────────────────────────┐   │
│ │ Out of Sequence / Errors   │ │ NAK Errors                     │   │
│ └────────────────────────────┘ └────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────┤
│ Row 6: Port Errors & CQE                                            │
│ ┌────────────────────────────┐ ┌────────────────────────────────┐   │
│ │ RDMA Port Errors           │ │ CQE Errors                     │   │
│ └────────────────────────────┘ └────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘

诊断命令

# 检查 RDMA 设备
ibstat

# 检查 QP
rdma res show qp | head -10

# 手动读取计数器
cat /sys/class/infiniband/mlx5_0/ports/1/hw_counters/np_cnp_sent

# 检查 per-priority 统计
ethtool -S ens3np0 | grep prio

# 检查 node_exporter 指标
curl -s http://localhost:9100/metrics | grep rdma_hw_counter | head -10

# 检查 Prometheus 数据
curl -s "http://<prometheus>:9090/api/v1/query?query=rdma_qp_count" | jq

文件清单

文件位置说明
rdma_collector.sh/opt/node_exporter/采集脚本
rdma.prom/var/lib/node_exporter/textfile/采集数据(自动生成)
node_exporter.service/etc/systemd/system/node_exporter 服务
rdma-collector.service/etc/systemd/system/采集服务(oneshot)
rdma-collector.timer/etc/systemd/system/定时器(1秒间隔)

注意:

  • 如需添加其他指标,编辑 /opt/node_exporter/rdma_collector.sh,在末尾添加新的采集逻辑;
  • 添加完后,需要使用 time /opt/node_exporter/rdma_collector.sh 确认下执行时间,如果执行时间超过1s,那么更新频率就需要变更,否则会失败;
本文出自 Frank's Blog

版权声明:


本文链接:Server Telemetry Deployment
版权声明:本文为原创文章,仅代表个人观点,版权归 Frank Zhao 所有,转载时请注明本文出处及文章链接
你可以留言,或者trackback 从你的网站

留言哦

blonde teen swallows load.xxx videos