ethtool 的 Speed 和 node_network_transmit_bytes_total指标

1. 概念与单位一键对齐

  • ethtoolSpeed: 50000Mb/s50 Gbit/s(十进制 SI,1 Mb/s=10^6 bit/s)。

  • container_network_*_bytes_total / node_network_*_bytes_total累计字节(Bytes);需用 rate()/irate() 才能得到 Bytes/s

  • 常用换算(SI)

    • 1 Byte/s = 8 bit/s
    • 1 Mb/s = 10^6 bit/s = 125,000 Byte/s
    • 50,000 Mb/s = 50 Gbit/s = 6.25 GB/s ≈ 5.82 GiB/s(GiB 为二进制 IEC,1 GiB=2^30 Bytes)
  • 对齐思路:把 Bytes/s × 8 ⇒ bits/s,即可与 ethtool 的 50 Gbit/s 直接对比;或者 Bytes/s ÷ 1e9 ⇒ GB/s 再对比 6.25 GB/s

2. 速查公式(Host/Container/RxTx)

  • 容器发送速率(Gbit/s)

    sum by (instance) (
      rate(container_network_transmit_bytes_total{pod!=""}[5m])
    ) * 8 / 1e9
    
  • 主机物理(或聚合)口发送速率(Gbit/s)

    聚合口更贴近真实链路:

    rate(node_network_transmit_bytes_total{device="bond0"}[5m]) * 8 / 1e9
    
  • 带宽利用率(%),以 50 Gbit/s 为上限

    100 * rate(node_network_transmit_bytes_total{device="bond0"}[5m]) * 8 / 50e9
    
  • 双向提示:物理带宽是 Rx 50G + Tx 50G;Rx、Tx 各自独立计算与对比。

3. 你给的 executedQueryString 的“生产版”写法

已给:

Expr: sum(irate(node_network_transmit_bytes_total{node_name="172-x-x-181",name="node_exporter", device!="lo"}[5m]))
Step: 15s

3.1 按节点聚合的“总发送速率”(转为 Gbit/s)

过滤掉虚拟口避免重复统计(veth/cni/docker/kube/br/tun/tap 等),保留物理/聚合口:

sum by (node_name) (
  irate(node_network_transmit_bytes_total{
    node_name="172-x-x-181",
    device!~"lo|veth.*|cni.*|docker.*|kube.*|br.*|tun.*|tap.*|virbr.*"
  }[5m])
) * 8 / 1e9

3.2 只看 bond0 的“链路口”速率与利用率

# 速率(Gbit/s)
irate(node_network_transmit_bytes_total{
  node_name="172-x-x-181", device="bond0"
}[5m]) * 8 / 1e9
# 利用率(%),以 50 Gbit/s 为上限
100 * irate(node_network_transmit_bytes_total{
  node_name="172-x-x-181", device="bond0"
}[5m]) * 8 / 50e9

3.3 顶级“话痨”容器(TopN)

topk(10,
  sum by (namespace, pod) (
    rate(container_network_transmit_bytes_total{pod!=""}[5m])
  ) * 8 / 1e9
)

注:

  • irate() 更灵敏,适合“秒级峰值”;rate() 更平滑,适合“趋势/告警”。你现在 Step: 15s 很合理,但窗口 [5m] 仍要 ≥ 4×抓取周期(常见 scrape_interval=15s,则 5m 足够平滑且能处理计数器回卷)。

4. Grafana 展示小贴士(直接可用)

  • 单位选择

    • 想对齐 ethtool(bit/s):表达式乘 8,单位选 bits/sec (SI);若已除以 1e9,则选 Gbits/sec
    • 想以 GB/s 展示:别乘 8,除 1e9,单位选 bytes/sec (SI)GB/s
  • 字段转换

    • 也可在 Transform → Add field from calculation 里做 *8/1e9,表达式保持 Bytes/s。
  • 阈值

    • 利用率面板设 50%/70%/90% 阈值更直观。

5. 采样与窗口的最佳实践

  • 抓取周期(scrape_interval)若为 15s,建议:

    • 面板评估步长(Step)≈ 15s~30s;
    • rate()/irate() 窗口 [3m..5m](≥ 4×scrape_interval);
    • 高频短峰用 irate(),周报/趋势用 rate()
  • 计数器回卷rate()/irate()天然处理回卷与重启;窗口过短容易抖动,过长会“磨平”突发。

6. LACP(bond4)与链路验证

  • 单流上限≈单条从口速率(例如 25G×2 的 LACP,单 TCP 流通常 ≈25G;多流可逼近 50G 总和)。

  • 聚合速率确认

    cat /proc/net/bonding/bond0
    # 看每个 Slave 的 Speed/State
    
    # 直接汇总 Slave 速度
    awk '/Speed:/{gsub(/Mb\/s/,"",$2); sum+=$2} END{print sum " Mb/s total"}' /proc/net/bonding/bond0
    
  • 压测建议iperf3 -P 8(多并发流)更接近 LACP 总带宽上限。

7. 三个“拎包即走”的面板示例

A. 主机(去虚拟口)总发送速率(Gbit/s,按节点)

sum by (node_name) (
  rate(node_network_transmit_bytes_total{
    device!~"lo|veth.*|cni.*|docker.*|kube.*|br.*|tun.*|tap.*|virbr.*"
  }[5m])
) * 8 / 1e9

B. bond0 利用率(%),以 50G 为上限

100 * rate(node_network_transmit_bytes_total{device="bond0"}[5m]) * 8 / 50e9

C. Top 10 发送最猛的 Pod(Gbit/s)

topk(10,
  sum by (namespace, pod) (
    rate(container_network_transmit_bytes_total{pod!=""}[5m])
  ) * 8 / 1e9
)

8. 常见坑位清单

  • 设备过滤node_network_* 默认包含虚拟设备,必须过滤 veth|cni|docker|kube|br|tun|tap|virbr,否则会重复/放大。
  • 容器指标差异:不同 cAdvisor/CRI 版本的标签略有出入(如 interface 标签);按需补充 interface=~"eth.*" 来剔除 lo/虛拟。
  • 双向统计:Rx/Tx 分开看;不要把 Rx+Tx 与“单向 50G 上限”比较。
  • SI/IEC 混用:图表轴单位选对(bits/sec (SI)、bytes/sec (SI)、Gbits/sec、GB/s、GiB/s),避免 “GB ≠ GiB” 造成误判。

9. 直观对比小例子

  • 如果某时段 rate(..._bytes_total[5m]) = 1.0e9 Bytes/s

    • 转 bit/s:1e9 × 8 = 8e9 bit/s = 8 Gbit/s
    • 50G 利用率:8e9 / 50e9 = 16%
    • 转 GB/s:1e9 / 1e9 = 1 GB/s;与上限 6.25 GB/s 比例亦为 1/6.25 ≈ 16%

一句话总结:始终以 Bytes/s × 8 对齐到 bit/s,再与 ethtool50 Gbit/s 上限(或 6.25 GB/s)比对;Host 用 node_network_*bond0 更贴近真实链路,容器用 container_network_* 定位“话痨”与热点命名空间;窗口 ≥ 4×抓取周期,rate 看趋势、irate 抓尖峰。

排查定位:哪个进程占用网络带宽高呢

可以!给你一套分层打法(主机 → eBPF/容器 → K8s),从“先大致锁定,再精确到进程/容器”,每步都能直接复制用。

一、主机上“先看个大概”(最快)

目标:先判断是谁在发/收,粗定位到进程/端口/远端IP。

  • 按进程看实时带宽(对单机最直接)

    # 按进程汇总带宽,2s刷新;建议对bond口/物理口执行
    sudo nethogs -t -d 2 bond0
    
  • 按连接对看(先找“谁在聊”)

    # 按连接对显示流量,P 显示端口,host字面量
    sudo iftop -i bond0 -P
    

    看到“重流量”的远端IP/端口后,用 ss 反查进程:

    # 定位连接对应的进程(PID/命令行)
    sudo ss -tinp dst <REMOTE_IP> or sport = :<PORT> or dport = :<PORT>
    

小贴士

  • nethogs 直接到“进程”这一层;iftop 先给你“会话对”,再用 ss -p 归属到进程。
  • 只看某块网卡就把接口名换成相应 bondX/ensX

二、精准到“进程/容器”的带宽(eBPF方式,容器友好)

目标:轻量、低开销地按进程/容器统计 TCP 收发速率。

  • BCC 的 tcptop(推荐,内核≥4.x 更佳)

    # 安装(示例基于 RHEL/CentOS 系):yum install -y bcc-tools kernel-devel-$(uname -r)
    # 按进程/容器每秒统计TCP吞吐(发送/接收),容器感知
    sudo /usr/share/bcc/tools/tcptop -C 1
    # 仅看某个进程
    sudo /usr/share/bcc/tools/tcptop -p <PID> 1
    

    输出会直接给出 PID/进程名/发送KB/s/接收KB/s(加 -C 会标注容器)。

  • bpftrace 一行流(若没 bcc,可选,内核符号需匹配)

    sudo bpftrace -e '
    kprobe:tcp_sendmsg     { @tx[pid,comm] += args->size; }
    kprobe:tcp_cleanup_rbuf{ @rx[pid,comm] += args->copied; }
    interval:s:1 { printf("\n--- TX (bytes/s) ---\n"); print(@tx); clear(@tx);
                   printf("\n--- RX (bytes/s) ---\n"); print(@rx); clear(@rx); }'
    

    注:老内核/裁剪内核可能缺少这些参数符号;优先用 tcptop

  • 不改内核的传统方案(可持久化)
    atop + netatop 能做按进程网络带宽的历史记录:

    yum install -y atop netatop
    modprobe netatop && systemctl enable --now netatop
    atop  # 按 'n' 进入网络视图,可查看按进程NET列
    

三、Kubernetes 场景:先锁 Pod,再钻进容器查进程

目标:在很多 Pod/容器里,先找“哪几个 Pod 在吃带宽”,再定位到容器内的具体进程。

  1. PromQL 找 Top Pod(总收发,Gbit/s)
topk(10,
  sum by (namespace, pod) (
    rate(container_network_transmit_bytes_total{pod!=""}[2m]) +
    rate(container_network_receive_bytes_total{pod!=""}[2m])
  ) * 8 / 1e9
)
  1. (用 Cilium 的话)看每个 Pod/Endpoint 的字节计数
# 进入任意 cilium DaemonSet 容器
kubectl -n kube-system exec -it ds/cilium -- cilium endpoint list
# 列表里有 TX/RX bytes,可快速看“谁在冲”
  1. 把“重流量 Pod”映射到宿主机进程
# 找容器ID与所在节点
kubectl -n <NS> get pod <POD> -o wide
crictl -r unix:///run/containerd/containerd.sock ps | grep <POD>

# 取容器的宿主机PID
cid=<ContainerID>
pid=$(sudo crictl inspect $cid | jq -r .info.pid)

# 进入容器网络命名空间看会话与进程
sudo nsenter -t "$pid" -n ss -tinp     # 按会话+PID
# 或直接用 bcc 的 tcptop 按该 PID 统计
sudo /usr/share/bcc/tools/tcptop -p "$pid" 1

解释:PromQL 先把“谁在吃带宽”找出来;Cilium/hubble 能给到更细的流向和端口;最后 nsenter 进容器 netns,用 ss/tcptop 精确到容器里的具体 PID/进程名

四、一些判断与陷阱

  • bond/LACP:单 TCP 流常被单条从口“限速”;多流才能压满总和。观测时尽量用多连接(例如业务本身或 iperf3 -P 8)。
  • 虚拟设备重复计数:在主机用 node_network_* 统计时,过滤 lo|veth|cni|docker|kube|br|tun|tap|virbr,避免重复。
  • 短峰 vs 趋势:秒级抓尖峰用 irate()/tcptop;做趋势/告警用 rate()

快速路径总结

  1. 先用 nethogs bond0 或 PromQL TopK 抓“大户”;
  2. K8s 下锁定 Pod → crictl inspect 拿 PID → nsenter 进去用 ss -tinp / tcptop
  3. 要长期留痕就上 atop+netatop 或用 eBPF(bcc)定点巡检。
struct virtrdma_sq_stats { struct u64_stats_sync syncp; u64 packets; u64 bytes; u64 xdp_tx; u64 xdp_tx_drops; u64 kicks; u64 tx_timeouts; }; struct virtrdma_rq_stats { struct u64_stats_sync syncp; u64 packets; u64 bytes; u64 drops; u64 xdp_packets; u64 xdp_tx; u64 xdp_redirects; u64 xdp_drops; u64 kicks; }; /* Internal representation of a send virtqueue */ struct virtrdma_send_queue { /* Virtqueue associated with this send _queue */ struct virtqueue *vq; /* TX: fragments + linear part + virtio header */ struct scatterlist sg[MAX_SKB_FRAGS + 2]; /* Name of the send queue: output.$index */ char name[16]; struct virtrdma_sq_stats stats; struct napi_struct napi; /* Record whether sq is in reset state. */ bool reset; }; /* RX packet size EWMA. The average packet size is used to determine the packet * buffer size when refilling RX rings. As the entire RX ring may be refilled * at once, the weight is chosen so that the EWMA will be insensitive to short- * term, transient changes in packet size. */ DECLARE_EWMA(pkt_len, 0, 64) /* Internal representation of a receive virtqueue */ struct virtrdma_receive_queue { /* Virtqueue associated with this receive_queue */ struct virtqueue *vq; struct napi_struct napi; struct bpf_prog __rcu *xdp_prog; struct virtrdma_rq_stats stats; /* Chain pages by the private ptr. */ struct page *pages; /* Average packet length for mergeable receive buffers. */ struct ewma_pkt_len mrg_avg_pkt_len; /* Page frag for packet buffer allocation. */ struct page_frag alloc_frag; /* RX: fragments + linear part + virtio header */ struct scatterlist sg[MAX_SKB_FRAGS + 2]; /* Min single buffer size for mergeable buffers case. */ unsigned int min_buf_len; /* Name of this receive queue: input.$index */ char name[16]; struct xdp_rxq_info xdp_rxq; }; struct virtrdma_info { struct virtio_device *vdev; struct virtqueue *cvq; struct net_device *dev; struct virtrdma_send_queue *sq; struct virtrdma_receive_queue *rq; unsigned int status; /* Max # of queue pairs supported by the device */ u16 max_queue_pairs; /* # of queue pairs currently used by the driver */ u16 curr_queue_pairs; /* # of XDP queue pairs currently used by the driver */ u16 xdp_queue_pairs; /* xdp_queue_pairs may be 0, when xdp is already loaded. So add this. */ bool xdp_enabled; /* I like... big packets and I cannot lie! */ bool big_packets; /* number of sg entries allocated for big packets */ unsigned int big_packets_num_skbfrags; /* Host will merge rx buffers for big packets (shake it! shake it!) */ bool mergeable_rx_bufs; /* Host supports rss and/or hash report */ bool has_rss; bool has_rss_hash_report; u8 rss_key_size; u16 rss_indir_table_size; u32 rss_hash_types_supported; u32 rss_hash_types_saved; /* Has control virtqueue */ bool has_cvq; /* Host can handle any s/g split between our header and packet data */ bool any_header_sg; /* Packet virtio header size */ u8 hdr_len; /* Work struct for delayed refilling if we run low on memory. */ struct delayed_work refill; /* Is delayed refill enabled? */ bool refill_enabled; /* The lock to synchronize the access to refill_enabled */ spinlock_t refill_lock; /* Work struct for config space updates */ struct work_struct config_work; /* Does the affinity hint is set for virtqueues? */ bool affinity_hint_set; /* CPU hotplug instances for online & dead */ struct hlist_node node; struct hlist_node node_dead; struct control_buf *ctrl; /* Ethtool settings */ u8 duplex; u32 speed; /* Interrupt coalescing settings */ u32 tx_usecs; u32 rx_usecs; u32 tx_max_packets; u32 rx_max_packets; unsigned long guest_offloads; unsigned long guest_offloads_capable; /* failover when STANDBY feature enabled */ struct failover *failover; }; static inline struct virtio_rdma_dev *to_vdev(struct ib_device *ibdev) { return container_of(ibdev, struct virtio_rdma_dev, ib_dev); } #define virtio_rdma_dbg(ibdev, fmt, ...) \ ibdev_dbg(ibdev, "%s: " fmt, __func__, ##__VA_ARGS__) #endif 这一段也改一下
最新发布
09-23
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值