後端服務(nginx部署)<status code 500>的一次排查經歷

本文介紹狼廠同事一次解決服務端接口報 http status 500錯誤的過程。問題不復雜,但是問題的起因在實際工作中並不多見,並且涉及多個知識點。

一、項目背景

項目是狼廠一個前後端分離的項目,項目簡略框架如下圖。

  • 項目包含業務塊比較多,分別由不同部門負責。
  • 各業務塊後端服務分別獨立部署。
  • 各業務塊後端服務都在同一域名下,根據url路徑進行區分,比如www.***.com/our/**表示我們的服務,www.***.com/abc/**表示另一服務。
  • 上條所說的根據不同url路徑往後端服務的轉發是統一轉發層來完成的,轉發層服務器爲nginx,主要配置如下: server { listen 80; server_name www.***.com; location /our/ { proxy_pass http://innerip:port1/; } location /abc/ { proxy_pass http://innerip:port2/; } }
  • 我們的後端服務也是部署在Nginx服務器上。
  • 轉發層和後端服務都部署有很多臺機器實例。

二、問題描述

忽然陸續接到用戶反饋線上報錯,而且結合QA同事復現,問題表象有以下特點:1.偶現,對同一用戶也是偶現,2.只是部分功會有問題,這些偶現問題的功能基本都是寫操作。

(事後總結,這兒其實由現象可以猜測出一些原因,針對同一用戶偶現基本可以確定只是部分服務器實例出現問題,另外只有寫操作相關功能有問題,可以猜測可能和http請求的請求參數數據包大小有關,當然這種猜測不能作爲事發時解決問題的正常思路)

三、追蹤步驟

程序員都知道解決bug最怕的問題是不能復現,好在此問題可以偶發復現。而且這是瀏覽器上訪問出的問題,這相對於App報錯又是相對簡單的,解決思路如下:

  1. 瀏覽器打開調試模式,通過復現發現報錯的操作都返回http status 500錯誤。
  2. 大家都知道根據http響應status code規範,5開頭的code都是服務端錯誤,500的標準定義爲 HTTP-Internal Server Error,即服務器內部錯誤,那就可以確定是後端服務的問題導致。思路就比較明確了,那就去追查nginx的error日誌。
  3. 根據第一步項目介紹框架可知,有可能是轉發層nginx出錯,也有可能業務後端服務層本身Nginx出錯,那麼接下來就來確認一下究竟是哪一層的問題。
  4. 因爲轉發層和後端服務都部署有很多服務器實例,而且問題是偶現的,所以無法做到像調試單機那樣去實時通過tail log文件的方式去追蹤,而只能通過全量實例去分析過去某時間段的日誌來分析。此時,團隊平時在日誌管理和分析方面所做的努力就發揮作用了,我們支持以小時粒度對過去的日誌進行快速查看和搜索,而且是使用shell命令來執行,於是結合常用日誌分析shell命令來快速分析出近一個小時內500錯誤的請求。
  5. 在轉發層服務器實例上發現了大量500錯誤日誌,並且所有錯誤日誌都來自於同一臺實例機器,這也驗證了之前的第一個只有部分實例出問題的猜想,也就是說其實只有一個服務器實例除了問題。
  6. 那麼這個500錯誤究竟是什麼原因造成的呢?請看其中一條錯誤日誌的詳細內容: open() "/home/our/nginx/client_body_temp/0000000102" failed (28: No space left on device)

問題找到了,下面來說解決方法。

四、分析解決

1.no space left on device 的原因

後半句括號裏字面(no space left on device)意思很簡單理解,就是磁盤沒有空間了嘛。

前半句中的目錄/home/our/nginx/client_body_temp/0000000102是什麼呢?簡單說就是如果客戶端POST一個比較大的內容,長度超過了nginx緩衝區的大小,nginx需要把這個文件的部分或者全部內容暫存到client_body_temp目錄下的臨時文件。

那麼問題就比較清楚了,就是post請求的內容超過nginx配置的緩衝區大小,就會先寫到臨時文件中,由於沒有存儲空間可用,創建臨時文件失敗,於是導致500錯誤。

這也驗證了本文第一部分的另一個和請求參數大小有關的猜想。

本着嚴謹的態度,去找運維同學幫機器給硬盤擴容之前,先自己用 df -h 命令查看來確認一下磁盤空間使用情況,結果如下,nginx所在目錄的磁盤有很多可用空間,明明磁盤空間使用還不到一半,爲什麼提示沒有空間可用了呢?

如果感覺奇怪的話,再來看另一個linux命令:df -i

顯示使用率爲100%,剩餘可用數爲0,來解釋一下這個命令,-i 是指 inode,每個文件佔用一個inode,包含以下幾方面信息:

  • 文件字節數
  • 擁有者的user id、group id
  • 文件的讀寫執行權限
  • 文件時間戳ctime changetime mtime modify atime access
  • 軟連接和硬鏈接ln
  • 文件block位置

綜上,df -h可清楚查看磁盤空間使用情況,df -i 用來查看inode使用情況,磁盤空間和inode剩餘爲0時都會導致no space left on device 的錯誤。

2. inode爲什麼使用量這麼多?

其它服務器實例配置都相似,爲什麼只有這臺問題實例inode佔滿,其它實例還剩餘很多。

而且我們聯繫運維同事試着刪除了一些數百個log日誌文件,問題解決,但過了幾分鐘後inode就又佔滿。感覺像有某個應用進程在持續快速的生成小文件。Google出如下shell命令可以來查看當前目錄下的inode使用情況(並會展示出子目錄的佔用情況):

很快找出某目錄佔用inode量巨大,聯繫運維同學,發現是運維同學在隨機抽取樣本實例來做一些服務器檢測,檢測應用會快速增量生成很多小文件。

運維同學先關掉了檢測應用的進程,同時刪除了小文件,問題解決。

五、總結

  1. 500錯誤就直接去服務端分析問題。
  2. nginx可以對緩衝區大小進行配置,當http請求數據大小超過這個值則會寫入臨時文件,針對這塊配置其實有多個配置項,google很多介紹,本文不做詳解。
  3. df -h 查看磁盤空間使用情況,df -i 查看系統inode使用情況,兩者用盡後都會導致“no space left on device” 。
  4. 服務器監控機制有問題,此bug其實不應該由用戶報上來,而應該由監控系統自動報警上報。事實上有針對nginx日誌的監控,但是監控機制本身也涉及到寫文件操作,所以同樣因爲inode用盡,導致監控系統本身也因此除了問題。
  5. 對於線上問題的追蹤,對日誌的管理很重要,要提前建立起一套方便快速查詢分析日誌的系統,不要笑,有不少公司仗着自己線上機器數少,當出現問題時,就登錄所有機器單個進行追蹤。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章