linux性能評估-cpu案例操作篇


1.平均負載案例分析

預先安裝 stress 和 sysstat 包。(yum install -y stress , yum install -y sysstat)
stress 是一個 Linux 系統壓力測試工具,這里我們用作異常進程模擬平均負載升高的場景。
sysstat 包含了常用的 Linux 性能工具,用來監控和分析系統的性能。我們的案例會用到這個包的兩個命令 mpststat 和 pidstat。

  • mpstat 是一個常用的多核 CPU 性能分析工具,用來實時查看每個 CPU 的性能指標,以及所有 CPU 的平均指標。
  • pidstat 是一個常用的進程性能分析工具,用來實時查看進程的 CPU、內存、I/O 以及上下文切換等性能指標。

場景一:CPU 密集型進程

我們在第一個終端運行 stress 命令,模擬一個 CPU 使用率 100% 的場景:

$ stress --cpu 1 --timeout 600

接着,在第二個終端運行 uptime 查看平均負載的變化情況:

[root @CESHI_Game_YALI_26 ceshi]# uptime
17 : 37 : 54 up 277 days,  2 : 27 3 users,  load average: 0.99 , 0.69 , 0.31

在第三個終端運行 mpstat 查看 CPU 使用率的變化情況:

# -P ALL 表示監控所有 CPU,后面數字 5 表示間隔 5 秒后輸出一組數據
[root @CESHI_Game_YALI_26 ceshi]# mpstat -P ALL 5
Linux 2.6 . 32 - 642.6 . 2 .el6.x86_64 (CESHI_Game_YALI_26)    02 / 22 / 2019  _x86_64_    ( 4 CPU)
05 : 36 : 53 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
05 : 36 : 58 PM  all   25.06    0.00    0.00    0.00    0.00    0.00    0.00    0.00   74.94
05 : 36 : 58 PM    0  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
05 : 36 : 58 PM    1    0.20    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.80
05 : 36 : 58 PM    2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
05 : 36 : 58 PM    3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00

分析:從終端二中可以看到,1 分鍾的平均負載會慢慢增加到 0.99,而從終端三中還可以看到,正好有一個 CPU 的使用率為 100%,但它的 iowait 只有 0。這說明,平均負載的升高正是由於 CPU 使用率為 100% 。
那么,到底是哪個進程導致了 CPU 使用率為 100% 呢?可以使用 pidstat 來查詢:

# 間隔 5 秒后輸出一組數據
# pidstat -u 5 1
Linux 2.6 . 32 - 642.6 . 2 .el6.x86_64 (CESHI_Game_YALI_26)    02 / 22 / 2019  _x86_64_    ( 4 CPU)
 
05 : 47 : 17 PM       PID    %usr %system  %guest    %CPU   CPU  Command
05 : 47 : 22 PM        19    0.00    0.20    0.00    0.20     0  events/ 0
05 : 47 : 22 PM     15345  100.00    0.00    0.00  100.00     3  stress
05 : 47 : 22 PM     27162    0.00    0.20    0.00    0.20     2  mail
05 : 47 : 22 PM     32008    0.20    0.20    0.00    0.40     2  telegraf
 
Average:          PID    %usr %system  %guest    %CPU   CPU  Command
Average:           19    0.00    0.20    0.00    0.20     -  events/ 0
Average:        15345  100.00    0.00    0.00  100.00     -  stress
Average:        27162    0.00    0.20    0.00    0.20     -  mail
Average:        32008    0.20    0.20    0.00    0.40     -  telegraf

從這里可以明顯看到,stress 進程的 CPU 使用率為100%。

場景二:I/O密集型進程

首先還是運行 stress 命令,但這次模擬 I/O壓力,即不停地執行 sync:

# stress -i 1 --timeout 600

還是在第二個終端運行 uptime 查看平均負載的變化情況:

[root @CESHI_Game_YALI_26 ceshi]# uptime
18 : 14 : 43 up 277 days,  3 : 04 3 users,  load average: 1.11 , 0.55 , 0.31

然后,第三個終端運行 htop 查看 CPU 使用率的變化情況:

比如cpu密集型的應用,它的負載顏色是綠色偏高iowait的操作,它的負載顏色是紅色偏高等等,此案例的查看結果是紅色偏高,正是iowait的操作導致的。根據這些指標再用htop的sort就很容易定位到有問題的進程。按上下鍵選擇進程之后,按s,按s:用strace追蹤進程的系統調用

場景三:大量進程的場景

當系統中運行進程超出 CPU 運行能力時,就會出現等待 CPU 的進程。
我們還是使用 stress,但這次模擬的是 8 個進程:

$ stress -c 8 --timeout 600

由於系統只有 4個 CPU,明顯比 8 個進程要少得多,因而,系統的 CPU 處於嚴重過載狀態,平均負載高達 7.74.

[root @CESHI_Game_YALI_26 ceshi]# uptime
14 : 36 : 30 up 277 days, 23 : 26 3 users,  load average: 7.74 , 3.85 , 1.51

在第三個終端中,輸入htop,查看CPU使用情況,可以看出綠色顯示4個CPU已經使用完,出現了CPU過載。


所以,在理解平均負載時,也要注意:

  • 平均負載高有可能是 CPU 密集型進程導致的;
  • 平均負載高並不一定代表 CPU 使用率高,還有可能是 I/O更繁忙了;
  • 當發現負載高的時候,你可以使用 mpstat、pidstat,htop等工具,輔助分析負載的來源。

場景二和場景三沒有使用pidstat查看,是因為CentOS默認的sysstat稍微有點老,不顯示%wait的問題,所以用的htop查看的。

2.CPU 上下文切換案例

2.1怎么查看系統的上下文切換情況

vmstat 是一個常用的系統性能分析工具,主要用來分析系統的內存使用情況,也常用來分析 CPU 上下文切換和中斷的次數。

# 每隔 5 秒輸出 1 組數據
[root @CESHI_Game_YALI_26 ceshi]# vmstat 5
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
  0  0 1221036 6357068 153540 1062220    1    1     2     7    0    0  0  0 99  0  0
  0  0 1221036 6357052 153540 1062240    0    0     0     5  129  200  0  0 100  0  0
  0  0 1221036 6357052 153540 1062240    0    0     0     0   89  149  0  0 100  0  0
  • cs(context switch)是每秒上下文切換的次數。

  • in(interrupt)則是每秒中斷的次數。

  • r(Running or Runnable)是就緒隊列的長度,也就是正在運行和等待 CPU 的進程數。

  • b(Blocked)則是處於不可中斷睡眠狀態的進程數。

2.2查看每個進程上下文切換的情況

[root @CESHI_Game_YALI_26 ceshi]# pidstat -w 5
Linux 2.6 . 32 - 642.6 . 2 .el6.x86_64 (CESHI_Game_YALI_26)    02 / 23 / 2019  _x86_64_    ( 4 CPU)
 
03 : 18 : 47 PM       PID   cswch/s nvcswch/s  Command
03 : 18 : 52 PM         7      0.20      0.00  migration/ 1
03 : 18 : 52 PM         9      0.40      0.00  ksoftirqd/ 1
03 : 18 : 52 PM        17      0.20      0.00  ksoftirqd/ 3
  • cswch ,表示每秒自願上下文切換的次數。

  • nvcswch ,表示每秒非自願上下文切換的次數。

  • 所謂自願上下文切換,是指進程無法獲取所需資源,導致的上下文切換。比如說, I/O、內存等系統資源不足時,就會發生自願上下文切換。

  • 而非自願上下文切換,則是指進程由於時間片已到等原因,被系統強制調度,進而發生的上下文切換。比如說,大量進程都在爭搶 CPU 時,就容易發生非自願上下文切換。

2.3 案例實操

安裝sysbench(yum install -y sysbench)

在第一個終端里運行 sysbench ,模擬系統多線程調度的瓶頸:

# 以 10 個線程運行 5 分鍾的基准測試,模擬多線程切換的問題
sysbench --threads= 10 --max-time= 300 threads run

接着,在第二個終端運行 vmstat ,觀察上下文切換情況。

[root @CESHI_Game_YALI_26 ceshi]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
  r  b   swpd   free   buff  cache   si   so    bi    bo   in cs us sy  id  wa  st
  5  0 1221036 6336044 154056 1080128    1    1     2     7    0    0  0  0 99  0  0
  7  0 1221036 6336028 154056 1080128    0    0     0     0 24446 2328677 19 70 11  0  0
  7  0 1221036 6336028 154056 1080128    0    0     0     0 29130 2355960 16 71 13  0  0
  • cs 列的上下文切換次數已經到了232萬,同時,觀查其他指標:
  • r列:就緒隊列到了7,遠遠超過了CPU的個數4,所以肯定會有CPU競爭。
  • us(user)列和sy(system)列:這兩列的CPU使用加起來到了90%,其中sy列的使用率到了71%,說明CPU主要被內核占用。
  • in列:中斷次數上升了2萬多,說明中斷也是潛在的問題。

綜合這幾個指標,我們可以知道,系統的就緒隊列過長,也就是正在運行和等待 CPU 的進程數過多,導致了大量的上下文切換,而上下文切換又導致了系統 CPU 的占用率升高。
分析是什么進程導致:

pidstat -wt參數表示輸出線程的上下文切換指標:

# 每隔 1 秒輸出一組數據(需要 Ctrl+C 才結束)
# -wt 參數表示輸出線程的上下文切換指標
$ pidstat -wt 1
Linux 2.6 . 32 - 642.6 . 2 .el6.x86_64 (CESHI_Game_YALI_26)    02 / 23 / 2019  _x86_64_    ( 4 CPU)
03 : 30 : 31 PM      TGID       TID   cswch/s nvcswch/s  Command
03 : 30 : 32 PM         -     31593  51816.67 199325.49  |__sysbench
03 : 30 : 32 PM         -     31594  50935.29 183059.80  |__sysbench
03 : 30 : 32 PM         -     31595  48006.86 204500.00  |__sysbench
03 : 30 : 32 PM         -     31596  51797.06 172247.06  |__sysbench
03 : 30 : 32 PM         -     31597  48173.53 188500.98  |__sysbench
03 : 30 : 32 PM         -     31598  47034.31 193852.94  |__sysbench
03 : 30 : 32 PM         -     31599  48339.22 219121.57  |__sysbench
03 : 30 : 32 PM         -     31600  57043.14 209324.51  |__sysbench
03 : 30 : 32 PM         -     31601  46477.45 205529.41  |__sysbench
03 : 30 : 32 PM         -     31602  55468.63 178434.31  |__sysbench

看來,上下文切換罪魁禍首,還是過多的 sysbench 線程。

分析中斷問題的導致:
既然是中斷,它只發生在內核態,而 pidstat 只是一個進程的性能分析工具,並不提供任何關於中斷的詳細信息。沒錯,那就是從 /proc/interrupts 這個只讀文件中讀取。/proc 實際上是 Linux 的一個虛擬文件系統,用於內核空間與用戶空間之間的通信。/proc/interrupts 就是這種通信機制的一部分,提供了一個只讀的中斷使用情況。

# -d 參數表示高亮顯示變化的區域
$ watch -d cat /proc/interrupts

觀察一段時間,你可以發現,變化速度最快的是重調度中斷(RES),這個中斷類型表示,喚醒空閑狀態的 CPU 來調度新的任務運行。這是多處理器系統(SMP)中,調度器用來分散任務到不同CPU 的機制,通常也被稱為處理器間中斷(Inter-Processor Interrupts,IPI)。

如果最開始時,我們只用了 pidstat 觀測,這些很嚴重的上下文切換線程,壓根兒就發現不了了。
這個數值其實取決於系統本身的 CPU 性能。在我看來,如果系統的上下文切換次數比較穩定,那么從數百到一萬以內,都應該算是正常的。但當上下文切換次數超過一萬次,或者切換次數出現數量級的增長時,就很可能已經出現了性能問題。所以,這里的中斷升高還是因為過多任務的調度問題,跟前面上下文切換次數的分析結果是一致的。

所以:

  • 自願上下文切換變多了,說明進程都在等待資源,有可能發生了 I/O 等其他問題;
  • 非自願上下文切換變多了,說明進程都在被強制調度,也就是都在爭搶 CPU,說明 CPU 的確成了瓶頸;

  • 中斷次數變多了,說明 CPU 被中斷處理程序占用,還需要通過查看 /proc/interrupts 文件來分析具體的中斷類型。

3.CPU使用率的案例

3.1CPU 使用率很高,但為啥卻找不到高 CPU 的應用?

當你發現系統的 CPU 使用率很高的時候,不一定能找到相對應的高 CPU 使用率的進程。
首先要觀察CPU的使用情況:使用top命令:
如果在top命令和pidstat 命令中都查看不到使用CPU較高的進程,那么需要從 CPU 使用率不高但處於 Running 狀態的進程入手,找出了可疑之處,最終通過 perf record -g 和 perf report 進行排查。發現原來是短時進程在搗鬼。

改案例需要在虛擬機中進行。預先安裝 docker、sysstat、perf、ab 等工具,如 apt install docker.io sysstat linux-tools-common apache2-utils 

1.首先,我們在第一個終端,執行下面的命令運行 Nginx 和 PHP 應用:

$ docker run --name nginx -p 10000 : 80 -itd feisky/nginx:sp
$ docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:sp

2.然后,在第二個終端,使用 curl 訪問 http://[VM1 的 IP]:10000,確認 Nginx 已正常啟動。你應該可以看到 It works! 的響應。

# 172.16 . 109.245 是第一台虛擬機的 IP 地址
$ curl http: //172.16.109.245:10000/
It works!

3.接着,我們來測試一下這個 Nginx 服務的性能。在第二個終端運行下面的 ab 命令。要注意,與上次操作不同的是,這次我們需要並發 100 個請求測試 Nginx 性能,總共測試 1000 個請求。

# 並發 100 個請求測試 Nginx 性能,總共測試 1000 個請求
$ ab -c 100 -n 1000 http: //172.16.109.245:10000/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
...
Requests per second:    87.86 [#/sec] (mean)
Time per request:       1138.229 [ms] (mean)
...

從 ab 的輸出結果我們可以看到,Nginx 能承受的每秒平均請求數,只有 87 多一點,是不是感覺它的性能有點差呀。那么,到底是哪里出了問題呢?我們再用 top 和 pidstat 來觀察一下。

4.這次,我們在第二個終端,將測試的並發請求數改成 5,同時把請求時長設置為 10 分鍾(-t 600)。這樣,當你在第一個終端使用性能分析工具時, Nginx 的壓力還是繼續的。
繼續在第二個終端運行 ab 命令:

$ ab -c 5 -t 600 http: //172.16.109.245:10000/

5.然后,我們在第一個終端運行 top 命令,觀察系統的 CPU 使用情況:

$ top
...
%Cpu(s): 80.8 us, 15.1 sy,  0.0 ni,  2.8 id,  0.0 wa,  0.0 hi,  1.3 si,  0.0 st
...
 
   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  6882 root      20   0    8456   5052   3884 S   2.7  0.1   0 : 04.78 docker-containe
  6947 systemd+  20   0   33104   3716   2340 S   2.7  0.0   0 : 04.92 nginx
  7494 daemon    20   0  336696  15012   7332 S   2.0  0.2   0 : 03.55 php-fpm
  7495 daemon    20   0  336696  15160   7480 S   2.0  0.2   0 : 03.55 php-fpm
10547 daemon    20   0  336696  16200   8520 S   2.0  0.2   0 : 03.13 php-fpm
10155 daemon    20   0  336696  16200   8520 S   1.7  0.2   0 : 03.12 php-fpm
10552 daemon    20   0  336696  16200   8520 S   1.7  0.2   0 : 03.12 php-fpm
15006 root      20   0 1168608  66264  37536 S   1.0  0.8   9 : 39.51 dockerd
  4323 root      20   0       0      0      0 I   0.3  0.0   0 : 00.87 kworker/u4: 1
...

觀察 top 輸出的進程列表可以發現,CPU 使用率最高的進程也只不過才 2.7%,看起來並不高。
然而,再看系統 CPU 使用率( %Cpu )這一行,你會發現,系統的整體 CPU 使用率是比較高的:用戶 CPU 使用率(us)已經到了 80%,系統 CPU 為 15.1%,而空閑 CPU (id)則只有 2.8%。

為什么用戶 CPU 使用率這么高呢?我們再重新分析一下進程列表,看看有沒有可疑進程:
docker-containerd 進程是用來運行容器的,2.7% 的 CPU 使用率看起來正常;
Nginx 和 php-fpm 是運行 Web 服務的,它們會占用一些 CPU 也不意外,並且 2% 的 CPU 使用率也不算高;
再往下看,后面的進程呢,只有 0.3% 的 CPU 使用率,看起來不太像會導致用戶 CPU 使用率達到 80%。
那就奇怪了,明明用戶 CPU 使用率都 80% 了,可我們挨個分析了一遍進程列表,還是找不到高 CPU 使用率的進程。看來 top 是不管用了,那還有其他工具可以查看進程 CPU 使用情況嗎?不知道你記不記得我們的老朋友 pidstat,它可以用來分析進程的 CPU 使用情況。

6.接下來,我們還是在第一個終端,運行 pidstat 命令:

# 間隔 1 秒輸出一組數據(按 Ctrl+C 結束)
$ pidstat 1
...
04 : 36 : 24      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
04 : 36 : 25        0      6882    1.00    3.00    0.00    0.00    4.00     0  docker-containe
04 : 36 : 25      101      6947    1.00    2.00    0.00    1.00    3.00     1  nginx
04 : 36 : 25        1     14834    1.00    1.00    0.00    1.00    2.00     0  php-fpm
04 : 36 : 25        1     14835    1.00    1.00    0.00    1.00    2.00     0  php-fpm
04 : 36 : 25        1     14845    0.00    2.00    0.00    2.00    2.00     1  php-fpm
04 : 36 : 25        1     14855    0.00    1.00    0.00    1.00    1.00     1  php-fpm
04 : 36 : 25        1     14857    1.00    2.00    0.00    1.00    3.00     0  php-fpm
04 : 36 : 25        0     15006    0.00    1.00    0.00    0.00    1.00     0  dockerd
04 : 36 : 25        0     15801    0.00    1.00    0.00    0.00    1.00     1  pidstat
04 : 36 : 25        1     17084    1.00    0.00    0.00    2.00    1.00     0  stress
04 : 36 : 25        0     31116    0.00    1.00    0.00    0.00    1.00     0  atopacctd
...

觀察一會兒,你是不是發現,所有進程的 CPU 使用率也都不高啊,最高的 Docker 和 Nginx 也只有 4% 和 3%,即使所有進程的 CPU 使用率都加起來,也不過是 21%,離 80% 還差得遠呢!

7.現在,我們回到第一個終端,重新運行 top 命令,並觀察一會兒:

$ top
top - 04 : 58 : 24 up 14 days, 15 : 47 1 user,  load average: 3.39 , 3.82 , 2.74
Tasks: 149 total,   6 running,  93 sleeping,   0 stopped,   0 zombie
%Cpu(s): 77.7 us, 19.3 sy,  0.0 ni,  2.0 id,  0.0 wa,  0.0 hi,  1.0 si,  0.0 st
KiB Mem :  8169348 total,  2543916 free,   457976 used,  5167456 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  7363908 avail Mem
 
   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  6947 systemd+  20   0   33104   3764   2340 S   4.0  0.0   0 : 32.69 nginx
  6882 root      20   0   12108   8360   3884 S   2.0  0.1   0 : 31.40 docker-containe
15465 daemon    20   0  336696  15256   7576 S   2.0  0.2   0 : 00.62 php-fpm
15466 daemon    20   0  336696  15196   7516 S   2.0  0.2   0 : 00.62 php-fpm
15489 daemon    20   0  336696  16200   8520 S   2.0  0.2   0 : 00.62 php-fpm
  6948 systemd+  20   0   33104   3764   2340 S   1.0  0.0   0 : 00.95 nginx
15006 root      20   0 1168608  65632  37536 S   1.0  0.8   9 : 51.09 dockerd
15476 daemon    20   0  336696  16200   8520 S   1.0  0.2   0 : 00.61 php-fpm
15477 daemon    20   0  336696  16200   8520 S   1.0  0.2   0 : 00.61 php-fpm
24340 daemon    20   0    8184   1616    536 R   1.0  0.0   0 : 00.01 stress
24342 daemon    20   0    8196   1580    492 R   1.0  0.0   0 : 00.01 stress
24344 daemon    20   0    8188   1056    492 R   1.0  0.0   0 : 00.01 stress
24347 daemon    20   0    8184   1356    540 R   1.0  0.0   0 : 00.01 stress
...

這次從頭開始看 top 的每行輸出,咦?Tasks 這一行看起來有點奇怪,就緒隊列中居然有 6 個 Running 狀態的進程(6 running),是不是有點多呢?
回想一下 ab 測試的參數,並發請求數是 5。再看進程列表里, php-fpm 的數量也是 5,再加上 Nginx,好像同時有 6 個進程也並不奇怪。但真的是這樣嗎?
再仔細看進程列表,這次主要看 Running(R) 狀態的進程。你有沒有發現, Nginx 和所有的 php-fpm 都處於 Sleep(S)狀態,而真正處於 Running(R)狀態的,卻是幾個 stress 進程。這幾個 stress 進程就比較奇怪了,需要我們做進一步的分析。

8.我們還是使用 pidstat 來分析這幾個進程,並且使用 -p 選項指定進程的 PID。首先,從上面 top 的結果中,找到這幾個進程的 PID。比如,先隨便找一個 24344,然后用 pidstat 命令看一下它的 CPU 使用情況:

$ pidstat -p 24344
 
16 : 14 : 55      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command

9.奇怪,居然沒有任何輸出。在懷疑性能工具出問題前,最好還是先用其他工具交叉確認一下,。那用什么工具呢? ps 應該是最簡單易用的。我們在終端里運行下面的命令,看看 24344 進程的狀態:

# 從所有進程中查找 PID 是 24344 的進程
$ ps aux | grep 24344
root      9628  0.0  0.0  14856  1096 pts/ 0    S+   16 : 15   0 : 00 grep --color=auto 24344

10.還是沒有輸出。現在終於發現問題,原來這個進程已經不存在了,所以 pidstat 就沒有任何輸出。既然進程都沒了,那性能問題應該也跟着沒了吧。我們再用 top 命令確認一下:

$ top
...
%Cpu(s): 80.9 us, 14.9 sy,  0.0 ni,  2.8 id,  0.0 wa,  0.0 hi,  1.3 si,  0.0 st
...
 
   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  6882 root      20   0   12108   8360   3884 S   2.7  0.1   0 : 45.63 docker-containe
  6947 systemd+  20   0   33104   3764   2340 R   2.7  0.0   0 : 47.79 nginx
  3865 daemon    20   0  336696  15056   7376 S   2.0  0.2   0 : 00.15 php-fpm
   6779 daemon    20   0    8184   1112    556 R   0.3  0.0   0 : 00.01 stress
...

11.好像又錯了。結果還跟原來一樣,用戶 CPU 使用率還是高達 80.9%,系統 CPU 接近 15%,而空閑 CPU 只有 2.8%,Running 狀態的進程有 Nginx、stress 等。
可是,剛剛我們看到 stress 進程不存在了,怎么現在還在運行呢?再細看一下 top 的輸出,原來,這次 stress 進程的 PID 跟前面不一樣了,原來的 PID 24344 不見了,現在的是 6779。
進程的 PID 在變,這說明什么呢?在我看來,要么是這些進程在不停地重啟,要么就是全新的進程,這無非也就兩個原因:

  • 第一個原因,進程在不停地崩潰重啟,比如因為段錯誤、配置錯誤等等,這時,進程在退出后可能又被監控系統自動重啟了。
  • 第二個原因,這些進程都是短時進程,也就是在其他應用內部通過 exec 調用的外面命令。這些命令一般都只運行很短的時間就會結束,你很難用 top 這種間隔時間比較長的工具發現(上面的案例,我們碰巧發現了)。

至於 stress,我們前面提到過,它是一個常用的壓力測試工具。它的 PID 在不斷變化中,看起來像是被其他進程調用的短時進程。要想繼續分析下去,還得找到它們的父進程。
12.要怎么查找一個進程的父進程呢?沒錯,用 pstree 就可以用樹狀形式顯示所有進程之間的關系:

$ pstree | grep stress
         |-docker-containe-+-php-fpm-+-php-fpm---sh---stress
         |         |- 3 *[php-fpm---sh---stress---stress]

從這里可以看到,stress 是被 php-fpm 調用的子進程,並且進程數量不止一個(這里是 3 個)。找到父進程后,我們能進入 app 的內部分析了。
分析原因:
perf 工具,它可以用來分析 CPU 性能事件,用在這里就很合適。依舊在第一個終端中運行 perf record -g 命令 ,並等待一會兒(比如 15 秒)后按 Ctrl+C 退出。然后再運行 perf report 查看報告:

# 記錄性能事件,等待大約 15 秒后按 Ctrl+C 退出
$ perf record -g
 
# 查看報告
$ perf report -g

結束案例:

$ docker rm -f nginx phpfpm

小結:
碰到常規問題無法解釋的 CPU 使用率情況時,首先要想到有可能是短時應用導致的問題,比如有可能是下面這兩種情況。

  • 第一,應用里直接調用了其他二進制程序,這些程序通常運行時間比較短,通過 top 等工具也不容易發現。
  • 第二,應用本身在不停地崩潰重啟,而啟動過程的資源初始化,很可會占用相當多的 CPU。

3.2 等待 I/O 的 CPU的使用(多進程 I/O 的案例)

進程狀態:當 iowait 升高時,進程很可能因為得不到硬件的響應,而長時間處於不可中斷狀態。從 ps 或者 top 命令的輸出中,你可以發現它們都處於 D 狀態,也就是不可中斷狀態。
首先使用top命令。查看系統的各個指標:

問題一:iowait升高
問題二:僵屍進程不斷增多

首先看問題一:iowait問題分析
首先、使用工具dstat 進程查看,它的好處是,可以同時查看 CPU 和 I/O 這兩種資源的使用情況,便於對比分析。

# 間隔 1 秒輸出 10 組數據
$ dstat 1 10

從 dstat 的輸出,我們可以看到,每當 iowait 升高(wai)時,磁盤的讀請求(read)都會很大。這說明iowait 的升高跟磁盤的讀請求有關,很可能就是磁盤讀導致的。
到底是哪個進程在讀磁盤呢?我們在top中可以看到有D狀態的不可中斷進程很可疑,我們來分析一下:

第二、使用pidstat分析D狀態的進程:

# -d 展示 I/O 統計數據,-p 指定進程號,間隔 1 秒輸出 3 組數據
# pidstat -d -p 116712 1 3

kB_rd 表示每秒讀的 KB 數, kB_wr 表示每秒寫的 KB 數,iodelay 表示 I/O 的延遲(單位是時鍾周期)。它們都是 0,那就表示此時沒有任何的讀寫,說明問題不是 116712進程導致的。
第三、使用pidstat 去掉-p參數,查看所有進程的IO情況:

# 間隔 1 秒輸出多組數據 (這里是 20 組)
pidstat -d 1 20

觀察一會兒可以發現,的確是 app 進程在進行磁盤讀,並且每秒讀的數據有 32 MB,看來就是 app 的問題。
第四、app 進程到底在執行啥 I/O 操作呢?

  • strace 正是最常用的跟蹤進程系統調用的工具。
# strace -p 116911

結果可以看到沒有權限。使用ps查看為啥沒有權限?

ps aux | grep 116921

可以看到該進程已經變成了僵屍進程,所以沒有權限查看。

  • 使用perf record -g 工具進行分析:
#等待 20 秒后,按ctrl+C 停止
perf record -g
  • 然后使用perf report -g 查看:

這個圖里的 swapper 是內核中的調度進程,可以先忽略。
app 的確在通過系統調用 sys_read() 讀取數據。並且從 new_sync_read 和 blkdev_direct_IO 能看出,進程正在對磁盤進行直接讀,也就是繞過系統緩存,每個讀請求都會從磁盤直接讀,這就可以解釋我們觀察到的 iowait 升高了。
小結:

  • 碰到 iowait 升高時,需要先用 dstat、pidstat 等工具,確認是不是磁盤 I/O 的問題,然后再找是哪些進程導致了 I/O。
  • 等待 I/O 的進程一般是不可中斷狀態,所以用 ps 命令找到的 D 狀態(即不可中斷狀態)的進程,多為可疑進程。但這個案例中,在 I/O 操作后,進程又變成了僵屍進程,所以不能用strace 直接分析這個進程的系統調用。
  • 這種情況下,我們用了 perf 工具來分析系統的 CPU 時鍾事件,最終發現是直接 I/O 導致的問題。

4.系統的軟中斷CPU使用率升高,該怎么辦?

預先安裝 docker、sysstat、sar 、hping3、tcpdump 等工具,比如 apt-get install docker.io sysstat hping3 tcpdump。

  • sar 是一個系統活動報告工具,既可以實時查看系統的當前活動,又可以配置保存和報告歷史統計數據。
  • hping3 是一個可以構造 TCP/IP 協議數據包的工具,可以對系統進行安全審計、防火牆測試等。
  • tcpdump 是一個常用的網絡抓包工具,常用來分析各種網絡問題。

本次案例用到兩台虛擬機。你可以看到,其中一台虛擬機運行 Nginx ,用來模擬待分析的 Web 服務器;而另一台當作 Web 服務器的客戶端,用來給 Nginx 增加壓力請求。使用兩台虛擬機的目的,是為了相互隔離,避免“交叉感染”。
同以前的案例一樣,下面的所有命令都默認以 root 用戶運行,如果你是用普通用戶身份登陸系統,請運行 sudo su root 命令切換到 root 用戶。
操作和分析
安裝完成后,我們先在第一個虛擬機,執行下面的命令運行案例,也就是一個最基本的 Nginx 應用:

# 運行 Nginx 服務並對外開放 80 端口
$ docker run -itd --name=nginx -p 80 : 80 nginx

然后,在第二個虛擬機,使用 curl 訪問 Nginx 監聽的端口,確認 Nginx 正常啟動。假設172.16.109.245 是 Nginx 所在虛擬機的 IP 地址,運行 curl 命令后你應該會看到下面這個輸出界面:

$ curl http: //172.16.109.245/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

接着,還是在第二個虛擬機,我們運行 hping3 命令,來模擬 Nginx 的客戶端請求:

# -S 參數表示設置 TCP 協議的 SYN(同步序列號),-p 表示目的端口為 80
# -i u100 表示每隔 100 微秒發送一個網絡幀
# 注:如果你在實踐過程中現象不明顯,可以嘗試把 100 調小,比如調成 10 甚至 1
$ hping3 -S -p 80 -i u100 172.16 . 109.245

現在我們再回到第一個虛擬機,你應該發現了異常。是不是感覺系統響應明顯變慢了,即便只是在終端中敲幾個回車,都得很久才能得到響應?這個時候應該怎么辦呢?

雖然在運行 hping3 命令時,這是一個 SYN FLOOD 攻擊,你肯定也會想到從網絡方面入手,來分析這個問題。不過,在實際的生產環境中,沒人直接告訴你原因。所以希望你把 hping3 模擬 SYN FLOOD 這個操作暫時忘掉,然后重新從觀察到的問題開始,分析系統的資源使用情況,逐步找出問題的根源。

那么,該從什么地方入手呢?剛才我們發現,簡單的 SHELL 命令都明顯變慢了,先看看系統的整體資源使用情況應該是個不錯的注意,比如執行下 top 看看是不是出現了 CPU 的瓶頸。我們在第一個終端運行 top 命令,看一下系統整體的資源使用情況。

top - 03 : 32 : 44 up  3 : 50 1 user,  load average: 0.75 , 0.98 , 0.98
Tasks: 287 total,   2 running, 216 sleeping,   0 stopped,   0 zombie
%Cpu0  :  4.0 us,  1.7 sy,  0.0 ni, 94.0 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu1  :  1.0 us,  0.3 sy,  0.0 ni, 49.2 id,  0.0 wa,  0.0 hi, 49.5 si,  0.0 st
KiB Mem :  4025720 total,  2419400 free,  1186524 used,   419796 buff/cache
KiB Swap:   969960 total,   551012 free,   418948 used.  2558736 avail Mem
 
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                               
     18 root      20   0       0      0      0 R   5.0  0.0  63 : 08.96 ksoftirqd/ 1                                                                                                                           
   1583 xhong     20   0 3394648 131844  39900 S   2.3  3.3   1 : 11.93 gnome-shell                                                                                                                           
   1450 xhong     20   0  418176  41028  11544 S   2.0  1.0   0 : 37.07 Xorg                                                                                                                                  
   1948 xhong     20   0  803616  28288  17176 S   1.7  0.7   0 : 18.58 gnome-terminal-                                                                                                                       
  33806 root      20   0   51340   3880   3124 R   1.0  0.1   0 : 00.11 top                                                                                                                                   
   2186 root      20   0 1099276  12968   6344 S   0.3  0.3   0 : 16.79 docker-containe                                                                                                                       
      1 root      20   0  159936   5608   3772 S   0.0  0.1   0 : 05.44 systemd                                                                                                                                                                                                                                               

我們從第一行開始,逐個看一下:

  • 平均負載在1左右,就緒隊列里面有2個進程(2 running)。
  • 每個 CPU 的使用率都挺低,si: 處理軟件中斷的CPU時間是49.5%
  • 再看進程列表,CPU 使用率最高的進程也只有 5%,還是不高呀。

根據上一期的內容,既然軟中斷可能有問題,那你先要知道,究竟是哪類軟中斷的問題。停下來想想,上一節我們用了什么方法,來判斷軟中斷類型呢?沒錯,還是 proc 文件系統。觀察 /proc/softirqs 文件的內容,你就能知道各種軟中斷類型的次數。

不過,這里的各類軟中斷次數,又是什么時間段里的次數呢?它是系統運行以來的累積中斷次數。所以我們直接查看文件內容,得到的只是累積中斷次數,對這里的問題並沒有直接參考意義。因為,這些中斷次數的變化速率才是我們需要關注的。

那什么工具可以觀察命令輸出的變化情況呢?我想你應該想起來了,在前面案例中用過的 watch 命令,就可以定期運行一個命令來查看輸出;如果再加上 -d 參數,還可以高亮出變化的部分,從高亮部分我們就可以直觀看出,哪些內容變化得更快。

比如,還是在第一個虛擬機,我們運行下面的命令:

$ watch -d cat /proc/softirqs
                     CPU0       CPU1
           HI:          0          0
        TIMER:    1083906    2368646
       NET_TX:         53          9
       NET_RX:    1550643    1916776
        BLOCK:          0          0
     IRQ_POLL:          0          0
      TASKLET:     333637       3930
        SCHED:     963675    2293171
      HRTIMER:          0          0
          RCU:    1542111    1590625

通過 /proc/softirqs 文件內容的變化情況,你可以發現, TIMER(定時中斷)、NET_RX(網絡接收)、SCHED(內核調度)、RCU(RCU 鎖)等這幾個軟中斷都在不停變化。

其中,NET_RX,也就是網絡數據包接收軟中斷的變化速率最快。而其他幾種類型的軟中斷,是保證 Linux 調度、時鍾和臨界區保護這些正常工作所必需的,所以它們有一定的變化倒是正常的。

那么接下來,我們就從網絡接收的軟中斷着手,繼續分析。既然是網絡接收的軟中斷,第一步應該就是觀察系統的網絡接收情況。這里你可能想起了很多網絡工具,不過,我推薦今天的主人公工具 sar 。

sar 可以用來查看系統的網絡收發情況,還有一個好處是,不僅可以觀察網絡收發的吞吐量(BPS,每秒收發的字節數),還可以觀察網絡收發的 PPS,即每秒收發的網絡幀數。

我們在第一個終端中運行 sar 命令,並添加 -n DEV 參數顯示網絡收發的報告:

# -n DEV 表示顯示網絡收發的報告,間隔 1 秒輸出一組數據
root @ubuntu :/home/xhong# sar -n DEV 1
Linux 4.18 . 0 - 16 -generic (ubuntu)    03 / 14 / 2019  _x86_64_    ( 2 CPU)
 
03 : 20 : 02 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
03 : 20 : 03 AM veth7734be2   9401.00  14402.00    532.44    759.48      0.00      0.00      0.00      0.06
03 : 20 : 03 AM     ens33  23260.00  14758.00   1362.89    864.73      0.00      0.00      0.00      1.12
03 : 20 : 03 AM        lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
03 : 20 : 03 AM   docker0   9779.00  14909.00    420.16    786.22      0.00      0.00      0.00      0.00

對於 sar 的輸出界面,我先來簡單介紹一下,從左往右依次是:

  • 第一列:表示報告的時間。
  • 第二列:IFACE 表示網卡。
  • 第三、四列:rxpck/s 和 txpck/s 分別表示每秒接收、發送的網絡幀數,也就是 PPS。
  • 第五、六列:rxkB/s 和 txkB/s 分別表示每秒接收、發送的千字節數,也就是 BPS。
  • 后面的其他參數基本接近 0,顯然跟今天的問題沒有直接關系,你可以先忽略掉。

我們具體來看輸出的內容,你可以發現:

對網卡ens33來說,每秒接收的網絡幀數比較大,達到了 23260,而發送的網絡幀數則比較小,只有 14758;每秒接收的千字節數只有 1362 KB,而發送的千字節數更小,只有 864 KB。

docker0 和veth7734be2的數據基本一致,只是發送和接收相反,發送的數據較大而接收的數據較小。這是 Linux 內部網橋轉發導致的,你暫且不用深究,只要知道這是系統把 ens33 收到的包轉發給 Nginx 服務即可。

既然懷疑是網絡接收中斷的問題,我們還是重點來看 ens33 :接收的 PPS 比較大,達到 23260,而接收的 BPS 卻很小,只有 1362 KB。直觀來看網絡幀應該都是比較小的,我們稍微計算一下,1362*1024/23260= 59 字節,說明平均每個網絡幀只有 59 字節,這顯然是很小的網絡幀,也就是我們通常所說的小包問題。

那么,有沒有辦法知道這是一個什么樣的網絡幀,以及從哪里發過來的呢?

使用 tcpdump 抓取 ens33 上的包就可以了。我們事先已經知道, Nginx 監聽在 80 端口,它所提供的 HTTP 服務是基於 TCP 協議的,所以我們可以指定 TCP 協議和 80 端口精確抓包。

接下來,我們在第一個終端中運行 tcpdump 命令,通過 -i ens33 選項指定網卡 ens33,並通過 tcp port 80 選項指定 TCP 協議的 80 端口:

# -i ens33 只抓取 ens33 網卡,-n 不解析協議名和主機名
# tcp port 80 表示只抓取 tcp 協議並且端口號為 80 的網絡幀
$ tcpdump -i ens33 -n tcp port 80
02 : 41 : 42.239723 IP 172.16 . 109.170 . 58080 > 172.16 . 109.245 . 80 : Flags [S], seq 1664120269 , win 512 , length 0
...

從 tcpdump 的輸出中,你可以發現172.16.109.170.58080 > 172.16.109.245.80,表示網絡幀從 172.16.109.170 的 58080 端口發送到 172.16.109.245 的 80 端口,也就是從運行 hping3 機器的 58080 端口發送網絡幀,目的為 Nginx 所在機器的 80 端口。

Flags [S] 則表示這是一個 SYN 包。

再加上前面用 sar 發現的, PPS 超過 23260 的現象,現在我們可以確認,這就是從 172.16.109.170 這個地址發送過來的 SYN FLOOD 攻擊。

到這里,我們已經做了全套的性能診斷和分析。從系統的軟中斷使用率高這個現象出發,通過觀察 /proc/softirqs 文件的變化情況,判斷出軟中斷類型是網絡接收中斷;再通過 sar 和 tcpdump ,確認這是一個 SYN FLOOD 問題。

SYN FLOOD 問題最簡單的解決方法,就是從交換機或者硬件防火牆中封掉來源 IP,這樣 SYN FLOOD 網絡幀就不會發送到服務器中。

案例結束后,也不要忘了收尾,記得停止最開始啟動的 Nginx 服務以及 hping3 命令。

在第一個終端中,運行下面的命令就可以停止 Nginx 了:

# 停止 Nginx 服務
$ docker rm -f nginx

然后到第二個終端中按下 Ctrl+C 就可以停止 hping3。

小結:

軟中斷 CPU 使用率(softirq)升高是一種很常見的性能問題。雖然軟中斷的類型很多,但實際生產中,我們遇到的性能瓶頸大多是網絡收發類型的軟中斷,特別是網絡接收的軟中斷。

在碰到這類問題時,你可以借用 sar、tcpdump 等工具,做進一步分析。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM