如何解決linux系統平均負載高(load average)

月圖靈發表於2022-05-09

系統平均負載高(load average)

問題現象

  • 兩個案例都是:系統平均負載高,但cpu,記憶體,磁碟io都正常

什麼是系統平均負載

  • 平均負載是指單位時間內,系統處於可執行狀態不可中斷狀態的平均程式數,也就是平均活躍程式數,它和CPU使用率並沒有直接關係。

  • 可執行狀態的程式,是指正在使用CPU或者正在等待CPU的程式,也就是我們常用ps命令看到的,處於R狀態(Running 或 Runnable)的程式

  • 不可中斷狀態的程式則是正處於核心態關鍵流程中的程式,並且這些流程是不可打斷的,比如最常見的是等待硬體裝置的I/O響應,也就是我們在ps命令中看到的D狀態(Uninterruptible Sleep,也稱為Disk Sleep)的程式

負載高的常見情況

  • 我們常見的負載高一般有這幾種情況引起,一個是cpu密集型,使用大量cpu會導致平均負載升高;

  • 另外一個就是io密集型等待I/O會導致平均負載升高,但是CPU使用率 不一定很高;

  • 還有就是大量等待CPU的程式排程也會導致平均負載升高,此時的CPU使用率也會比較高

問題分析

1、top:

2、vmstat:

可以看到r,cs和us較高

procs:

​ r 表示執行和等待CPU時間片的程式數,這個值如果長期大於系統CPU個數,說明CPU不足。

​ b 表示等待資源的程式數,比如正在等待I/O、或者記憶體交換等。

memory:算是正常

swap:

​ si 每秒從磁碟讀入虛擬記憶體的大小,如果這個值大於0,表示實體記憶體不夠或者記憶體洩露了,要查詢耗記憶體程式解決掉。

​ so 每秒虛擬記憶體寫入磁碟的大小,如果這個值大於0,同上。

​ 一般情況下,si、so的值都為0,如果si、so的值長期不為0,則表示記憶體不足。

IO:

​ bi 塊裝置每秒接收的塊數量,這裡的塊裝置是指系統上所有的磁碟和其他塊裝置,預設塊大小是1024byte

​ bo 塊裝置每秒傳送的塊數量

​ 設定的bi+bo參考值為1000,如果超過1000,而且wa值較大,則表示系統磁碟IO有問題,應該考慮提高磁碟的讀寫效能。

system:

​ in 每秒CPU的中斷次數,包括時間中斷。(較高)

​ cs 每秒上下文切換次數(較高)

​ 這兩個值越大,核心消耗的CPU就越多

cpu:

​ us 使用者程式消耗的CPU時間百分比,us的值比較高時,說明使用者程式消耗的cpu時間多,但是如果長期大於50%,就需要考慮優化程式或演算法

​ sy 核心程式消耗的CPU時間百分比,sy值如果太高,說明核心消耗CPU資源很多,例如是IO操作頻繁。

​ id CPU處於空閒狀態的時間百分比。(cpu空閒)

​ wa io等待所佔用的時間百分比,wa值越高,說明IO等待越嚴重,根據經驗,wa的參考值為20%,如果wa超過20%,說明IO等待嚴重,引起IO等待的

​ 原因可能是磁碟大量隨機讀寫造成的,也可能是磁碟或者磁碟控制器的頻寬瓶頸造成的(主要是塊操作)

通過上面的分析,發現主要是cpu每秒中斷次數以及 上下文切換較高

3、iostat檢視io情況

io無問題

4.1、使用pidstat分析上下文切換


上下文切換並沒有發現特別高的程式,這與vmstat的現象不太吻合,沒有找到是哪個程式導致的負載高

4.2使用pidstat -wt 1

pidstat -wt 1可以看到子程式的上下文切換,但並發現上下文切換較高的程式

解決問題

1、懷疑是docker引起的

但是將所有的docker停了以後並沒有效果

過程中比較奇怪的是,使用df會一直卡住,但是df -lh可以正常訪問

可以看到掛載了許多k8s的tmpfs

tmpfs預設的大小是RM的一半,假如你的實體記憶體是32GB,那麼tmpfs預設的大小就是16GB。

tmpfs 的另一個主要的好處是它閃電般的速度。因為典型的tmpfs檔案系統會完全駐留在記憶體RAM中,讀寫幾乎可以是瞬間的。同時它也有一個缺點tmpfs資料在重新啟動之後不會保留,這點與記憶體的資料特性是一致的。

2、分析程式資訊

因為df打不開,懷疑是磁碟的問題

使用ps -aux,可以看到大量的D狀態的程式,而且都是nfs檔案系統的

仔細觀察可以發現,這些D狀態的程式基本都是在掛載 /var/lib/kublet/pods,就是在掛載k8s,但是一直是D狀態,後面我們詳細分析這個狀態(TASK_UNINTERRUPTIBLE 不可中斷的睡眠狀態。)

3、kill掉D狀態程式

kill掉D狀態程式後load average就降下來了。但是df還是打不開,重啟伺服器後,df能正常開啟。

想起上週排查的一個問題,也是各項指標都很正常,但是負載高達300+,當時排查時主要去分析狀態為R的程式了,由於當時沒有發現有R狀態的程式也就沒有排查出原因,後面陳老師說kill了一個系統的exporter程式後面負載就降下來了,今天分析日誌發現原來這個node_exporter的程式狀態也是一直為D狀態。

4、系統日誌

通過系統日誌/var/log/messages可以看到確實有大量的nfs對映錯誤

Apr 10 04:48:12 node-1722516126 nfsidmap[11838]: nss_getpwnam: name 'root@nfs-provisioner.nfs-provisioner.svc.cluster.local' does not map into domain 'localdomain' Apr 10 04:48:12 node-1722516126 nfsidmap[11852]: nss_name_to_gid: name 'root@nfs-provisioner.nfs-provisioner.svc.cluster.local' does not map into domain 'localdomain' Apr 19 16:05:24 node-1722516126 kubelet: E0419 16:05:24.039993 43275 kubelet_volumes.go:154] orphaned pod "033d41bc-502b-4142-bfb4-deac0330dd8b" found, but volume paths are still present on disk : There were a total of 5 errors similar to this. Turn up verbosity to see them. Apr 19 16:05:26 node-1722516126 kubelet: E0419 16:05:26.054950 43275 kubelet_volumes.go:154] orphaned pod "033d41bc-502b-4142-bfb4-deac0330dd8b" found, but volume paths are still present on disk : There were a total of 5 errors similar to this. Turn up verbosity to see them. Apr 19 16:05:28 node-1722516126 kubelet: E0419 16:05:28.054644 43275 kubelet_volumes.go:154] orphaned pod "033d41bc-502b-4142-bfb4-deac0330dd8b" found, but volume paths are still present on disk : There were a total of 5 errors similar to this. Turn up verbosity to see them. Apr 19 16:05:30 node-1722516126 kubelet: E0419 16:05:30.054966 43275 kubelet_volumes.go:154] orphaned pod "033d41bc-502b-4142-bfb4-deac0330dd8b" found, but volume paths are still present on disk : There were a total of 5 errors similar to this. Turn up verbosity to see them. Apr 19 16:05:32 node-1722516126 kubelet: E0419 16:05:32.040576 43275 kubelet_volumes.go:154] orphaned pod "033d41bc-502b-4142-bfb4-deac0330dd8b" found, but volume paths are still present on disk : There were a total of 5 errors similar to this. Turn up verbosity to see them.

什麼是D狀態的程式

程式為什麼會被置於uninterruptible sleep(D)狀態呢?處於uninterruptible sleep(D)狀態的程式通常是在等待IO,比如磁碟IO,網路IO,其他外設IO,如果程式正在等待的IO在較長的時間內都沒有響應,那麼就很會不幸地被 ps看到了,同時也就意味著很有可能有IO出了問題,可能是外設本身出了故障,也可能是比如掛載的遠端檔案系統已經不可訪問了,我這裡遇到的問題就是由 down掉的NFS伺服器引起的。

正是因為得不到IO的相應,程式才進入了uninterruptible sleep狀態,所以要想使程式從uninterruptible sleep狀態恢復,就得使程式等待的IO恢復,比如如果是因為從遠端掛載的NFS卷不可訪問導致程式進入uninterruptible sleep狀態的,那麼可以通過恢復該NFS卷的連線來使程式的IO請求得到滿足,除此之外,要想幹掉處在D狀態程式就只能重啟整個Linux系統了。

關於程式狀態

一、R TASK_RUNNING 可執行狀態。如果一個程式處於該狀態,那麼說明它立刻就要或正在CPU上執行。不過執行的時機是不確定的,這有程式排程器來決定。

二、S TASK_INTERRUPTIBLE. 可中斷的睡眠狀態。當程式正在等待某個事件(比如網路連線或者訊號量)到來時,會進入此狀態。這樣的程式會被放入對應事件的等待佇列中。當事件發生時,對應的等待佇列中的一個或者多個程式就會被喚醒。(大部分程式處於該狀態)

三、D TASK_UNINTERRUPTIBLE 不可中斷的睡眠狀態。

與TASK_INTERRUPTIBLE狀態類似,程式處於睡眠狀態,但是此刻程式是不可中斷的。不可中斷,指的並不是CPU不響應外部硬體的中斷,而是指程式不響應非同步訊號。

絕大多數情況下,程式處在睡眠狀態時,總是應該能夠響應非同步訊號的。否則你將驚奇的發現,kill -9竟然殺不死一個正在睡眠的程式了!於是我們也很好理解,為什麼ps命令看到的程式幾乎不會出現TASK_UNINTERRUPTIBLE狀態,而總是TASK_INTERRUPTIBLE狀態。

四、T TASK_STOPPED. 暫停狀態或者跟蹤狀態。 向程式傳送SIGSTOP訊號,就會使該程式轉入暫停狀態,除非該程式正處於不可中斷的睡眠狀態。向正處於暫停狀態的程式傳送SIGCONT訊號,會使該程式轉向可執行狀態。處於該狀態的程式會暫停,並等待另一個程式(跟蹤它的那個程式)對它進行操作。例如,我們使用除錯工具GDB在某個程式中設定一個斷點,然後對應的程式中執行到該斷點處就會停下來。這時,該程式就處於跟蹤狀態。跟蹤狀態與暫停狀態非常類似。但是,向處於跟蹤狀態的程式傳送SIGCONT訊號並不能使它恢復。只有當除錯程式進行了相應的系統呼叫或者退出後才能恢復。

五、Z TASK_DEAD-EXIT_ZOMBIE 殭屍狀態,處於此狀態的程式即將結束執行,該程式佔用的絕大多數資源也都已經被回收,不過還有一些資訊未刪除,比如退出碼以及一些統計資訊。之所以保留這些資訊,可能是考慮到該程式的父程式需要這些資訊。由於此時的父程式主體已經被刪除而只留下一個空殼,故此狀態才稱為殭屍狀態。

六、X TASK_DEAD-EXIT_DEAD 退出狀態 在程式退出的過程中,有可能連退出碼和統計資訊都不需要保留。造成這種情況的原因可能是顯式地讓該程式的父程式忽略掉SIGCHILD訊號,也可能是改程式已經被分離。分離後的子程式不會再使用和執行副程式共享的程式碼段中的指令,而是載入並執行一個全新的程式。在這些情況下,該程式的退出的時候就 不會轉入殭屍狀態,而會直接轉入退出狀態。處於退出狀態的程式會立即被幹淨利落的結束掉,它佔用的系統資源也會被作業系統自動回收。

在程式對某些硬體進行操作時(比如程式呼叫read系統呼叫對某個裝置檔案進行讀操作,而read系統呼叫最終執行到對應裝置驅動的程式碼,並與對應的物理裝置進行互動),可能需要使用TASK_UNINTERRUPTIBLE狀態對程式進行保護,以避免程式與裝置互動的過程被打斷,造成裝置陷入不可控的狀態。這種情況下的TASK_UNINTERRUPTIBLE狀態總是非常短暫的,通過ps命令基本上不可能捕捉到。

程式為什麼會被置於uninterruptible sleep狀態呢?處於uninterruptiblesleep狀態的程式通常是在等待IO,比如磁碟IO,網路IO,其他外設IO,如果程式正在等待的IO在較長的時間內都沒有響應,那麼就很會不幸地被ps看到了,同時也就意味著很有可能有IO出了問題,可能是外設本身出了故障,也可能是比如掛載的遠端檔案系統已經不可訪問了(由down掉的NFS伺服器引起的D狀態)。

正是因為得不到IO的相應,程式才進入了uninterruptible sleep狀態,所以要想使程式從uninterruptiblesleep狀態恢復,就得使程式等待的IO恢復,比如如果是因為從遠端掛載的NFS卷不可訪問導致程式進入uninterruptiblesleep狀態的,那麼可以通過恢復該NFS卷的連線來使程式的IO請求得到滿足。

D狀態,往往是由於 I/O 資源得不到滿足,而引發等待,在核心原始碼 fs/proc/array.c 裡,其文字定義為“ "D (disk sleep)", /* 2 */ ”(由此可知 D 原是Disk的打頭字母),對應著 include/linux/sched.h 裡的“ #define TASK_UNINTERRUPTIBLE 2 ”。舉個例子,當 NFS 服務端關閉之時,若未事先 umount 相關目錄,在 NFS 客戶端執行 df 就會掛住整個登入會話,按 Ctrl+C 、Ctrl+Z 都無濟於事。斷開連線再登入,執行 ps axf 則看到剛才的 df 程式狀態位已變成了 D ,kill -9 無法殺滅。正確的處理方式,是馬上恢復 NFS 服務端,再度提供服務,剛才掛起的 df 程式發現了其苦苦等待的資源,便完成任務,自動消亡。若 NFS 服務端無法恢復服務,在 reboot 之前也應將 /etc/mtab 裡的相關 NFS mount 項刪除,以免 reboot 過程例行呼叫 netfs stop 時再次發生等待資源,導致系統重啟過程掛起。

參考資料:

https://unix.stackexchange.com/questions/16738/when-a-process-will-go-to-d-state

https://blog.csdn.net/chinalinuxzend/article/details/4288784

https://www.cnblogs.com/embedded-linux/p/7043569.html

https://developer.aliyun.com/article/25448

https://blog.csdn.net/arkblue/article/details/46862751

相關文章