問題排查:nginx能跑,但是隻能跑一會,不能跑多了

背景

上週都是查測試環境的問題,比如,我上一篇寫的問題排查:nginx的反向代理感覺失效了一樣 ,就是說這個事的。在文章裏,最終查到是nginx的全連接隊列滿了(每個監聽端口有個隊列,完成三次握手的請求會進入這個監聽端口的全連接隊列,隊列大小是隻有128,比較小),我當時的解決方式,是把隊列大小調大到了512,然後重啓nginx,果然功能正常了。

但當時沒有去再多問一個爲什麼:爲什麼nginx的全連接隊列會滿呢?而且這個功能雖然用得少,但是之前應該都好好的,突然就抽風了?

nginx的全連接隊列,簡單,它就是一個隊列,網絡協議棧負責將完成三次握手的連接放到該隊列,算是生產者;那麼,消費者是誰呢,自然是我們的應用程序,應用程序每次調用accept的時候,就會從隊列中獲取一個連接,獲取了之後,隊列也就空出來了。

我翻了下accept這個syscall的手冊(man accept),https://linux.die.net/man/2/accept:

It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket.

這塊是說,是從隊列頭部獲取,先進先出;獲取到了之後,給調用方返回一個文件描述符,指向該socket。

If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller until a connection is present. If the socket is marked nonblocking and no pending connections are present on the queue, accept() fails with the error EAGAIN or EWOULDBLOCK.

In order to be notified of incoming connections on a socket, you can use select(2) or poll(2). A readable event will be delivered when a new connection is attempted and you may then call accept() to get a socket for that connection. Alternatively, you can set the socket to deliver SIGIO when activity occurs on a socket; see socket(7) for details.

這裏又提到,如果隊列裏沒有連接,且該監聽socket不是非阻塞的,那麼accpet調用會阻塞;如果socket是非阻塞的,那麼此時就會返回錯誤:EAGAIN or EWOULDBLOCK。

爲了在新的連接到達時(進入隊列時)能夠得到提醒,我們可以使用select或者poll機制。當新連接到達時,會有一個可讀事件發送給程序,此時再去調用accept就肯定能獲取到連接,而不會阻塞。

我發現,文檔寫得還是非常清楚,很有價值,總的來說,應用程序就是那個消費者,隊列會滿,那肯定是消費者有問題,消費者是nginx,nginx能有啥問題呢,還真不知道,我當時以爲猜測可能是請求處理有點慢吧,我把隊列給你加大了,應該就沒事了。

當天下午有事,也沒管了,結果第二天,測試同事說,又不行了,我去看了下隊列(ss -lnt|grep 8088),512的隊列,又滿了。

排查

nginx 日誌

說實話,當時真的有點無語,因爲手裏還有別的事,也不想一直耗在這個事情上,但是,我們也不能阻礙測試同事工作開展,這也是份內事。

因爲當時這個nginx,除了監聽了我們關注的8088端口,還監聽了8082 8080等端口,我以爲是被其他端口影響到了,也看了下這兩個端口的隊列長度,ss -lnt|grep 8082,發現隊列長度是0,只有我們8088端口是滿的。

我當時想過,怎麼看看這些隊列裏都是啥內容,但感覺意義不大,無非就是那些socket,所以沒有深入去看。

然後又去看nginx日誌,首先奇怪的一點是,我是週四的下午2點多處理完隊列長度,重啓nginx,然後access日誌也只打到了2點20分左右就沒了,error日誌也是。

另外,error日誌倒是打印了一些內容,就是有很多獲取圖片的請求,處理失敗了,就是說open xxx.jpg failed之類的,我當時想着估計是圖片找不到吧,404啥的,這種見多了,一般也不怎麼理。

我還是去看了下那個文件,結果,ssh卡死了:

[root@168assi log]# ll /hxspace/production/files/unsafty/hxtg_dd
^C^C^C^C

我之前就是感覺這個機器有點問題,經常各種命令都卡死,現在ll都不行了。。

常規檢查

然後就是開始檢查系統資源,首先是top,按cpu排序和按內存排序,都沒發現很離譜的佔用很高的應用。

但是,top中看到1、5/15分鐘的平均負載基本在12左右,我們是8核,按我理解,12/8=1.5,那基本上,每個核上有1個線程在運行,還有0.5個線程在等待運行。

感覺是有點高,但我不太擅長排查cpu問題,也沒有再深入。

然後free -h檢查了下內存,空閒內存也還很多,8個g,感覺沒問題。

然後是磁盤,df一執行,結果直接卡死了,也不知道咋回事,之前就是感覺這機器有問題,之前lsof命令也是執行卡死。

當時都懷疑是不是磁盤有問題,還是機器哪裏有問題,要不要換臺機器部署算了。

內核日誌

再檢查下內核日誌吧,dmesg -T,瀏覽了下。沒看到nginx的相關日誌。但是看到一些亂七八糟的內容:

[五 8月 25 11:06:58 2023] nfs: server 10.0.251.111 not responding, timed out
[五 8月 25 11:06:58 2023] nfs: server 10.0.251.111 not responding, timed out
[五 8月 25 11:12:04 2023] nfs: server 10.0.251.111 not responding, still trying

然後又去看了/var/log/messages,也沒啥特別的。

strace查看df阻塞點

之前不是執行df,把我ssh卡死了嘛,我這次想看看到底是哪裏卡住了,於是用strace跟蹤了下。

[root@168assi servers]# strace -q -f -s 500 df

結果,這個命令卡在了下面這個地方:

stat("/run/user/42", {st_mode=S_IFDIR|0700, st_size=180, ...}) = 0
stat("/sys/fs/fuse/connections", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat("/run/user/42/gvfs", 0x7ffe818bf1a0) = -1 EACCES (Permission denied)
stat("/run/user/0", {st_mode=S_IFDIR|0700, st_size=40, ...}) = 0
stat("/hxspace/production/files/unsafty/hxtg_dd", ^C^C^C^C

可以看到我狂按ctrl c嗎,就是因爲卡死了

檢查掛載

這個目錄有問題啊,因爲這個目錄我還是很有印象的,nginx讀圖片就是在這個目錄下讀,然後還失敗了。

然後我想起來那個內核日誌裏nfs報錯的相關信息,我他麼感覺,這個目錄是不是網絡存儲啊,就是掛載到了nfs server。

然後我執行mount -l看了下,果然找到了nfs的蹤跡:

...
10.0.251.111:/home/app on /hxspace/production/files/unsafty/hxtg_dd type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=10.0.251.192,local_lock=none,addr=10.0.251.111)

這個掛載也就是說,/hxspace/production/files/unsafty/hxtg_dd掛載的是nfs服務器10.0.251.111的/home/app目錄.

然後,我ping了下這個機器,不通。此時,基本確定就是這個問題了。

解決

所以,我猜測,df、lsof等各種要遍歷文件夾的命令都卡死了,那估計nginx去讀取那個目錄下的文件,也卡死了,worker如果卡死,那麼nginx負責accept的進程,應該就會停止去accept連接,進而導致全連接隊列滿。

但是,怎麼驗證是這個問題呢?

我們把nginx.conf中讀取那個目錄的配置先刪了,然後再試,果然,這次全連接隊列再沒有積壓了,肉眼看到的一直都是0.

但是,把別人配置刪了也不合適,那看看能不能恢復nfs吧?

我們先去找服務器管理的同事,結果跟我們說,這個nfs服務器已經被回收了,果然,主打一個混亂。

行吧,反正是測試環境,既然nfs服務器沒了,我們也沒打算再搭一個,後邊問到相關業務同事,已經沒在用這臺機器了,那就不用顧忌他們了,那這個掛載就得想辦法去掉,不然各種命令都卡死,實在不爽。

去掉mount掛載,採用的umount命令,結果執行失敗,提示device is busy。

後邊查網上文章,需要ps aux找出狀態爲D的(也就是TASK_UNINTERRUPTIBLE,是一種不可中斷的狀態),我發現查出來的基本就是我之前卡死的那些df等命令。

kill 9強殺這些進程後,再去執行取消掛載,成功了:

[root@168assi ~]# umount -f -v 10.0.251.111:/home/app
/hxspace/production/files/unsafty/hxtg_dd: nfs4 mount point detected
/hxspace/production/files/unsafty/hxtg_dd: umounted

擴展:nfs

我們這裏作爲nfs客戶端,掛載時,其實是可以指定選項的,其中有個選項如下:

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-nfs-client-config-options

hard or soft — Specifies whether the program using a file via an NFS connection should stop and wait (hard) for the server to come back online, if the host serving the exported file system is unavailable, or if it should report an error (soft).

If hard is specified, the user cannot terminate the process waiting for the NFS communication to resume unless the intr option is also specified.

If soft is specified, the user can set an additional timeo= option, where specifies the number of seconds to pass before the error is reported.

也就是可以指定hard或soft,hard就是nfs服務端如果像我們這種情況,直接ip都不通了,客戶端會一直死等,直到nfs server上線;而soft就是會超時,然後報錯。

我們這邊,這次就是掛載用了hard模式,不知道怎麼考慮的。

10.0.251.111:/home//app on /hxspace/production/files/unsafty/hxtg_dd type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=10.0.251.192,local_lock=none,addr=10.0.251.111)

總結

這次遇到的這個問題,說白了,最終就是因爲關閉了nfs服務器的問題,也就能解釋,爲啥以前沒問題了。

另外,我在寫本篇的時候,感覺幾年前好像研究過全連接隊列的問題,去找了下之前文章,發現現象竟然是一模一樣,幾年下來忘得好乾淨,果然是唯手熟爾:

https://www.cnblogs.com/grey-wolf/p/10999342.html

image-20230826161022861

參考文章

https://www.cnblogs.com/yuboyue/archive/2011/07/18/2109867.html

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-nfs-client-config-options

https://superuser.com/questions/1562153/shutdown-of-rhel-6-9-7-7-nfs-client-hangs-when-nfs-server-has-become-unavailab

https://unix.stackexchange.com/questions/328746/how-can-i-monitor-the-length-of-the-accept-queue (查看accept隊列內容)

https://krisnova.net/posts/linux-accept-queues/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章