浅谈 Linux 高负载的系统化分析
簡介:?淺談 Linux 高負載的系統化分析,阿里云系統組工程師楊勇通過對線上各種問題的系統化分析。
講解 Linux Load 高如何排查的話題屬于老生常談了,但多數文章只是聚焦了幾個點,缺少整體排查思路的介紹。所謂 “授人以魚不如授人以漁”。本文試圖建立一個方法和套路,來幫助讀者對 Load 高問題排查有一個更全面的認識。
從消除誤解開始
沒有基線的 Load,是不靠譜的 Load
從接觸 Unix/Linux 系統管理的第一天起,很多人就開始接觸 System Load Average 這個監控指標了,然而,并非所有人都知道這個指標的真正含義。一般說來,經常能聽到以下誤解:
- Load 高是 CPU 負載高……
傳統 Unix 于 Linux 設計不同。Unix 系統,Load 高就是可運行進程多引發的,但對 Linux 來說不是。對 Linux 來說 Load 高可能有兩種情況:
- 系統中處于 R 狀態的進程數增加引發的
- 系統中處于 D 狀態的進程數增加引發的
- Loadavg 數值大于某個值就一定有問題……
Loadavg 的數值是相對值,受到 CPU 和 IO 設備多少的影響,甚至會受到某些軟件定義的虛擬資源的影響。Load 高的判斷需要基于某個歷史基線 (Baseline),不能無原則的跨系統去比較 Load。 - Load 高系統一定很忙…..
Load 高系統可以很忙,例如 CPU 負載高,CPU 很忙。但 Load 高,系統不都很忙,如 IO 負載高,磁盤可以很忙,但 CPU 可以比較空閑,如 iowait 高。這里要注意,iowait 本質上是一種特殊的 CPU 空閑狀態。另一種 Load 高,可能 CPU 和磁盤外設都很空閑,可能支持鎖競爭引起的,這時候 CPU 時間里,iowait 不高,但 idle 高。
Brendan Gregg 在最近的博客 [Linux Load Averages: Solving the Mystery] (http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html) 中,討論了 Unix 和 Linux Load Average 的差異,并且回朔到 24 年前 Linux 社區的討論,并找到了當時為什么 Linux 要修改 Unix Load Average 的定義。文章認為,正是由于 Linux 引入的 D 狀態線程的計算方式,從而導致 Load 高的原因變得含混起來。因為系統中引發 D 狀態切換的原因實在是太多了,絕非 IO 負載,鎖競爭這么簡單!正是由于這種含混,Load 的數值更加難以跨系統,跨應用類型去比較。所有 Load 高低的依據,全都應該基于歷史的基線。本微信公眾號也曾寫過一篇相關文章,可以參見Linux Load Average那些事兒。
如何排查 Load 高的問題
如前所述,由于在 Linux 操作系統里,Load 是一個定義及其含混的指標,排查 loadavg 高就是一個很復雜的過程。其基本思路就是,根據引起 Load 變化的根源是 R 狀態任務增多,還是 D 狀態任務增多,來進入到不同的流程。
這里給出了 Load 增高的排查的一般套路,僅供參考:
在 Linux 系統里,讀取 /proc/stat 文件,即可獲取系統中 R 狀態的進程數;但 D 狀態的任務數恐怕最直接的方式還是使用 ps 命令比較方便。而 /proc/stat 文件里 procs_blocked 則給出的是處于等待磁盤 IO 的進程數:
通過簡單區分 R 狀態任務增多,還是 D 狀態任務增多,我們就可以進入到不同的排查流程里。下面,我們就這個大圖的排查思路,做一個簡單的梳理。
R 狀態任務增多
即通常所說的 CPU 負載高。此類問題的排查定位主要思路是系統,容器,進程的運行時間分析上,找到在 CPU 上的熱點路徑,或者分析 CPU 的運行時間主要是在哪段代碼上。
CPU user 和 sys 時間的分布通常能幫助人們快速定位與用戶態進程有關,還是與內核有關。另外,CPU 的 run queue 長度和調度等待時間,非主動的上下文切換 (nonvoluntary context switch) 次數都能幫助大致理解問題的場景。
因此,如果要將問題的場景關聯到相關的代碼,通常需要使用 perf,systemtap, ftrace 這種動態的跟蹤工具。
關聯到代碼路徑后,接下來的代碼時間分析過程中,代碼中的一些無效的運行時間也是分析中首要關注的,例如用戶態和內核態中的自旋鎖 (Spin Lock)。
當然,如果 CPU 上運行的都是有非常意義,非常有效率的代碼,那唯一要考慮的就是,是不是負載真得太大了。
D 狀態任務增多
根據 Linux 內核的設計, D 狀態任務本質上是 TASK_UNINTERRUPTIBLE 引發的主動睡眠,因此其可能性非常多。但是由于 Linux 內核 CPU 空閑時間上對 IO 棧引發的睡眠做了特殊的定義,即 iowait,因此iowait 成為 D 狀態分類里定位是否 Load 高是由 IO 引發的一個重要參考。
當然,如前所述, /proc/stat 中的 procs_blocked 的變化趨勢也可以是一個非常好的判定因 iowait引發的 Load 高的一個參考。
CPU iowait 高
很多人通常都對 CPU iowait 有一個誤解,以為 iowait 高是因為這時的 CPU 正在忙于做 IO 操作。其實恰恰相反, iowait 高的時候,CPU 正處于空閑狀態,沒有任何任務可以運行。只是因為此時存在已經發出的磁盤 IO,因此這時的空閑狀態被標識成了 iowait ,而不是 idle。
但此時,如果用 perf probe 命令,我們可以清楚得看到,在 iowait 狀態的 CPU,實際上是運行在 pid 為 0 的 idle 線程上:
相關的 idle 線程的循環如何分別對 CPU iowait 和 idle 計數的代碼,如下所示:
而 Linux IO 棧和文件系統的代碼則會調用 io_schedule,等待磁盤 IO 的完成。這時候,對 CPU 時間被記為 iowait 起關鍵計數的原子變量 rq->nr_iowait 則會在睡眠前被增加。注意,io_schedule 在被調用前,通常 caller 會先將任務顯式地設置成 TASK_UNINTERRUPTIBLE 狀態:
CPU idle 高
如前所述,有相當多的內核的阻塞,即 TASK_UNINTERRUPTIBLE 的睡眠,實際上與等待磁盤 IO 無關,如內核中的鎖競爭,再如內存直接頁回收的睡眠,又如內核中一些代碼路徑上的主動阻塞,等待資源。
Brendan Gregg 在最近的博客 [Linux Load Averages: Solving the Mystery] (http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html)中,使用 perf 命令產生的 TASK_UNINTERRUPTIBLE 的睡眠的火焰圖,很好的展示了引起 CPU idle 高的多樣性。本文不在贅述。
因此,CPU idle 高的分析,實質上就是分析內核的代碼路徑引起阻塞的主因是什么。通常,我們可以使用 perf inject 對 perf record 記錄的上下文切換的事件進行處理,關聯出進程從 CPU 切出 (swtich out) 和再次切入 (switch in) 的內核代碼路徑,生成一個所謂的 Off CPU 火焰圖.
當然,類似于鎖競爭這樣的比較簡單的問題,Off CPU 火焰圖足以一步定位出問題。但是對于更加復雜的因 D 狀態而阻塞的延遲問題,可能 Off CPU 火焰圖只能給我們一個調查的起點。
例如,當我們看到,Off CPU 火焰圖的主要睡眠時間是因為 epoll_wait 等待引發的。那么,我們繼續要排查的應該是網絡棧的延遲,即本文大圖中的 Net Delay 這部分。
至此,你也許會發現,CPU iowait 和 idle 高的性能分析的實質就是 延遲分析。這就是大圖按照內核中資源管理的大方向,將延遲分析細化成了六大延遲分析:
- CPU 延遲
- 內存延遲
- 文件系統延遲
- IO 棧延遲
- 網絡棧延遲
- 鎖及同步原語競爭
任何上述代碼路徑引發的 TASK_UNINTERRUPTIBLE 的睡眠,都是我們要分析的對象!
以問題結束
限于篇幅,本文很難將其所涉及的細節一一展開,因為讀到這里,你也許會發現,原來 Load 高的分析,實際上就是對系統的全面負載分析。怪不得叫 System Load 呢。這也是 Load 分析為什么很難在一篇文章里去全面覆蓋。
本文也開啟了淺談 Linux 性能分析系列的第一章。后續我們會推出系列文章,就前文所述的六大延遲分析,一一展開介紹,敬請期待……
關于作者
楊勇 (Oliver Yang),Linux 內核工程師,來自阿里云系統組。曾就職于 EMC,Sun 中國工程研究院,在存儲系統和 Solaris 內核開發領域工作。
原文鏈接
本文為阿里云原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的浅谈 Linux 高负载的系统化分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle数据到MaxCompute乱
- 下一篇: Flink on Zeppelin 流计