《從400錯誤引發的故障入手,談談如何分析和修復常見的Nginx異常》要點:
本文介紹了從400錯誤引發的故障入手,談談如何分析和修復常見的Nginx異常,希望對您有用。如果有疑問,可以聯系我們。
作者介紹
林偉壕,網絡安全DevOps新司機,先后在中國電信和網易游戲從事數據網絡、網絡安全和游戲運維工作.對Linux運維、虛擬化和網絡安全防護等研究頗多,目前專注于網絡安全自動化檢測、防御系統構建.
眾所周知,Nginx是目前最流行的Web Server之一,也廣泛應用于負載均衡、反向代理等服務,使用過程中可能因為對Nginx工作原理、變量含義、參數大小等問題的理解錯誤,導致Nginx工作異常.
因此,本文將從一個Nginx錯誤代碼400引發的故障入手,談談如何分析和修復常見的Nginx異常.
小明某天中午在線優化一個敏感服務的Nginx配置時,發現5分鐘內Nginx errorlog里出現了大量400錯誤,于是迅速回滾了Nginx配置.
原來的Nginx配置存在重復或者需廢棄的內容,于是在多次diff了新舊兩份配置內容后,小明認為最新配置是不影響業務的,因此在線推送更新配置后,直接reload了Nginx,出于double check原則,在線觀察了5分鐘Nginx日志:
發現出現大量類似下面的400錯誤:
400錯誤的產生,很可能影響服務端或客戶端的后續業務邏輯判斷,因此需要引起重視.
節點1
當時回滾配置后,小明先在搜索引擎查找了Nginx 400錯誤的可能原因和解決辦法,初步確定有下面兩種可能:1是空主機頭,2是請求包頭過大.
小明跟客戶端同學確認了客戶端請求方式,發現他們使用的是類似telnet的方式發起的http請求,類似下面的:
為了方便后續排查,小明參考線上環境臨時搭建了一套Nginx測試環境,重現了故障:
后來小明了解到原來客戶端不是從代碼的http庫調用, 而是按照上面的方式走TCP/telnet傳遞http參數來調用服務端http接口.但是為什么一樣的客戶端請求方式,舊配置完全ok,新配置則會出現大量400錯誤?
節點2
至此,小明懷疑自己沒有完全diff出新舊兩份配置的差別,于是他使用vimdiff再次對比新舊兩份配置.下面僅貼出關鍵配置:
舊配置:
新配置:
本次排查中,小明考慮的重點是新配置里遺漏了某些配置,于是他把location ~ (.*)的相關邏輯加上,發現問題依舊:
節點3
既然前面往缺失配置的思路走不通,下面就按照新增配置的思路排查,結果發現新配置增加了一些包頭信息,小明懷疑是請求包過大,于是優先排查了Nginx針對包頭大小的設置,其中有這么幾個配置:
client_header_buffer_size:默認是1k,所以header小于1k的話是不會出現問題的.
large_client_header_buffers:該命令用于設置客戶端請求的Header頭緩沖區的大小,默認值為4KB.
客戶端請求行不能超過large_client_header_buffers指令設置的值,客戶端請求的Header頭信息不能大于large_client_header_buffers指令設置的緩沖區大小,否則會報“Request URL too large”(414)或者“Bad-request”(400)錯誤,如果客戶端Cookie信息較大,則須增加緩沖區大小.于是小明將client_header_buffer_size和large_client_header_buffers都設置為128k.結果問題也重現了.
接下來,小明發現新配置中多了“proxy_set_header Host $http_host;”查找了Nginx官方文檔發現跟$http_host類似功能的還有$server_name和$host等變量,在他將$http_host更換成$host后,問題修復了.
根據Nginx官方文檔介紹,400狀態碼含義如下:
上面是http1.1的rfc關于host部分的解釋,從上面我們了解到如果一個http1.1的請求沒有host域,那么server應該給client段發送400的狀態碼,表明這個請求server不能處理.而對于Nginx server來說,也遵循這樣的方式,說明client發送了一個無效的請求,Nginx server無法處理,于是返回了400的狀態碼.
另外,關于$host和$http_host這兩個變量的區別如下:
本次故障中,客戶端的調用方式沒有使用host 參數,傳遞了空的Host頭給服務端,一旦Nginx設置了proxy_set_header Host $http_host,空Host頭就傳給了后端.然而,在http 1.1的規范中,Host只要出現空,就會返回400,所以出現了這個故障.而對于需要在Host字段里帶上端口信息的,則仍需要配置proxy_set_header Host $http_host.
最后,需要注意的是,400錯誤不一樣會影響業務,需要看具體的業務處理邏輯,比如使用nagios的check_tcp插件對Nginx server端口做檢測或者使用keepalived的tcp_check功能對后端Nginx端口的存活做檢測,這兩種情況都會在Nginx errorlog中產生400的請求.
原因也很簡單,就是一般tcp check的方式,就是建立tcp連接,但是沒有發送任何數據,當然也沒有Host頭,然后再reset或者四次揮手斷開連接.
運維規范
細心的同學會發現本次配置更新是在大中午操作,而且也沒有在測試環境測試通過,這在流程上是不嚴謹的.雖然Nginx等web服務的配置更新基本上通過熱更就可以了,但沒有灰度測試或者在測試環境測試,一是無法提前發現問題,二是無法控制業務影響.所以,在運維規范上看,即使是熱更也應當在測試環境測試正常后再同步到線上,其他的更新則應在業務低谷時操作.
技術學習方法
本次故障的產生,很大程度上就是運維同學不理解Nginx變量的定義和區別,直接從搜索引擎上找了些配置,檢查覺得正確就推到了線上.這里仍需要重申的是,以官方文檔為準!互聯網上很多知識或者配置有各種各樣的問題,隨時都有暗坑在里邊,只有啃過官方文檔才能避免誤讀.
Web日志分析
針對這里的Nginx錯誤日志查看,我們看到小明是用在線命令查看的,其實現在有很多web日志分析工具或系統,比如ELK(ElacticSearch+LogStash+Kibana),只需要配置好grok正則,是可以通過可視化界面實時監控web服務質量的.
引申
上面介紹了Nginx 400錯誤的可能原因和解決辦法,但實際工作中,我們遇到的可不止這么一點.于是,由此引申出去的是,針對那些Nginx常見錯誤如何去排查和解決.
403錯誤
403是很常見的錯誤代碼,一般就是未授權被禁止訪問的意思.
可能的原因有兩種:
Nginx程序用戶無權限訪問web目錄文件
Nginx需要訪問目錄,但是autoindex選項被關閉
修復方法:
授予Nginx程序用戶權限讀取web目錄文件
設置autoindex目錄為on
413錯誤
在上傳時Nginx返回了413錯誤:“413 Request Entity Too Large”,這一般就是上傳文件大小超過Nginx配置引起.
修復方法:
在Nginx.conf增加client_max_body_size的設置,這個值默認是1M,可以增加到8M以提高文件大小限制;
如果運行的是php,那么還要檢查php.ini,這個大小client_max_body_size要和php.ini中的如下值的最大值一致或者稍大,這樣就不會因為提交數據大小不一致出現的錯誤.
post_max_size = 8M
upload_max_filesize = 2M
502錯誤
Nginx 502 Bad Gateway的含義是請求的PHP-CGI已經執行,但是由于某種原因(一般是讀取資源的問題)沒有執行完畢而導致PHP-CGI進程終止.一般來說Nginx 502 Bad Gateway和php-fpm.conf的設置有關.
修復方法:
1、查看FastCGI進程是否已經啟動
ps -aux | grep php-cgi
2、檢查系統Fastcgi進程運行情況
除了第一種情況,fastcgi進程數不夠用、php執行時間長、或者是php-cgi進程死掉也可能造成Nginx的502錯誤.
運行以下命令判斷是否接近FastCGI進程,如果fastcgi進程數接近配置文件中設置的數值,表明worker進程數設置太少.
netstat -anpo | grep “php-cgi” | wc -l
3、FastCGI執行時間過長
根據實際情況調高以下參數值
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
504錯誤
Nginx 504 Gateway Time-out的含義是所請求的網關沒有請求到,簡單來說就是沒有請求到可以執行的PHP-CGI.
Nginx 504 Gateway Time-out一般與Nginx.conf的設置有關.
頭部太大這種情況可能是由于Nginx默認的fastcgi進程響應的緩沖區太小造成的, 這將導致fastcgi進程被掛起,如果你的fastcgi服務對這個掛起處理的不好,那么最后就極有可能導致504 Gateway Time-out.
默認的fastcgi進程響應的緩沖區是8K,可以調大以下參數:
fastcgi_buffer_size 128k;
fastcgi_buffers 8 128k;
fastcgi_busy_buffers_size 由 128K 改為 256K;
fastcgi_temp_file_write_size 由 128K 改為 256K.
此外,也可能是php-cgi的問題,需要修改php.ini的配置:
將max_children由之前的10改為30,這樣操作是為了保證有充足的php-cgi進程可以被使用.
將request_terminate_timeout由之前的0秒改成60秒,這樣使php-cgi進程處理腳本的超時時間提高到60秒,可以防止進程被掛起以提高利用效率.
原文出處:http://dbaplus.cn/news-21-1129-1.html
Nginx 400狀態碼排查
http://www.68idc.cn/help/jiabenmake/qita/20140707115201.html
Nginx變量大全
https://openresty.org/download/agentzh-Nginx-tutorials-en.html
Nginx報502、504、400、413錯誤
http://blog.csdn.net/miltonzhong/article/details/9195855