一次死鎖導致CPU異常飄高的整個故障排查過程

自由早晚亂餘生發表於2021-04-23

一、問題詳情

linux一切皆檔案

2021年4月2號,晚上10.45分左右,線上業務異常,後排查 線上伺服器CPU 異常高,機器是 16核 64G的。但是實際負載已經達到了 140左右。

top 命令截圖

image-20210408094444156

聯絡騰訊雲排查

  1. 虛擬機器所屬於物理機是否有故障。
  2. 虛擬機器所用的資源是否有抖動或者變更。(網路/儲存等)

騰訊雲回覆暫無異常。

檢查系統日誌發現異常

Apr  2 22:45:22 docker-machine systemd: Reloading.
Apr  2 22:46:37 docker-machine systemd-logind: Failed to start session scope session-175098.scope: Connection timed out
Apr  2 22:47:26 docker-machine systemd-logind: Failed to start session scope session-175101.scope: Connection timed out
Apr  2 22:47:51 docker-machine systemd-logind: Failed to start session scope session-175102.scope: Connection timed out
Apr  2 22:48:26 docker-machine systemd-logind: Failed to start session scope session-175104.scope: Connection timed out
Apr  2 22:48:51 docker-machine systemd-logind: Failed to start session scope session-175105.scope: Connection timed out
Apr  2 22:49:06 docker-machine kernel: INFO: task systemd:1 blocked for more than 120 seconds.
Apr  2 22:49:06 docker-machine kernel:      Not tainted 4.4.108-1.el7.elrepo.x86_64 #1
Apr  2 22:49:06 docker-machine kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
Apr  2 22:49:06 docker-machine kernel: systemd         D ffff880fd8bebc68     0     1      0 0x00000000
Apr  2 22:49:06 docker-machine kernel: ffff880fd8bebc68 ffff880fd5e69c00 ffff880fd8be0000 ffff880fd8bec000
Apr  2 22:49:06 docker-machine kernel: ffff880fd8bebdb8 ffff880fd8bebdb0 ffff880fd8be0000 ffff88039c6a9140
Apr  2 22:49:06 docker-machine kernel: ffff880fd8bebc80 ffffffff81700085 7fffffffffffffff ffff880fd8bebd30
Apr  2 22:49:06 docker-machine kernel: Call Trace:
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81700085>] schedule+0x35/0x80
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81702d97>] schedule_timeout+0x237/0x2d0
Apr  2 22:49:06 docker-machine kernel: [<ffffffff813392cf>] ? idr_remove+0x17f/0x260
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81700b81>] wait_for_completion+0xf1/0x130
Apr  2 22:49:06 docker-machine kernel: [<ffffffff810aa6a0>] ? wake_up_q+0x80/0x80
Apr  2 22:49:06 docker-machine kernel: [<ffffffff810e2804>] __synchronize_srcu+0xf4/0x130
Apr  2 22:49:06 docker-machine kernel: [<ffffffff810e1c70>] ? trace_raw_output_rcu_utilization+0x60/0x60
Apr  2 22:49:06 docker-machine kernel: [<ffffffff810e2864>] synchronize_srcu+0x24/0x30
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81249b3b>] fsnotify_destroy_group+0x3b/0x70
Apr  2 22:49:06 docker-machine kernel: [<ffffffff8124b872>] inotify_release+0x22/0x50
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81208b64>] __fput+0xe4/0x210
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81208cce>] ____fput+0xe/0x10
Apr  2 22:49:06 docker-machine kernel: [<ffffffff8109c1e6>] task_work_run+0x86/0xb0
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81079acf>] exit_to_usermode_loop+0x73/0xa2
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81003bcd>] syscall_return_slowpath+0x8d/0xa0
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81703d8c>] int_ret_from_sys_call+0x25/0x8f
Apr  2 22:49:06 docker-machine kernel: INFO: task fsnotify_mark:135 blocked for more than 120 seconds.
Apr  2 22:49:06 docker-machine kernel:      Not tainted 4.4.108-1.el7.elrepo.x86_64 #1
Apr  2 22:49:06 docker-machine kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
Apr  2 22:49:06 docker-machine kernel: fsnotify_mark   D ffff880fd4993c88     0   135      2 0x00000000
Apr  2 22:49:06 docker-machine kernel: ffff880fd4993c88 ffff880fdf597648 ffff880fd8375900 ffff880fd4994000
Apr  2 22:49:06 docker-machine kernel: ffff880fd4993dd8 ffff880fd4993dd0 ffff880fd8375900 ffff880fd4993e40
Apr  2 22:49:06 docker-machine kernel: ffff880fd4993ca0 ffffffff81700085 7fffffffffffffff ffff880fd4993d50
Apr  2 22:49:06 docker-machine kernel: Call Trace:
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81700085>] schedule+0x35/0x80
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81702d97>] schedule_timeout+0x237/0x2d0
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81062aee>] ? kvm_clock_read+0x1e/0x20
Apr  2 22:49:06 docker-machine kernel: [<ffffffff81700b81>] wait_for_completion+0xf1/0x130
Apr  2 22:49:11 docker-machine kernel: INFO: task java:12560 blocked for more than 120 seconds.
Apr  2 22:49:11 docker-machine kernel:      Not tainted 4.4.108-1.el7.elrepo.x86_64 #1
Apr  2 22:49:11 docker-machine kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
Apr  2 22:49:11 docker-machine kernel: java            D ffff880bfbdc7b00     0 12560   4206 0x00000180
Apr  2 22:49:11 docker-machine kernel: ffff880bfbdc7b00 ffff880bfbdc7b40 ffff880bfbdac2c0 ffff880bfbdc8000
Apr  2 22:49:11 docker-machine kernel: ffff8809beb142d8 ffff8809beb14200 0000000000000000 0000000000000000
Apr  2 22:49:11 docker-machine kernel: ffff880bfbdc7b18 ffffffff81700085 ffff880b155adfc0 ffff880bfbdc7b98
Apr  2 22:49:11 docker-machine kernel: Call Trace:
Apr  2 22:49:11 docker-machine kernel: [<ffffffff81700085>] schedule+0x35/0x80
Apr  2 22:49:11 docker-machine kernel: [<ffffffff8124ca55>] fanotify_handle_event+0x1b5/0x2f0
Apr  2 22:49:11 docker-machine kernel: [<ffffffff810c2b50>] ? prepare_to_wait_event+0xf0/0xf0
Apr  2 22:49:11 docker-machine kernel: [<ffffffff8124933f>] fsnotify+0x26f/0x460
Apr  2 22:49:11 docker-machine kernel: [<ffffffff810a1fd1>] ? in_group_p+0x31/0x40
Apr  2 22:49:11 docker-machine kernel: [<ffffffff812111fc>] ? generic_permission+0x15c/0x1d0
Apr  2 22:49:11 docker-machine kernel: [<ffffffff812b355b>] security_file_open+0x8b/0x90
Apr  2 22:49:11 docker-machine kernel: [<ffffffff8120484f>] do_dentry_open+0xbf/0x320
Apr  2 22:49:11 docker-machine kernel: [<ffffffffa02cb552>] ? ovl_d_select_inode+0x42/0x110 [overlay]
Apr  2 22:49:11 docker-machine kernel: [<ffffffff81205e15>] vfs_open+0x55/0x80
Apr  2 22:49:11 docker-machine kernel: [<ffffffff81214143>] path_openat+0x1c3/0x1300
                                                                              

檢視日誌,覺得很大可能性是: cache 落盤故障,有可能是 io 的問題。通過 iotop 進行排查,未發現異常。

當時我們認為是 騰訊雲底層儲存或者網路出現問題導致的。

在排查了近一個小時,機器上面的cpu 還是沒有降低。我們對機器進行了重啟。重啟後,一些恢復了正常。

二、 問題解析

  • 認為是儲存的問題

    首先上面的故障是同時出現在兩臺機器(A和B)的, 詢問騰訊雲 A 的系統盤和A的資料盤以及B的資料盤都是在同一個遠端儲存的,所以這更加深了我們認為是儲存導致的問題,有可能是到物理機到儲存之間的網路,也有可能是儲存本身的效能問題。

    騰訊雲排查後說這兩個機器,所用的儲存和儲存網路沒有問題,所以儲存問題不成立。

  • 系統的殭屍程式很多

    在上面top 命令我們可以看到有僵死程式,後面也是一直在增加僵死程式。

    image-20210408131034043

    僵死程式的來源:

    1. 上面的僵死程式來源是我們的定時任務導致的,我們定時任務指令碼執行的程式變成的僵死程式。

    如何看僵死程式

    ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
    
  • /var/log/message 異常資訊

    我們再看看 /var/log/message 的日誌,我們可以看到一個很關鍵的資訊 kernel: INFO: task systemd:1 blocked for more than 120 seconds.

    網上大多數時是說 vm.dirty_ratiovm.dirty_background_ratio 這兩個引數設定的有問題。

    我們檢視了我們這兩個核心引數的配置,都是正常合理的。

    $ sysctl -a|grep -E  'vm.dirty_background_ratio|vm.dirty_ratio'
    vm.dirty_background_ratio = 10  # 
    vm.dirty_ratio = 30
    

    具體的引數詳解,見下文。

  • 我們再看看 /var/log/message 的日誌,我們可以看到一個很關鍵的資訊

    Apr  2 22:45:22 docker-machine systemd: Reloading.
    Apr  2 22:49:06 docker-machine kernel: INFO: task systemd:1 blocked for more than 120 seconds.
    Apr  2 22:49:06 docker-machine kernel: systemd         D ffff880fd8bebc68     0     1      0 0x00000000
    Apr  2 22:49:06 docker-machine kernel: INFO: task fsnotify_mark:135 blocked for more than 120 seconds.
    Apr  2 22:49:06 docker-machine kernel: fsnotify_mark   D ffff880fd4993c88     0   135      2 0x00000000
    Apr  2 22:49:11 docker-machine kernel: INFO: task java:12560 blocked for more than 120 seconds.
    Apr  2 22:49:11 docker-machine kernel: java            D ffff880bfbdc7b00     0 12560   4206 0x00000180
    

    就是 systemdReloading, systemdfsnotify_mark 都被block了,那麼被鎖了原因是什麼,按道理來說應該 io 的問題啊,就是寫得慢啊,但是我們忽略了一個問題,如果要寫的檔案加鎖了,那麼也是會出現這個情況的啊。

    尋找加鎖的原因: 騰訊雲主機安全產品 雲鏡, 沒錯就很大可能性是它導致的。 具體內容見下文。

三、問題原因

為什麼會定位到雲鏡產品,首先是我們認為如果底層 io 沒有問題的話,那麼就只能是檔案可能被鎖了,並且如果你細心的話,你會發現僵死程式裡面,有云鏡的身影

image-20210408221649277

為什麼雲鏡會變成僵死程式,是因為雲鏡啟動失敗了,一直在啟動。

我們再說回為什麼會定位到雲鏡上面,主要是因為雲鏡會對系統上檔案有定期掃描的,為什麼會想到就是安全產品(https://access.redhat.com/solutions/2838901)。 安全產品就是雲鏡。

我們觀察雲鏡的日誌,我們又發現了一個問題,原來在 22:45 左右,雲鏡在更新,這個很巧合啊,我們出問題的兩個機器都在這個時間段進行了更新,而沒有異常的機器,都沒有更新操作。

  1. 雲鏡更新的日誌

    image-20210409153620789

  2. 更新後一直沒有云鏡一直啟動失敗

    image-20210409153748281

  3. redhat 官方文件

    https://access.redhat.com/solutions/2838901

    也是說到安全產品會可能觸發這個問題。

    image-20210409153148717

最終結論

最終讓騰訊雲排查雲鏡此次版本升級,得到答覆:

​ 推測YDServiceexit group退出的時未及時對fanotify/inotify進行適當的清理工作,導致其它程式阻塞等待,因此針對此點進行了優化。

問題1: 針對為什麼只有兩臺機器在那個時間點進行更新,是因為那個雲鏡後端排程策略是分批升級。

四、擴充套件

程式的幾種狀態

https://liam.page/2020/01/10/the-states-of-processes-on-Linux/

程式通常處於以下兩種狀態之一:

  • 在 CPU 上執行(此時,程式正在執行) 在 ps 或是 top 中,狀態標識為 R 的程式,即處於正在執行狀態。

  • 不在 CPU 上執行(此時,程式未在執行)

    未在執行的程式可能處於不同狀態:

    • 可執行狀態 (R)

      程式獲取了所有所需資源,正等待 CPU 時,就會進入可執行狀態。處於可執行狀態的程式在 ps 的輸出中,也已 R 標識。

      舉例來說,一個正在 I/O 的程式並不立即需要 CPU。當程式完成 I/O 操作後,就會觸發一個訊號,通知 CPU 和排程器將該程式置於執行佇列(由核心維護的可執行程式的列表)。當 CPU 可用時,該程式就會進入正在執行狀態。

    • 可中斷之睡眠狀態 (S)

      可中斷之睡眠狀態表示程式在等待時間片段或者某個特定的事件。一旦事件發生,程式會從可中斷之睡眠狀態中退出。ps 命令的輸出中,可中斷之睡眠狀態標識為 S

    • 不可中斷之睡眠狀態(D)

      不可中斷之睡眠狀態的程式不會處理任何訊號,而僅在其等待的資源可用或超時時退出(前提是設定了超時時間)。

      不可中斷之睡眠狀態通常和裝置驅動等待磁碟或網路 I/O 有關。在核心原始碼 fs/proc/array.c 中,其文字定義為 "D (disk sleep)", /* 2 */。當程式進入不可中斷之睡眠狀態時,程式不會處理訊號,而是將訊號都積累起來,等程式喚醒之後再處理。在 Linux 中,ps 命令使用 D 來標識處於不可中斷之睡眠狀態的程式。

      系統會為不可中斷之睡眠狀態的程式設定程式執行狀態為:

      p->state = TASK_UNINTERRUPTABLE;
      

      由於處於不可中斷之睡眠狀態的程式不會處理任何訊號,所以 kill -9 也殺不掉它。解決此類程式的辦法只有兩個:

      • 對於怨婦,你還能怎麼辦,只能滿足它啊:搞定不可中斷之睡眠狀態程式所等待的資源,使資源可用。
      • 如果滿足不了它,那就只能 kill the world——重啟系統。
    • 僵死狀態(Z)

      程式可以主動呼叫 exit 系統呼叫來終止,或者接受訊號來由訊號處理函式來呼叫 exit 系統呼叫來終止。

      當程式執行 exit 系統呼叫後,程式會釋放相應的資料結構;此時,程式本身已經終止。不過,此時作業系統還沒有釋放程式表中該程式的槽位(可以形象地理解為,「父程式還沒有替子程式收屍」);為解決這個問題,終止前,程式會向父程式傳送 SIGCHLD 訊號,通知父程式來釋放子程式在作業系統程式表中的槽位。這個設計是為了讓父程式知道子程式退出時所處的狀態。

      子程式終止後到父程式釋放程式表中子程式所佔槽位的過程,子程式進入殭屍狀態(zombie state)。如果在父程式因為各種原因,在釋放子程式槽位之前就掛掉了,也就是,父程式來不及為子程式收屍。那麼,子程式就會一直處於殭屍狀態。而考慮到,處於殭屍狀態的程式本身已經終止,無法再處理任何訊號,所以它就只能是孤魂野鬼,飄在作業系統程式表裡,直到系統重啟。

    馬後炮

    在前面的日誌中,也就是下面:

    Apr  2 22:49:06 docker-machine kernel: systemd         D ffff880fd8bebc68     0     1      0 0x00000000
    Apr  2 22:49:06 docker-machine kernel: INFO: task fsnotify_mark:135 blocked for more than 120 seconds.
    Apr  2 22:49:06 docker-machine kernel: fsnotify_mark   D ffff880fd4993c88     0   135      2 0x00000000
    

    我們部分程式處於 不可中斷之睡眠狀態(D), 在這個狀態的服務,前面也說到只能給他資源,或者重啟系統。 也就可以說明:

    解釋疑問:

    1. 為什麼我們故障機器上面部分服務存在問題,部分服務正常。

      因為部分程式處於 不可中斷之睡眠狀態(D)。檔案(linux一切皆檔案)被鎖,導致了部分服務程式進入了不可中斷睡眠狀態。

如何快速清理殭屍程式(Z)

用top檢視系統中的殭屍程式情況
top
再看看這些殭屍是什麼程式來的
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
 
kill -s SIGCHLD pid  (父程式pid)

核心引數相關

  • dirty_background_ratio 指當檔案系統快取髒頁數量達到系統記憶體百分之多少時(預設10%)喚醒核心的 flush 等程式,寫回磁碟。
  • dirty_ratio 為最大髒頁比例,當髒頁數達到該比例時,必須將所有髒資料提交到磁碟,同時所有新的 IO 都會被阻塞,直到髒資料被寫入磁碟,通常會造成 IO 卡頓。系統先會達到 vm.dirty_background_ratio 的條件然後觸發 flush 程式進行非同步的回寫操作,此時應用程式仍然可以進行寫操作,如果達到 vm.dirty_ratio 這個引數所設定的值,此時作業系統會轉入同步地處理髒頁的過程,阻塞應用程式。

如何檢視哪些檔案被哪些程式被鎖

http://blog.chinaunix.net/uid-28541347-id-5678998.html

cat /proc/locks
1: POSIX  ADVISORY  WRITE 3376 fd:10:805736756 0 EOF
2: FLOCK  ADVISORY  WRITE 1446 00:14:23843 0 EOF
3: FLOCK  ADVISORY  WRITE 4650 00:14:32551 0 EOF
4: POSIX  ADVISORY  WRITE 4719 fd:01:531689 1073741824 1073742335
5: OFDLCK ADVISORY  READ  1427 00:06:1028 0 EOF
6: POSIX  ADVISORY  WRITE 4719 00:14:26155 0 EOF
7: POSIX  ADVISORY  WRITE 4443 00:14:26099 0 EOF
8: FLOCK  ADVISORY  WRITE 4561 00:14:34870 0 EOF
9: POSIX  ADVISORY  WRITE 566 00:14:15509 0 EOF
10: POSIX  ADVISORY  WRITE 4650 fd:01:788600 0 EOF
11: OFDLCK ADVISORY  READ  1713 00:06:1028 0 EOF
12: FLOCK  ADVISORY  WRITE 1713 fd:10:268435553 0 EOF
13: FLOCK  ADVISORY  WRITE 1713 fd:10:268435528 0 EOF
14: POSIX  ADVISORY  WRITE 12198 fd:01:526366 0 EOF
15: POSIX  ADVISORY  WRITE 3065 fd:10:805736741 0 EOF
16: FLOCK  ADVISORY  WRITE 1731 fd:10:268435525 0 EOF
17: FLOCK  ADVISORY  WRITE 4459 00:14:37972 0 EOF
18: POSIX  ADVISORY  WRITE 1444 00:14:14793 0 EOF

我們可以看到/proc/locks下面有鎖的資訊:我現在分別敘述下含義:

  1. POSIX FLOCK 這個比較明確,就是哪個型別的鎖。flock系統呼叫產生的是FLOCK,fcntl呼叫F_SETLK,F_SETLKW或者lockf產生的是POSIX型別,有次可見兩種呼叫產生的鎖的型別是不同的;

  2. ADVISORY表明是勸告鎖;

  3. WRITE顧名思義,是寫鎖,還有讀鎖;

  4. 18849 是持有鎖的程式ID。當然對於flock這種型別的鎖,會出現程式已經退出的狀況。

  5. 08:02:852674 表示的對應磁碟檔案的所在裝置的主裝置好,次裝置號,還有檔案對應的inode number。

  6. 0 表示的是所的其實位置

  7. EOF表示的是結束位置。 這兩個欄位對fcntl型別比較有用,對flock來是總是0 和EOF。

相關文章