问题描述

最近看了一个虚机的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

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的源代码中可以看到,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展示结果相同:

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

grafana展示的CPU利用率与实际不符的问题探究的更多相关文章

  1. linux系统性能监控--CPU利用率

    在对系统的方法化分析中,首要且最基本的工具之一常常是对系统的 CPU利用率进行简单测量. Linux以及大多数基于 UNIX的操作系统都提供了一条命令来显示系统的平均负荷(loadaverage) . ...

  2. [转帖]震惊,用了这么多年的 CPU 利用率,其实是错的

    震惊,用了这么多年的 CPU 利用率,其实是错的 2018年12月22日 08:43:09 Linuxer_ 阅读数:50 https://blog.csdn.net/juS3Ve/article/d ...

  3. CPU 利用率背后的真相,只有 1% 人知道【转】

    导读:本文翻译自 Brendan Gregg 去年的一篇博客文章 “CPU Utilization is Wrong”,从标题就能想到这篇文章将会引起争议.文章一上来就说,我们“人人皆用.处处使用,每 ...

  4. Prometheus笔记(二)监控go项目实时给grafana展示

    欢迎加入go语言学习交流群 636728449 Prometheus笔记(二)监控go项目实时给grafana展示 Prometheus笔记(一)metric type 文章目录 一.promethe ...

  5. 震惊,用了这么多年的 CPU 利用率,其实是错的

    导读:本文翻译自 Brendan Gregg 去年的一片博客文章 "CPU Utilization is Wrong",从标题就能想到这篇文章将会引起争议.文章一上来就说,我们&q ...

  6. 系统服务监控指标--load、CPU利用率、磁盘剩余空间、磁盘I/O、内存使用情况等

    介绍 大型互联网企业的背后,依靠的是成千上万台服务器日夜不停的运转,以支撑其业务的运转.宕机对于互联网企业来说,代价是沉重的,轻则影响用户体验,重则直接影响交易,导致交易下跌,并且给企业声誉造成不可挽 ...

  7. CPU利用率异常的分析思路和方法交流探讨

    CPU利用率异常的分析思路和方法交流探讨在生产运行当中,经常会遇到CPU利用率异常或者不符合预期的情况,此时,往往暗示着系统性能问题.那么究竟是核心应用的问题?是监控工具的问题?还是系统.硬件.网络层 ...

  8. python多进程提高cpu利用率

    cpu参数: 1个物理cpu,2个逻辑cpu(超线程),单核 具体 http://blog.csdn.net/dba_waterbin/article/details/8644626   物理CPU. ...

  9. cpu利用率和cpu 队列

    SIP的第四期结束了,因为控制策略的丰富,早先的的压力测试结果已经无法反映在高并发和高压力下SIP的运行状况,因此需要重新作压力测试.跟在测试人员后面做了快一周的压力测试,压力测试的报告也正式出炉,本 ...

  10. 查看进程,按内存从大到小 ,查看进程,按CPU利用率从大到小排序

    查看进程,按内存从大到小 ps -e -o "%C : %p : %z : %a"|sort -k5 -nr 查看进程,按CPU利用率从大到小排序 ps -e -o "% ...

随机推荐

  1. Java手写一个批量获取数据工具类

    1. 背景 偶尔会在公司的项目里看到这样的代码 List<Info> infoList = new ArrayList<Info>(); if (infoidList.size ...

  2. 看起来简单实际上却很牛的KMP算法:LeetCode572-另一棵树的子树

    题目描述 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树.s 的一个子树包括 s 的一个节点和这个节点的所有子孙.s 也可以看做它自身的一棵子树. 暴力解法 从 ...

  3. TypeScript 之 控制流分析(Control Flow Analysis)

    控制流分析(Control Flow Analysis) 描述: CFA 几乎总是采用联合,基于代码逻辑去减少联合里面的类型数量. 大多数时候,CFA 在自然的JavaScript布尔逻辑中工作,但是 ...

  4. 高可用系列文章之三 - NGINX 高可用实施方案

    前文链接 高可用系列文章之一 - 概述 - 东风微鸣技术博客 (ewhisper.cn) 高可用系列文章之二 - 传统分层架构技术方案 - 东风微鸣技术博客 (ewhisper.cn) 四 NGINX ...

  5. java中的字符串数组

    本文主要讲述java中的字符串数组 字符串数组的声明有如下几种形式: // 第一种方式:new // 注意在String的后面[]中不需要添加字符串数组的长度.否则报错. String[] arr_1 ...

  6. 红袖添香,绝代妖娆,Ruby语言基础入门教程之Ruby3基础数据类型(data types)EP02

    Ruby是强类型动态语言,即Ruby中一旦某一个对象被定义类型,如果不通过强制转换操作,那么它永远就是该数据类型,并且只有在Ruby解释器运行时才会检测对象数据类型,它的一切皆为对象(包括 nil 值 ...

  7. ctfshow——萌新web3

    题目如下: 源码分析: 通过id可以传入一个参数,对id的值进行了过滤,这里是正则匹配过滤,内容分析:or,-,\,*,<,>,!,x,hex,+.最外面的i是同时匹配过滤内容的大小写.在 ...

  8. ssm——springMVC整理

    目录 1.概念 1.1.什么是SpringMVC 1.2.B/S架构 1.3.MVC结构 1.4.Spring MVC常用名词 1.5.常用注解 1.6.rest和restfull 1.7.Reque ...

  9. C#应用程序配置文件(XML序列化) - 开源研究系列文章

    上次写了一个C#线程池及管理器的博文( C#开发的线程池和管理器 - 开源研究系列文章 ),收到了不小的浏览量,在此感谢各位网友的支持.这次将另一个功能放出来单独讲解:C#应用程序的配置文件,使用的是 ...

  10. python之路56 dajngo最后一天 csrf跨站请求 auth模块登录注册方法

    csrf跨站请求伪造 钓鱼网站:模仿一个正规的网站 让用户在该网站上做操作 但是操作的结果会影响到用户正常的网站账户 但是其中有一些猫腻 eg:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但 ...