闻茂泉:系统性能监控与分析的工程化实践之路
點擊上方“朱小廝的博客”,選擇“設為星標”
后臺回復"書",獲取
后臺回復“k8s”,可領取k8s資料
一、系統性能分析工具ssar功能定位
說起性能分析就不得不提到《性能之巔》這本書,它是業界里程碑式的經典書籍。在書中第4章觀測工具部分,Brendan告訴我們觀測工具主要包括:計數器(Counters)、跟蹤(Tracing)、采樣(Profiling)和監控(Monitoring)幾大類。
依據數據獲取方式和數據實時性,將性能觀測工具做了分類:
1)左上角A區是直接讀取計數器實時數據的一些系統命令,top、ps這些命令我們都很常用,它們讀取的是/proc/這個目錄的信息,這里面的數據是內核幫我們記賬出來的。
2)左下角B區也是基于計數器的?,但它是記錄歷史信息的工具,比如說最常用的就是sar工具,下文我們統稱這類工具為系統性能監控工具。在阿里內部也有tsar工具,國外還有開源軟件atop。系統性能監控工具一方面可以回朔歷史數據,同時也提供實時模式數據。
3)右上角C區跟蹤采樣工具,內核的tracepoint,kprobe等就是跟蹤類工具,perf工具主要是采樣類工具,下文我們統稱這兩類工具為跟蹤采樣工具。目前這些工具都是只獲取實時的數據。如果不結合其他工具,單純使用它們來追查歷史數據,它們是無法提供的。
4)右下角D區,我們認為可以通過B區和C區的工具協同使用達到目標。
我們在性能分析工程實踐中重點聚焦在左下B區和右上C區藍框2個象限的建設上。今天我們主要探討的就是B區的系統性能監控工具建設情況。以后再給大家介紹C區的工具建設情況。
系統性能監控工具和跟蹤采樣工具各有優勢和特點,我們認為應該挖據各自潛力、發揮各自優勢,讓他們都最大限度在系統性能分析工作中發揮恰當的作用,切不可偏頗于任何一方。
在對系統性能監控和跟蹤采樣工具的具體理解上,我們認同如下3個理念:
1)內核計數器(Counters)信息獲取代價低,無額外開銷。相比較而言,跟蹤采樣工具或多或少都有一些運行開銷,如kprobe的使用還可能會引起一些穩定性風險。因此,我們傾向于最大化挖掘計數器信息的價值,給跟蹤采樣工具減負。比如要獲知機器直接內存回收的信息時,內核計數器已經提供了更低代價獲取的直接內存回收和異步內存回收的指標,就完全沒必要使用ftrace監控直接內存回收的情況了。另一方面,對于內核計數器不能涵蓋的細顆粒度內核數據,還必須要依賴內核跟蹤采樣工具獲取。比如當IOPS較高時,我們想了解具體的每一個IO讀寫的具體文件信息,內核計數器中完全沒有相關信息。只有讓跟蹤采樣工具專注于自己的核心任務,才更有條件打磨出更加穩定和可靠的產品級工具方案。畢竟只專注于2件事,比同時干10件事更容易干的出色。
2)現有的系統性能監控工具,除單機監控工具外,還有很多白屏化的監控平臺。白屏化監控平臺一般都是將數據采集到中心數據庫,然后再集中展示。白平化監控平臺采集更詳細的計數器信息時,總會遇到存儲成本的壓力的問題,因此不宜將過多計數器指標采集到白屏化監控平臺。但同時對于一些高頻使用的常規指標,如CPU、內存和網絡使用率情況,使用白屏化監控平臺展示,確實可以大大提升可觀測性。所以高頻常規指標使用白屏化監控平臺的同時,仍然需要依賴一個數據更加豐富的黑屏系統性能監控工具作為補充,以應對一些關鍵時刻必須依賴的低頻計數器數據。
3)傳統的黑屏系統性能監控工具,多年來一直相對比較固化,歷數2010年、2015年、2020年指標內容變化不是特別大。舉個例子,新版本內核的計數器中,網絡擴展指標多達400多個,僅TCP擴展指標就有116個,但是實際上常規的系統性能監控在TCP網絡指標這一塊,也就使用了不超過15個指標,大部分指標價值并沒有被挖掘出來。這些網絡的內核計數器指標,在很多網絡相關問題發生時,都特別有使用價值。
基于這些理念,我們認為研發一款數據指標更全、迭代周期更短、性能更加穩定的系統性能監控工具,以應對日益復雜系統性能問題是很必要的。
今天我們要介紹的阿里自研ssar工具,就是這樣一款系統性能監控類工具,并且已經在知名的操作系統社區龍蜥開源。它幾乎涵蓋了傳統系統性能監控sar工具的所有功能,同時擴展了更多的整機級別的指標,新增了進程級指標和特色的load指標,load高問題的診斷是這個工具一個獨特的功能。
開源軟件atop也是這樣一個類似功能的系統性能監控工具。公開渠道了解到友商也在大規模部署atop工具,這說明在業界其他互聯網公司也感受到了擁有這樣一個功能定位的監控工具的重要性。
下文我會用幾個例子說明我們是如何結合使用系統性能監控工具和跟蹤采樣工具的。
二、系統性能分析工具ssar介紹
系統性能監控工具ssar開源地址:
https://gitee.com/anolis/tracing-ssar.git
其中Reference_zh-CN.md是詳細的中文幫助手冊,package目錄中有rpm和deb包提供,其他操作系統可以自行編譯打包。
ssar工具分為采集器、內層的通用查詢器、外層的增強查詢器和經典查詢器幾部分。
1)采集器sresar:C語言實現的一個常駐進程,將數據記錄到本地磁盤,采集數據內容包括:
???(a) 按文件單位采集的整機數據,meminfo、stat、vmstat等;
???(b) 包含24個指標的進程級數據;
???(c) 獨特的load5s指標和詳細的R或D狀態線程詳情數據;
2)通用查詢器ssar命令:c++語言實現,負責按文件名、某行、某列等通用規則對文件數據進行邏輯解析。
3)古典查詢器tsar2:python語言實現,對ssar命令進行封裝,全面兼容tsar命令;
4)增強查詢器ssar+:python語言實現,對ssar命令進行封裝,規劃上是未來ssar工具的主要核心,適合于把較復雜的數據邏輯放在python語言實現層。
2.1、系統性能分析工具ssar支持快速迭代
傳統sar工具只采集一些固定指標。在使用C語言采集的同時進行指標解析的模式下,不是極難擴展新指標,就是擴展新指標迭代周期特別長。
ssar工具完全顛覆了傳統sar工具的架構設計,在產品設計和程序架構上做了很多變化,可以讓我們快速、低成本的增加新的計數器指標。
1)如果我們關注一個新的指標,但是又沒有被采集。對于修改傳統的系統性能監控工具sar來說,修改發布迭代周期是非常長的,發布下來可能要數周到數月。在學習內核知識或者解決生產問題的時候,新問題等不起這么久。但ssar工具以文件為采集單位,不需要修改代碼,直接改一下配置文件,重啟srerar采集進程就可以采集一個新的數據源文件。新增采集文件可在sys.conf文件中的file區域增加一行配置項,其中sre_path為數據源位置,cfile為數據文件存儲名,turn為開啟采集。
2)ssar把一些通用處理邏輯都抽象到ssar命令通用查詢器里,配置文件采集后,只需一條ssar命令查詢顯示數據,完美實現分鐘級周期的迭代。其中cfile指定存儲文件名,line指定第3行,column指定第5到15行,metric指定顯示原值,alias指定指標名稱。
3)更加復雜的邏輯,ssar把它轉移到外層python語言的查詢器中實現。這個時候你很容易通過在python中的修改來適應數據解析的變化。內核中有些指標伴隨著內核版本的變化格式也在變化,比如TCP的TimeWaitOverflow指標在3.10、4.9和4.19版中所處的列數就不同。此時可以python語言中輕松獲取column值,傳遞給ssar通用查詢器。即使面對未來內核版本中的未知變化,我們也可以通過python查詢器應付自如。可隨時調試和升級python查詢器,如cp tsar2 ?/tmp/test.py,不論是單機環境debug,還是腳本批量下發,均可輕量級操作。
2.2、系統性能分析工具ssar不占用額外存儲空間
相比較傳統系統性能監控工具,ssar的特點是能記錄更加豐富的計數器 (Counters) 指標信息,但這也會占用更多的磁盤存儲空間。ssar工具默認記錄7天歷史數據,通常占用磁盤空間不超過1GB。近20年來,隨著存儲技術的發展,單位空間的磁盤存儲成本價格下降到千分之一。適當提高系統性能監控工具的采集數據量是有一定可行性的。
為了追求更加完美的磁盤空間穩定性,ssar仍然設計了更加積極的磁盤存儲空間策略。還設置了專門的參數,當磁盤空間使用率達到閾值(默認90%)后,會停止數據采集。不但如此,當由于其他進程原因使磁盤空間數據繼續增長并超過90%時,ssar會啟動對7天內較早歷史數據的刪除工作,直到磁盤空間使用率小于90%或將ssar歷史數據基本刪除。
用圖示表示,圖中藍色的部分就是A、B、C、D中各臺機器的原來磁盤空間使用情況,有多些的,有少些的。
傳統的系統性能監控工具會占用每臺機器固定的磁盤存儲空間,相當于不區分各個機器的磁盤空間使用現狀,每臺機器都增加圖中紅色部分的磁盤空間占用。當系統性能監控工具采集數據量較大后,必須重新規劃磁盤分區的存儲空間。
ssar系統性能監控工具,使用的是圖中這個綠色區域的存儲空間。無需重新規劃磁盤分區的存儲空間。
實際生產中磁盤空間的規劃,一般都會保留一定的free空間作為buffer,應對突發情況。傳統系統性能監控工具所在磁盤分區必須相應的規劃一個額外的磁盤空間,而ssar工具的這種磁盤空間處理邏輯不用規劃額外的磁盤空間。唯一的缺點就是磁盤打滿同時又需要消費ssar數據的場景,但這個場景概率極低。
三、ssar工具診斷linux?load和背后索歷程
傳統的系統性能監控工具中的load1指標盡管比load5和load15指標更精準,仍然不能滿足排查問題時的時間維度的精準度要求。ssar在國內外全行業獨創了load5s指標,該指標可以讓我們將load的準確性提升到5秒級的精度。load5s指標的準確性絕不僅體現在采集頻率上,簡單說load5s指標就是R+D的線程數,也是內核數據結構中的全局變量active值。
下面通過一個實驗來帶大家理解load5s指標。我們找一臺實驗機器,我們在終端窗口A查看load,沒有任何負載壓力load1非常低。我們現在再開一個終端窗口B,運行一個模擬負載壓力的命令,命令內容是“stress -t 40 -c 1000”,其中stress命令需要自己安裝。stress 命令主要用來模擬系統負載較高時的場景,這里并發啟動1000個消耗CPU資源的單線程進程,就是1000個線程,并且控制只運行40秒后就停止。我們測試的stress命令是15分10秒執行的,經過40秒就是15分50秒結束。stress命令執行結束后,我們再回到終端A使用ssar load5s命令查看運行效果。
依次分析實驗的結果:
1)綠色時間區域15分09秒時,load5s和load1都還處于一個低水位,毫無疑問說明當時機器負載壓力很低。
2)伴隨著15分10秒stress命令開始執行后,紅色時間區域load5s和load1都同時升高, 15分14秒采集時刻,load5s值已經達到1003,load1開始升高到80.4,但是這個load1的值遠遠不能體現出當前機器上并發運行1000個負載壓力的線程的狀況。隨著時間的推移,15分46秒這個時刻,load1的值緩慢升到了489.16。
3)15分50秒1000個stress并發進程退出,在藍色時間區域15分52秒這個時刻的load5s值也同時迅速降低到了很低的值0。但是我們看到15分52秒這個時刻的load1值,依然有449.99這么高,這里的load1值看上去是及其令人迷惑的。
上面這個實驗說明load5s才是更準確反映系統負載壓力的指標,而單純用load1值判斷機器的負載是不準確的。所以我們需要用load5s指標替代load1指標來精準判斷機器負載發生的時間范圍。除load5s指標外,上面的解決方案中,還提供了一組指標用于全面的評估系統負載情況。其中load5s是R+D狀態的線程數之和,runq是當時的R狀態線程數,threads是所有狀態線程數總和,因此threads值是load5s值的天花板,threads最大值受內核參數設置限制。
ssar工具還會根據load5s和CPU核數之比,來觸發對load詳情的采集,前邊的actr是采集到并發的R狀態線程數,actd是采集到并發的D狀態線程數,act是actr和actd數之和。當我們需要了解load構成的詳細因素時,可以借助load2p子命令進一步顯示actr和actd的詳情數據。
雖然load5s指標體系能如此精準的反應系統的負載壓力情況,但我們并沒有依賴kprobe、ebpf等任何跟蹤采樣技術。load5s指標完全在用戶態通過工程化的方法巧妙獲取,充分體現了我們“最大化挖掘計數器歷史信息價值”的理念。
如此神奇的load5s指標,在最初的發現和理解過程,還曾經深入借助了跟蹤采樣工具的支持,下面按時間線來回顧下這個歷程。
1)首先結合systemtap工具的使用,同時閱讀內核load部分的內核代碼,了解到linux load的計算邏輯是在calc_global_load函數中獲取一個active全局變量(active包含R和D兩種狀態的線程數量),然后按照每隔5秒鐘的頻率采樣,最后由近及遠按不同權重計算出load值。歸納起來的公式就是:
load1(n)??=func1(load1(n-1), active(n))
load1(n-1) ?=func1(load1(n-2), active(n-1))
當前的這個load1值,是拿5秒鐘前的上一個load1跟當前時刻這個active值做一個計算得出來的。那上一個時刻load1值就是上上一個時刻load1值和上一個時刻active值計算出來的,所以這是一個無限遞歸的過程。
2)遺憾的是內核并沒有暴露active變量值,但我們可以借助一個kprobe內核模塊hook內核變量并通過proc接口暴露給用戶空間。可參考本人較早文章《深度好文:全面解析 Linux Load 》,其中有如下代碼:
git clone https://github.com/os-health/load5s.git
通過暴露的內核變量值,運行load預測腳本,按照內核里面load的計算方法,在用戶空間重算一遍load值,結果如圖。
看到內核吐出的current_load值25.07和我們shell腳本預測的predict_load值25.07完全一樣,說明在load_predict.sh腳本中計算邏輯就是內核對load值的計算邏輯。這個預測值25.07是根據上一個load1值24.38和load5s值的33(load5s就是active值)計算而來。
3)由此,我們完全可以反其道而行。既然是這個25.07是通過這24.38和33這兩個值算出來的,我們完全可以監控這個load1的值的變化,然后每5秒鐘去把這個active值反算出來就可以了。
active(n)???= func2(load1(n)?,?load1(n-1))
通過在用戶空間反算active值,使其在工程上具有極大的通用性和可靠性,我們將active值命名為load5s。
四、ssar工具診斷sys CPU高和背后探索歷程
在load高的各種場景中,有一種R狀態線程數并發多的load高是由于sys CPU使用率偏高引起的。ssar全面的指標體系,從多個角度將這種場景發生過程進行了透徹的呈現。
為了準確的說明問題,有必要回顧下內核內存回收的相關概念。如圖所示,當整機free內存低于黃線low閾值時,內核異步內存回收線程kswapd開始被喚醒,kswapd會在其他進程申請內存的同時回收內存。當整機free內存到達紅線min閾值時,觸發整機直接內存回收,所有來自用戶空間的內存申請將被阻塞住,線程狀態同時轉換為D狀態。此時只有來自內核空間的內存申請可以繼續使用min值以下的free內存。后續當整機free內存逐步恢復到綠線high閾值以上后,kswapd線程停止內存回收工作。
下面以ssar的數據指標為依據,一步一步的展示了當整機內存緊張后是如何引起sys CPU高,并進而引發load高的完整過程:
1)用戶空間java進程在20點43分到20點45分2分鐘內大量申請24GB內存;
2)整機內存used在20點43分到20點45分2分鐘內迅速增長了26GB,同時整機free內存迅速減少了14GB;
3)free內存在20點45分時只有3GB,低于low閾值,pgscan_kswapd/s值非0表明觸發kswapd異步內存回收。
4)kswapd異步內存回收跟不上進程內存申請的速度,當free內存達到min閾值時,pgscan_direct/s值非0表明觸發直接內存回收,用戶空間內存申請進程D住。棧頂函數sleep_one_page和congestion_wait等都是典型的直接內存回收時的特征。
5)20點44分到20點45分出現大量網絡吞吐,每秒進出流量分別達到1.5G和1.0G。網絡流量吞吐會伴有內核空間內存申請,繼續消耗min閾值以下(橙色部分)free內存。
6)內核網絡模塊會申請order3階內存,20點45分時刻buddyinfo中order3以上高階內存消耗殆盡,剩余的3GB free內存處于碎片化狀態。內核空間申請的內存是連續內存,雖然order2和order1有庫存,但申請order3時是無法被分配的,內核只能處于忙等狀態。
7)觸發內核態忙等,同時會引發20點44分到20點45分的sys CPU升高,sys CPU平均每秒占總CPU資源的89.61%,擠占用戶空間既有進程CPU資源,同期用戶態CPU使用從原來的72.59%降低到7.73%。
8)觸發直接內存回收時,會引發大量D狀態線程,后續order3庫存枯竭引發sys CPU高后,會繼續引發大量R狀態線程。load5s子命令看到的現象就是先出現load5s指標升高,再出現load5s和runq同時升高。
以上各指標立體的展現了整機內存不足時,如何引發load高的機制。有人也許會好奇,一開始是如何將這些指標關聯到一起的。高階內存不足引起sys CPU異常的場景,在問題調查早期也的確是通過采樣(Profiling)工具火焰圖獲取的直接線索。在火焰圖熱點函數棧中有__alloc_skb這樣函數,再結合內核代碼,之后才想到補充buddyinfo內核計數器指標觀察order0到order10的情況。
補充buddyinfo計數器指標后,再遇到sys CPU高問題,通過以上指標體系就直接可以判斷了,不用再次使用采樣工具(perf)。
終于有一天,sys CPU又打高了,但是并沒有同時出現內存相關指標的異常,只發現占用sys CPU的是大量內核線程kworker。
這時還需要繼續借助采樣(Profiling)工具獲取火焰圖分析。
根據火焰圖中熱點函數ext4_mb_regular_allocator/ext4_mb_good_group,再結合sys CPU占用率高的進程是kworker,結合內核代碼可以看到這是文件系統方面的函數,在分配空閑塊。自然就會去想到查看磁盤空間指標,發現確實出現了100%的情況發生。
這是本文第二個例子體現了“最大化挖掘計數器歷史信息價值,發揮計數器指標和跟蹤采樣工具各自的優勢”的理念。
五、ssar工具診斷IOPS打滿和背后探索歷程
本文舉的第三個例子,是關于nvme協議的SSD盤的。這種盤IO性能很高,有時候會遇到讀的IOPS高達上萬的情況,磁盤IO Util打到100%,而且單IO size特別小只有4KB。
阿里有一個自研的IO跟蹤工具,可以抓到每一個IO讀寫了那個文件。最初出現這種IO打滿情況時,我們使用阿里自研的IO跟蹤工具抓取了每一個IOPS的詳情,發現讀取的文件都是二進制的so包,而且分析扇區地址Sector信息時發現同一個扇區地址平均最多每秒被重復讀取了178次,這是非常不符合常規的。正常情況下,我們的程序是一次性從磁盤加載到內存空間,不會重復加載,但是這里一秒鐘重復加載178次,說明一秒內又被丟棄了177次。問題調查到這里,我們自然想去查看內存方面的問題。
經分析最終確認是cgroup的內存設置問題,該進程的memory.limit_in_bytes設置為4GB,當前進程的rss內存使用已經到了3.9GB,剩余的100MB空間不足以完全加載800MB的clean 狀態的二進制可執行代碼部分。先加載第一個100MB的代碼段,當開始執行第二個100MB的代碼段時,必須釋放一些第一個100MB的代碼段。如此就會出現反復加載同一個扇區地址代碼段的情況。
了解了這種場景的原理,會發現出現這種場景時整機和進程級別的主缺頁中斷數會大量增加。當再次出現磁盤讀IO異常高時,我們可以不依賴IO跟蹤工具,直接查看整機主缺頁中斷數,判斷是否又發生了內存顛簸。
當確定時發生內存顛簸時,再進一步通過ssar進程級指標確定大量發生主缺頁中斷的進程。通過pid查看進程cgroup中memory.limit_in_bytes 值的設置,并對比進程當前的rss值也可以診斷這類問題。并且這類問題不論發生在任何時間,都可以通過ssar指標輕松診斷。
前面在linux load、sys CPU和IOPS的性能分析過程中,分享了3個案例。這3個案例都貫穿了一個性能分析的思路,最大化挖掘計數器歷史信息的價值,解放跟蹤采樣工具。使用跟蹤采樣工具探索發現問題的內在邏輯后,再將相關計數器指標固化到ssar系統性能監控工具中。
想知道更多?掃描下面的二維碼關注我后臺回復"技術",加入技術群 后臺回復“k8s”,可領取k8s資料【精彩推薦】ClickHouse到底是什么?為什么如此牛逼!
原來ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹可以存放多少行數據?
架構之道:分離業務邏輯和技術細節
星巴克不使用兩階段提交
面試官:Redis新版本開始引入多線程,談談你的看法?
喜馬拉雅自研網關架構演進過程
收藏:存儲知識全面總結
微博千萬級規模高性能高并發的網絡架構設計
總結
以上是生活随笔為你收集整理的闻茂泉:系统性能监控与分析的工程化实践之路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样的代码算是好代码?
- 下一篇: 系统、应用监控的缜密思路,堪称性能瓶颈的