在做性能分析的时候,我们不可避免地判断资源到底够不够用?哪里不够?为什么不够?证据是什么?

回复得了这些问题并不容易。

今天就来絮叨一下OS资源饱和度应该如何衡量。

现在kubernets盛行,所以这里来借用k8s中部署的prometheus+grafana来看直观的看图。

  • CPU资源:

先看一个图:

grafana 内存使用率 grafana cpu使用率_性能分析

一边是CPU使用率,一边是CPU饱和度。

饱和度如何来算的呢?

看它的query是什么样的:

 

node:node_cpu_saturation_load1:{cluster="$cluster"} / scalar(sum(min(kube_pod_info{cluster="$cluster"}) by (node)))

即是node_cpu_saturation_load1来计算的,那这个node_cpu_saturation_load1的基础数据是什么?再来它的来源:

sum by (node) (    
          node_load1{job="node-exporter"}    
        * on (namespace, pod) group_left(node)    
          node_namespace_pod:kube_pod_info:    
        )    
        /    
        node:node_num_cpu:sum    
      record: 'node:node_cpu_saturation_load1:'

也即是说使用了node_load1来取的数据。这个通过node_exporter的可以知道是来自于load average 1min内的数据,node_exporter同时也实现了node_load5/node_load15。分别来对应我们常见的linux中的load average的1分钟、5分钟、15分钟。

而这个load average从哪里来,在之前的文章中有过描述,同时也说了这个值用来判断系统负载的局限性。这里就不展开了。

知道了这个cpu饱和度的来源之后,我们再来看上面的图。即是说,我们在判断CPU是否够用的时候,不仅是要看CPU使用率,还要看CPU饱和度才可以。

  • 内存资源:

再上图

grafana 内存使用率 grafana cpu使用率_数据_02

这里的内存饱和度后面加了一个Swap IO。其实了解内存的人会知道,swap这个词实在是有特定的含义,交换分区,但是在我们在配置kubernets的时候会知道swap是关了的。但是这里swap是什么呢?再来看它的query语句。


node:node_memory_swap_io_bytes:sum_rate{cluster="$cluster"}


取了node_memory_swap_io_bytes 这个值,这个值在prometheus里是什么呢?


- expr: |    
        1e3 * sum(    
          (rate(node_vmstat_pgpgin{job="node-exporter"}[1m])    
         + rate(node_vmstat_pgpgout{job="node-exporter"}[1m]))    
        )    
      record: :node_memory_swap_io_bytes:sum_rate


取了vmstat的page in/out。这就理解了,swap并不是特指交换分区。而是页交换,没有交换分区不打紧,页交换还是要做的。

而有页交换并不一定就是内存用完了,指内存中找不到要使用的页也是要有page in的。而在代码中定义了某个变量或对象的内存大小之后,当不够用时,也照样会page out。

所以判断内存是否够用,不仅要看内存是否用完,还要看page in/out是否产生。

  • 磁盘资源:

grafana 内存使用率 grafana cpu使用率_性能分析_03

磁盘的IO饱和度是比较容易判断的,我们来看看它是如何判断,同样来看看它的query。


node:node_disk_saturation:avg_irate{cluster="$cluster"} / scalar(:kube_pod_info_node_count:{cluster="$cluster"})


取了node_disk_staturation,同时计算了avg_irate。我们来看一下这个值的来源。


- expr: |    
        avg by (node) (    
          irate(node_disk_io_time_weighted_seconds_total{job="node-exporter",device=~"nvme.+|rbd.+|sd.+|vd.+|xvd.+|dm-.+"}[1m])    
        * on (namespace, pod) group_left(node)    
          node_namespace_pod:kube_pod_info:    
        )    
      record: node:node_disk_saturation:avg_irate


它取了node_disk_io_time_weighted_seconds_total的值,这是一个加权累积值。而这个值是来源于iostat中的avgqu-sz,这个值是IO队列长度。

这样就知道这个饱和度的来源了。

  • 网络资源:

grafana 内存使用率 grafana cpu使用率_性能分析_04

在网络资源的判断上,这里用了一个非常直接的词dropped。直观理解就是丢包,来看它的query语句。


node:node_net_saturation:sum_irate{cluster="$cluster"}


这里调用了node_net_saturation的值,而这个值不够直观的知道是什么内容。再来看看它的来源:


- expr: |    
        sum by (node) (    
          (irate(node_network_receive_drop_total{job="node-exporter",device!~"veth.+"}[1m]) +    
          irate(node_network_transmit_drop_total{job="node-exporter",device!~"veth.+"}[1m]))    
        * on (namespace, pod) group_left(node)    
          node_namespace_pod:kube_pod_info:    
        )    
      record: node:node_net_saturation:sum_irate


从这段代码就很清楚地知道了,这是从接收的drop来的。好直观的名字。

这个值。

其实判断网络瓶颈,不止是丢不丢包,也要看队列。如果已经出现丢包了的话,那网络质量就已经非常差了,并且丢包的原因众多,有些是配置或代码bug的原因。所以在性能分析中,只看丢包还是不够的。

其实不管我们用什么工具来看性能数据,都是需要知道它的来源和值的含义,这样才能判断精确。

在很多场合,不管是培训还是做一些分享,我都强调过性能分析中要先理解每个数据的含义再来看所选择的工具中如何展现。

而有些人觉得上一个监控平台或APM之类的就可以知道瓶颈点在哪里了,这个理念绝对是有问题的。

打好基础,仍然是学习的重点。