記錄課程實踐過程,推薦前往 Linux 效能優化筆記 學習更多。:fire:
環境準備
- 兩臺虛擬機器(Ubuntu 18.04)
- 機器配置:2 CPU,8GB 記憶體
- 預先安裝 docker、sysstat、perf、ab 等工具,如 apt install docker.io sysstat linux-tools-common apache2-utils
操作和分析
其中一臺用作 Web 伺服器,來模擬效能問題;另一臺用作 Web 伺服器的客戶端,來給 Web 服務增加壓力請求。
開啟兩個終端,分別 SSH 登入到兩臺機器上,並安裝上面提到的工具。
預設以 root 使用者執行所有命令,先執行 sudo su root 命令切換到 root 使用者。
由於 Nginx 和 PHP 的配置比較麻煩, 課程提供了兩個Docker 映象,這樣只需要執行兩個容器,就可以得到模擬環境。
執行docker環境
首先,在第一個終端執行下面的命令來執行 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
下載過程可能比較緩慢... 下載完成後,執行 docker ps -a ,如圖:
第二個終端使用 curl 訪問 [http://[VM1] 的 IP]:10000,確認 Nginx 已正常啟動。
# 192.168.139.139是第一臺虛擬機器的IP地址
$ curl http://192.168.139.139:10000/
如圖:
壓力測試一
在第二個終端執行下面的 ab 命令:
# 併發100個請求測試Nginx效能,總共測試1000個請求
$ ab -c 100 -n 1000 http://192.168.139.139:10000/
如圖:
從 ab 的輸出結果我們可以看到,Nginx 能承受的每秒平均請求數,只有 63 多一點,效能相對較差。如果暫時感覺不到什麼,那嘗試用 top 和 pidstat 再做觀察。
壓力測試二
繼續在第二個終端,執行 ab 命令:
//併發數設定為 5,同時把請求時長設定為 10 分鐘(-t 600)
$ ab -c 5 -t 600 http://192.168.139.139:10000/
觀察 top 輸出資訊
回到第一個終端執行 top 命令,並按下數字 1 ,切換到每個 CPU 的使用率:
這裡可以觀察下,程式列表裡CPU使用率最高的也只是2.6%,並不是很多。然而,再看 %Cpu 這一行,你會發現,系統的整體 CPU 使用率是比較高的:使用者 CPU 使用率(us)已經到了 77.6%,系統 CPU 為 16.8%,而空閒 CPU (id)則只有 0.3%。
這裡開始有點疑惑,為何使用者 CPU 使用率會這麼高呢?重新分析下程式列表:
- containerd-shim 應該是跟docker容器相關的, containerd-shim相關文章;
- Nginx 和 php-fpm 是執行 Web 服務的,它們會佔用一些 CPU 也不意外,並且 2% 左右的 CPU 使用率也不算高;
- 後面的幾個 0.3% 的程式,也不太像是導致使用者CPU使用率驟然升高的原因。
也就是說,目前 top 命令顯示的資訊不足以解答我們遇到的問題。那嘗試 pidstat 命令吧,它可以用來分析程式的 CPU 使用情況。
觀察 pidstat 輸出資訊
保持壓力測試的情況下,在第一個終端執行以下命令,觀察輸出資訊:
# 間隔1秒輸出一組資料(按Ctrl+C結束)
$ pidstat 1
如圖:
可以隨機挑選兩組資料,基本上總和不會超過25%,遠遠沒有達到77%。問題似乎卡在這裡了,明明使用者CPU使用率很高,但卻找不到具體程式,下一步該怎麼分析呢?也許是遺漏了某些關鍵資訊。
壓力測試三
重新壓測,然後在第一個終端執行 top 命令,持續觀察一段時間(很重要),下圖只是某個瞬間的狀態:
重新觀察 top 輸出資訊
僅分析此圖,Tasks 這一行中,就緒佇列中有 8 個 Running 狀態的程式(8 running)。 8 running 似乎跟初始設定的併發數 5 沒有相差太多,不過圖中顯示php-fpm、nginx等程式大部分時間都處於 S(休眠)狀態,處於 R 狀態更多的是 stress 程式。
那接下來就分析下具體的 stress 程式吧,
// -p 指定具體的程式 PID
pidstat -p XXX
如圖:
發現這幾個程式實際已經消失了,不過,在壓測過程繼續觀察top介面,你還是可以看到有新的 stress 程式處於 R 狀態。
是不是可以做個猜想,要麼是這些程式在不停地崩潰、重啟,要麼就是全新的程式,具體分析:
- 第一種情況,程式在不停地崩潰重啟,比如因為段錯誤、配置錯誤等等,這時,程式在退出後可能又被監控系統自動重啟了。
- 第二種情況,這些程式都是短時程式,也就是在其他應用內部通過 exec 呼叫的外面命令。這些命令一般都只執行很短的時間就會結束,你很難用 top 這種間隔時間比較長的工具發現(如壓測二)。
其實 stress,它是一個常用的壓力測試工具。它的 PID 在不斷變化中,看起來像是被其他程式呼叫的短時程式。要想繼續分析下去,還得找到它們的父程式。
pstree 分析程式呼叫關係
// pstree 可以用樹狀形式顯示所有程式之間的關係:
$ pstree | grep stress
如圖:
可以看到,stress 是被 php-fpm 呼叫的子程式,而且很多時候不止一個(5* 代表5個),那麼還是檢視下 php-fpm 的應用原始碼吧。
# 拷貝原始碼到本地
$ docker cp phpfpm:/app .
# grep 查詢看看是不是有程式碼在呼叫stress命令
$ grep stress -r app
如圖:
發現 app/index.php 確實呼叫到了 stress 命令,再檢視具體程式碼。如圖:
根據程式碼註釋,stress 會通過 write() 和 unlink() 對 I/O 程式進行壓測,也就是說會對系統I/O造成壓力,但回過頭看之前 top 輸出的資料,並沒見到 iowait (wa)升高,反而是使用者 CPU(us) 和系統 CPU (sy)升高。
檢查輸出日誌資訊
直覺來說,我會認為它根本沒有走到最終呼叫 I/O 程式的地方。根據程式碼,傳入引數 verbose=1 檢視具體輸出資訊吧,如下:
curl http://192.168.139.139:10000?verbose=1
可以看到,提示 Permission denied 沒有許可權建立臨時檔案,同時提示 failed run 退出了。
至此,可以猜想一下,由於許可權錯誤,大量的 stress 程式在啟動時初始化失敗,進而導致使用者 CPU 使用率的升高。
perf 分析CPU效能事件
那麼,怎麼驗證是否有大量的 stress 程式呢?既然 top、pidstat 工具無法解決問題,嘗試 perf 工具吧,它可以用來分析 CPU 效能事件。如下:
# 記錄效能事件,等待大約15秒後按 Ctrl+C 退出
$ perf record -g
# 檢視報告
$ perf report
保持壓力測試,在終端一執行命令,如圖:
確實,stress 佔了所有 CPU 時鐘事件的 67%。你還可以通過 enter 鍵展開 + 號位置,檢視 stress 的呼叫棧詳情,如下:
找到問題的根源,隨後的優化就很簡單了,只要修復許可權問題,並減少或刪除 stress 的呼叫,就可以減輕系統的 CPU 壓力。
結語
圖例有點多,如果有不明白的,也可以在評論區留言。後續有機會再分享其他案例。:smile:
本作品採用《CC 協議》,轉載必須註明作者和本文連結