線上故障實錄-一大早服務就不可用了?
難得一個週末,一大早還沒有睡醒就接到另外一個團隊的電話,app 打不開了,所有的資料都沒有了,睡意全無,趕緊起來看能不能緊急搶救一下,最終發現是一個關鍵鏈路的 nginx 配置錯誤,導致 nginx 無法啟動,接下來完整的記錄下愉快的週末中,這個不愉快的早晨
1. 專案環境首先說一下背景,出問題的這個專案是我之前參與的,現在由另外的小夥伴負責。這個專案使用 nginx 作為反向代理,因為某些業務上的原因,搞了一個香港和大陸之間的專線,下面又有一層的 nginx 進行不同業務的請求轉發
後端服務基於 SpringCloud 微服務搭建,通過統一閘道器對外提供基本的業務服務; 也有部分服務不是通過閘道器,直接 nginx 轉發過去的(比如內部使用的控制檯就沒有走閘道器)
大致的結構如上圖,實際有一些區別,至於為什麼選擇這種架構設計,與實際的業務場景以及之前遭遇過的一次 ddos 有關,這個與本文主題關係不大,就不詳細展開
2. 問題描述接下來我們先看一下出現的狀況,運營的小夥伴最早接到反饋,app 上所有的資料都沒了,並提供了一個上次出現這種場景的原因是域名的證書過期
注意,這裡有兩個關鍵資訊點
資料沒有 -> 直觀反映是不是業務服務跪了導致的證書過期 -> 我們的域名採用的是 let's encrypt 進行證書頒發,只有三個月的有效期,所以也不是不存在這種可能3. 問題追蹤前面是背景介紹,然後要開始進入正題,起床第一件事,呃並不是開啟電腦,而是先開啟 appp 看一下是個什麼狀況,畢竟不能完全憑運營一說,就開始找問題
app 表現與運營同學描述一致,所有資料也空白,直觀的表現就是服務端 gg 了
嘗試解決思路
下面的文字相對冗長,基本思路可以參考下面這個圖
先從運營同學提供的思路來看一下證書是否過期,直接瀏覽器輸入 app 介面對應的一級域名xxx.com,結果發現被 302 到另外一個域名,還真的是證書過期,關鍵是這個也不是剛剛過期,而是過期了五十多天,這個時間對不上
既然一級域名沒法直接檢視,那就選擇 app 直接請求介面的域名,來看一下是不是過期了,結果尷尬的事情是我不知道這個二級域名的字首是啥(為了避免 ddos,我們之前做的一個方案是隨機生成了很多二級域名字首,然後根據使用者的地域進行二級域名的選取),所以只能通過抓包了
找到域名之後(假設為 xxx.a.com),直接瀏覽器訪問,毫無意外提示"無法訪問此網路",那這個域名證書是否過期就不好確定了
出現上面這個表現,自然而然的就是ping xxx.a.com,可以 ping 通,證明這個域名解析沒有問題
簡單的從域名上沒有找到明顯的突破口,接著去確認一下服務是否還線上,登入伺服器,jps -l檢視一下當前程序,結果居然發現居然沒有zuul閘道器程序,難道是閘道器跪了導致的麼,貌似有眉目了,在準備重啟之前,看了一下日誌,居然發現有正常的心跳日誌,這特麼的就鬼畜了啊,程序都沒有,日誌還在打;然後謹慎的用top看了一下伺服器程序,zuul程序還在,不過坑爹的是它居然是 root 許可權啟動的;所以我用普通賬號執行jps -l沒有展示。針對這種狀況,強烈建議所有的小夥伴,不要用 root 使用者在伺服器上搞事情
確定服務還在之後,使用curl http://127.0.0.1:8080/xxx來發出請求,正常響應,ok,服務沒掛,那麼問題就出現在上層
然後登入上層的幾個 nginx 伺服器,檢視對應的 nginx 訪問日誌tail -f /var/log/nginx/access.log,以及異常日誌tail -f /var/log/nginx/error.log,裡面有幾個之前的 ssl 驗證失敗的日誌,好像也不是導致這個問題的原因
從日誌檔案上,看不出太多的資訊,接著從最上層的 nginx 出發,ping 域名,層層下推,結果發現到了某一臺機器之後,ping 了沒反應,然後檢視nginx.conf配置,起初是懷疑這裡是不是被人動過了(雖然說可能性比較小),這個時候走了彎路,配置上看不出任何問題,然後下意思的查看了一下 nginx 程序ps aux | grep nginx,結果發現程序不在,原因找到
nginx 程序為什麼會突然沒了,這個後面在說
4. 問題 fix既然發現是因為 nginx 程序不再導致的原因,那就簡單了,啟動 nginx 就好了,結果發現 nginx 程序死活都起不來,一直提示 80 埠被佔用, nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) but no 80 process can find
遇到上面這個問題,要解決還不簡單,找到佔用 80 埠的程序,幹掉它
netstat -ntulp | grep 80
讓人詫異的是,沒有找到任何佔用 80 埠的程序
網上搜索了一下,有不少小夥伴是通過下面這個命令解決的 NGINX BIND() TO 0.0.0.0:80 FAILED (98: ADDRESS ALREADY IN USE) TO 0.0.0.0:80 FAILED (98: ADDRESS ALREADY IN USE)")
# use fuser to kill process using port 80!fuser -k 80/tcp
很遺憾的是,使用上面這個命令依然沒有能解決問題;這就很尷尬了啊,這個時候只能祭出我的大殺器了--重啟伺服器
reboot
經過短暫的重啟之後,再次啟動 nginx,嗯,依然沒有解決問題;nginx 居然死活起不來,這個問題就有點大發了,先解決線上問題讓服務可用吧,臨時調整了一下轉發規則,把這臺機器摘掉,操作完畢之後服務恢復
接下來我們的問題就是這個 nginx 為啥起不來
這裡有一篇文章帶來了一些思路 [Fix nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)](/file/2020/07/28/20200728230910_3961.jpg "Fix nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)")
這個文章裡面主要說的是在配置中,使用如下這種姿勢導致端口占用
server { listen :80; listen [::]:80;}
對應提請的解決方案是隻保留一個,或者在後面加一個 ipv6 的限定
server { listen 80; listen [::]:80 ipv6only=on;}# orserver { listen [::]:80;}
但是我的 nginx 配置中本來就只有一個listen 80,沒有上面兩個,講道理不應該會衝突才對
注意到nginx.conf配置檔案中有下面這一行
# Load modular configuration files from the /etc/nginx/conf.d directory.# See /file/2020/07/28/20200728230911_3962.jpg.html for more information.include /etc/nginx/conf.d/*.conf;
難道是 conf.d 目錄下的配置中,某個配置檔案和最外面的衝突了導致的麼,正好這個目錄下只有一個配置檔案,先幹掉它
cd conf.dmv xxx.conf xxx.conf.bknginx
然後發現 nginx 順利的起來了,通過再次檢視,果然是上面這個原因導致的 80 埠衝突,調整一下即可;然後就剩下一個疑問,就這個配置,之前是怎麼起來的(可能只有最開始部署這個的同學才知道了...)
最後揭曉一下,為啥這個 nginx 程序會掛掉,對於這個原因我也是很憂桑
5. 小結其實這個問題最後看來還是比較簡單的,根本原因在於某個單點的 nginx 跪了,導致整個服務不可用,這也暴露了幾個比較嚴重的缺陷
單點問題監控缺失(核心鏈路的程序監控還是比較重要的,在整個問題的排查中,真沒有想到會是 nginx 程序沒有的情況)簡訊要及時看,並提醒給相應的小夥伴(阿里雲已經提醒了,可惜這條簡訊是在最後問題修復之後才告訴到負責這一塊內容的小夥伴,這種事後,也就給我們排查為啥 nginx 跪了有點幫助了 )