線上故障實錄-一大早服務就不可用了?
難得一個週末,一大早還沒有睡醒就接到另外一個團隊的電話,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)
# 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)
這個文章裏面主要說的是在配置中,使用如下這種姿勢導致端口占用
server {
listen :80;
listen [::]:80;
}
對應提請的解決方案是隻保留一個,或者在後面加一個 ipv6 的限定
server {
listen 80;
listen [::]:80 ipv6only=on;
}
# or
server {
listen [::]:80;
}
但是我的 nginx 配置中本來就只有一個listen 80
,沒有上面兩個,講道理不應該會衝突纔對
注意到nginx.conf
配置文件中有下面這一行
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
難道是 conf.d 目錄下的配置中,某個配置文件和最外面的衝突了導致的麼,正好這個目錄下只有一個配置文件,先幹掉它
cd conf.d
mv xxx.conf xxx.conf.bk
nginx
然後發現 nginx 順利的起來了,通過再次查看,果然是上面這個原因導致的 80 端口衝突,調整一下即可;然後就剩下一個疑問,就這個配置,之前是怎麼起來的(可能只有最開始部署這個的同學才知道了...)
最後揭曉一下,爲啥這個 nginx 進程會掛掉,對於這個原因我也是很憂桑
5. 小結
其實這個問題最後看來還是比較簡單的,根本原因在於某個單點的 nginx 跪了,導致整個服務不可用,這也暴露了幾個比較嚴重的缺陷
- 單點問題
- 監控缺失(核心鏈路的進程監控還是比較重要的,在整個問題的排查中,真沒有想到會是 nginx 進程沒有的情況)
- 短信要及時看,並提醒給相應的小夥伴(阿里雲已經提醒了,可惜這條短信是在最後問題修復之後才告訴到負責這一塊內容的小夥伴,這種事後,也就給我們排查爲啥 nginx 跪了有點幫助了 🐩)
II. 其他
1. 一灰灰 Blog: https://liuyueyi.github.io/hexblog
一灰灰的個人博客,記錄所有學習和工作中的博文,歡迎大家前去逛逛
2. 聲明
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
- 微博地址: 小灰灰 Blog
- QQ: 一灰灰/3302797840
3. 掃描關注
一灰灰 blog