Grafana 展示的 CPU 利用率与实际不符的问题探究

本文转自 charlieroro 的博客,原文:https://www.cnblogs.com/charlieroro/p/17152138.html,版权归原作者所有。欢迎投稿,投稿请添加微信好友:cloud-native-yang

问题描述

最近看了一个虚机的 CPU 使用情况,使用 mpstat -P ALL 命令查看系统的 CPU 情况 (该系统只有一个 CPU core),发现该 CPU 的 %usr 长期维持在 70% 左右,且 %sys 也长期维持在 20% 左右:

03:56:29 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
03:56:34 AM  all   67.11    0.00   24.83    0.00    0.00    8.05    0.00    0.00    0.00    0.00
03:56:34 AM    0   67.11    0.00   24.83    0.00    0.00    8.05    0.00    0.00    0.00    0.00

mpstat 命令展示的 CPU 结果和 top 命令一致

但通过 Grafana 查看发现该机器的 %usr%sys 均低于实际情况。如下图棕色曲线为 usr,蓝色曲线为 sys

2df2e5404d22fd8ea0ea4b0670784367.png

Grafana 的表达式如下:

avg by (mode, instance) (irate(node_cpu_seconds_total{instance=~"$instance", mode=~"user|system|iowait"}[$__rate_interval]))

问题解决

尝试解决

一开始怀疑是 node-exporter 版本问题,但查看 node-exporter 的 release notes 并没有相关 bug,在切换为最新版本之后,问题也没有解决。

调研 node-exporter 运作方式

大部分与系统相关的 prometheus 指标都是直接从系统指标文件中读取并转换过来的。node-exporter 中与 CPU 相关的指标就读取自 /proc/stat,其中与 CPU 相关的内容就是下面的前两行,每行十列数据,分别表示 UserNiceSystemIdleIowaitIRQ SoftIRQStealGuestGuestNice

# cat /proc/stat
cpu  18651720 282843 9512262 493780943 10294540 0 2239778 0 0 0
cpu0 18651720 282843 9512262 493780943 10294540 0 2239778 0 0 0
intr 227141952 99160476 9 0 0 2772 0 0 0 0 0 0 0 157 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 4027171429
btime 1671775036
processes 14260129
procs_running 5
procs_blocked 0
softirq 1727699538 0 816653671 1 233469155 45823320 0 52888978 0 0 578864413

node-exporter 并没有做什么运算,它只是将这十列数据除以 userHZ(100),打上 mode 标签之后转换为 prometheus 格式的指标:

node_cpu_seconds_total{cpu="0", instance="redis:9100", mode="user"}                                    244328.77

mpstat 命令的计算方式

那 mpstat 是如何计算不同 mode 的 CPU 利用率呢?

在 mpstat 的源代码[1]中可以看到,mode 为 User 的计算方式如下,涉及三个参数:

  • scc: 当前采样到的 CPU 信息,对应 /proc/stat 中的 CPU 信息

  • scp: 上一次采样到的 CPU 信息,对应 /proc/stat 中的 CPU 信息

  • deltot_jiffies: 两次 CPU 采样之间的 jiffies(下面介绍什么是 jiffies)

ll_sp_value(scp->cpu_user - scp->cpu_guest,
           scc->cpu_user - scc->cpu_guest, deltot_jiffies)

ll_sp_value 函数的定义如下,它使用了宏定义 SP_VALUE

/*
 ***************************************************************************
 * Workaround for CPU counters read from /proc/stat: Dyn-tick kernels
 * have a race issue that can make those counters go backward.
 ***************************************************************************
 */
double ll_sp_value(unsigned long long value1, unsigned long long value2,
     unsigned long long itv)
{
 if (value2 < value1)
  return (double) 0;
 else
  return SP_VALUE(value1, value2, itv);
}

SP_VALUE 的定义如下:

/* With S_VALUE macro, the interval of time (@p) is given in 1/100th of a second */
#define S_VALUE(m,n,p)  (((double) ((n) - (m))) / (p) * 100)
/* Define SP_VALUE() to normalize to % */
#define SP_VALUE(m,n,p)  (((double) ((n) - (m))) / (p) * 100)
/*

根据 SP_VALUE 定义可以看到两次 CPU 采样获取到的 mode 为 User 的 CPU 占用率计算方式为:(((double) ((scp->cpu_user - scp->cpu_guest) - (scp->cpu_user - scp->cpu_guest))) / (deltot_jiffies) * 100)

下面函数用于计算 deltot_jiffies,可以看到 jiffies 其实就是 /proc/stat 中的 CPU 数值单位:

/*
 ***************************************************************************
 * Since ticks may vary slightly from CPU to CPU, we'll want
 * to recalculate itv based on this CPU's tick count, rather
 * than that reported by the "cpu" line. Otherwise we
 * occasionally end up with slightly skewed figures, with
 * the skew being greater as the time interval grows shorter.
 *
 * IN:
 * @scc Current sample statistics for current CPU.
 * @scp Previous sample statistics for current CPU.
 *
 * RETURNS:
 * Interval of time based on current CPU, expressed in jiffies.
 *
 * USED BY:
 * sar, sadf, mpstat
 ***************************************************************************
 */
unsigned long long get_per_cpu_interval(struct stats_cpu *scc,
     struct stats_cpu *scp)
{
 unsigned long long ishift = 0LL;

 if ((scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest)) {
  /*
   * Sometimes the nr of jiffies spent in guest mode given by the guest
   * counter in /proc/stat is slightly higher than that included in
   * the user counter. Update the interval value accordingly.
   */
  ishift += (scp->cpu_user - scp->cpu_guest) -
            (scc->cpu_user - scc->cpu_guest);
 }
 if ((scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice)) {
  /*
   * Idem for nr of jiffies spent in guest_nice mode.
   */
  ishift += (scp->cpu_nice - scp->cpu_guest_nice) -
            (scc->cpu_nice - scc->cpu_guest_nice);
 }

 /*
  * Workaround for CPU coming back online: With recent kernels
  * some fields (user, nice, system) restart from their previous value,
  * whereas others (idle, iowait) restart from zero.
  * For the latter we need to set their previous value to zero to
  * avoid getting an interval value < 0.
  * (I don't know how the other fields like hardirq, steal... behave).
  * Don't assume the CPU has come back from offline state if previous
  * value was greater than ULLONG_MAX - 0x7ffff (the counter probably
  * overflew).
  */
 if ((scc->cpu_iowait < scp->cpu_iowait) && (scp->cpu_iowait < (ULLONG_MAX - 0x7ffff))) {
  /*
   * The iowait value reported by the kernel can also decrement as
   * a result of inaccurate iowait tracking. Waiting on IO can be
   * first accounted as iowait but then instead as idle.
   * Therefore if the idle value during the same period did not
   * decrease then consider this is a problem with the iowait
   * reporting and correct the previous value according to the new
   * reading. Otherwise, treat this as CPU coming back online.
   */
  if ((scc->cpu_idle > scp->cpu_idle) || (scp->cpu_idle >= (ULLONG_MAX - 0x7ffff))) {
   scp->cpu_iowait = scc->cpu_iowait;
  }
  else {
   scp->cpu_iowait = 0;
  }
 }
 if ((scc->cpu_idle < scp->cpu_idle) && (scp->cpu_idle < (ULLONG_MAX - 0x7ffff))) {
  scp->cpu_idle = 0;
 }

 /*
  * Don't take cpu_guest and cpu_guest_nice into account
  * because cpu_user and cpu_nice already include them.
  */
 return ((scc->cpu_user    + scc->cpu_nice   +
   scc->cpu_sys     + scc->cpu_iowait +
   scc->cpu_idle    + scc->cpu_steal  +
   scc->cpu_hardirq + scc->cpu_softirq) -
  (scp->cpu_user    + scp->cpu_nice   +
   scp->cpu_sys     + scp->cpu_iowait +
   scp->cpu_idle    + scp->cpu_steal  +
   scp->cpu_hardirq + scp->cpu_softirq) +
   ishift);
}

从上面计算方式可以看到,deltot_jiffies 近似可以认为是两次 CPU 采样的所有 mode 总和之差,以下表为例:

User     Nice   System   Idle     Iowait  IRQ SoftIRQ  Steal Guest GuestNice
cpu  18424040 281581 9443941 493688502 10284789 0    2221013   0     0       0 # 第一次采样,作为scp

cpu  18424137 281581 9443954 493688502 10284789 0    2221020   0     0       0 # 第二次采样,作为scc

deltot_jiffies 的计算方式为:

(18424137+281581+9443954+493688502+10284789) - (18424040+281581+9443941+493688502+2221013) + 0 = 117

那么根据采样到的数据,可以得出当前虚拟上的 mode 为 User 的 CPU 占用率为:(((double) ((18424137 - 0) - (18424040 - 0))) / (117) * 100)=82.9%,与预期相符。

再回头看下出问题的 Grafana 表达式,可以看出其计算的是 mode 为 User 的 CPU 的变动趋势,而不是 CPU 占用率,按照 mpstat 的计算方式,该 mode 的占用率的近似计算方式如下:

increase(node_cpu_seconds_total{mode="user", instance="drg1-prd-dragon-redis-sentinel-data-1:9100"}[10m])/on (cpu,instance)(increase(node_cpu_seconds_total{mode="user", instance="drg1-prd-dragon-redis-sentinel-data-1:9100"}[10m])+ on (cpu,instance) increase(node_cpu_seconds_total{mode="system", instance="drg1-prd-dragon-redis-sentinel-data-1:9100"}[10m]))

得出的 mode 为 User 的 CPU 占用率曲线图如下,与 mpstat 展示结果相同:

cc536a54a27932fa828cba36b1c851b0.png

如果有必要的话,可以创建新的指标,用于准确表达 CPU 占用率。

引用链接

[1]

源代码: https://github.com/sysstat/sysstat/blob/v12.7.2/mpstat.c#L734

d83f62cc27c25f230f7ed893028a3b58.gif

3ebd5b07a2391f44921717172aa56516.png

你可能还喜欢

点击下方图片即可阅读

OpenAI 再放大招,ChatGPT 正式版 API 就是救世主尼奥,而我们都是愚蠢的人类

2023-03-03

3a33f8aa522d07f54472b7c8a53c924e.jpeg

深入了解 K8s 扩展神器 client-go 的详细用法

2023-02-28

f0ed2adbdfe23217cea7142f883e7eb5.jpeg

我让 ChatGPT 化身为全知全能的文档小助理,啥姿势她都会......

2023-02-27

8c486ae6d82b16c166c6e0136fa537b8.jpeg

深度分析 cAdvisor 的工作原理

2023-02-17

f400959dbb22401b99aa4fed15e7c8c1.jpeg

9b41f19a74075c2f2b20327d275662ec.gif

云原生是一种信仰 🤘

关注公众号

后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

3aa123c411758184aa04b23510b64b80.gif

77bab968de39c0d207d308e17ac6eaa8.gif

点击 "阅读原文" 获取更好的阅读体验!

发现朋友圈变“安静”了吗?

203de85e57d7f05f22e35b136d555752.gif

### 通过Zabbix采集交换机CPU使用率并在Grafana中可视化展示 #### 配置Zabbix Server以支持SNMP协议 为了采集交换机的CPU使用率,通常需要依赖SNMP协议。因此,在Zabbix Server上需确保已启用SNMP支持,并配置相应的OID来获取目标设备的数据。 在`zabbix_server.conf`文件中确认以下参数是否正确设置: ```bash [root@zabbix ~]# vim /usr/local/zabbix/etc/zabbix_server.conf ... EnableRemoteCommands=1 LogRemoteCommands=1 StartSNMPTrapper=1 SNMPTrapperFile=/tmp/snmptrap.log ``` 以上配置启用了SNMP Trapper功能[^2]。 #### 创建主机并关联SNMP接口 登录到Zabbix Web界面,创建一个新的主机用于表示待监控的交换机。具体操作如下: 1. **导航至“Configuration” -> “Hosts”,点击右上角的“Create host”。** 2. 填写主机名称(如 `Switch-CPU-Monitoring`),并将该主机分配给合适的主机组。 3. 添加SNMP接口:填写交换机IP地址、端口号(默认为161)以及SNMP版本(通常是v2c或v3)。如果使用的是社区字符串,则输入对应的值(如 `public` 或其他自定义值)。 #### 设置监控项以捕获CPU利用率数据 进入新创建的主机页面,转到“Items”选项卡下新增一项指标用来追踪交换机处理器负载情况。这里推荐利用标准MIB库中的hrProcessorLoad对象作为依据计算百分比形式的结果。 - 名称: Switch CPU Usage Percentage - 类型: SNMP v2 agent (or appropriate version based on device capabilities) - 键名: snmpget["HOST.CONN", "1.3.6.1.4.1.<VendorSpecificOID>.<InstanceID>"] *(注意替换实际厂商特定路径)* - 更新间隔: 根据需求调整频率,默认设为60秒即可满足大多数场景下的精度要求. - 数据类型: 数字(无符号整数) 此过程可能涉及查阅对应品牌型号文档找到确切位置信息[^1]. #### 构建图形化视图以便于理解趋势变化 一旦成功建立了上述项目之后,就可以着手设计图表呈现方式了. ##### 方法一: 使用内置Dashboard Widgets 直接回到前端界面上选择"Dashboards",然后添加新的小组件(widget).从中挑选折线图(Line Chart)或者面积填充样式(Area Chart),最后指定刚刚建立起来的那个item做为其数据源. ##### 方法二: 整合第三方工具-Grafana 对于更高级别的定制需求来说,Grafana无疑是个极佳的选择方案之一.以下是大致流程概述: 1. 安装部署好最新版Grafana服务实例; 2. 登录后台管理系统导入官方提供的预制插件包(zabbix-datasource); 3. 连接到现有的Zabbix数据库实例里读取原始记录条目; 4. 新增面板(panel),选取先前定义好的那个测量维度绘制曲线走势图像出来; 关于这部分的具体实施细节可以参照专门教程资料[^3][^4]. --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值