背景
業務架構:
部署細節:
兩容器均部署在同一機器上,通過 docker-compose
編排,并且通過link
方式鏈接。
故障描述
在有次更新代碼時,發現前端能夠打開,但是所有接口請求全是502(Bad GateWay)
故障排查
查看前端容器compose_ui_1
的日志,刷了一大波502(Bad GateWay)
UI沒問題的話,第一反映就是 compose_api_1
跪了,所以直接去容器看看日志
容器日志看起來很正常,沒有崩潰,而且這個日志就好像從來沒收到請求那樣,但是很明顯我前端肯定有訪問的,感覺很奇怪。將接口取出來單獨訪問試試看:
接口單獨訪問結果還是很殘暴的502(Bad GateWay)
,感覺還是不太可信,是不是端口或者主機什么訪問錯誤了?
本機開啟 wireshark 抓包確認下請求的主機和端口:
這樣就很確保前端compose_ui_1
訪問的主機和端口是正確的,而且確切結果是502(Bad GateWay)
,這樣只能從compose_api_1
下手排查了。
之前也是遇到相似的問題,因為compose_api_1
是通過uwsgi
部署的python flask
,那會總是用法覺得有點問題,改過uwsgi
配置之后消停了一會。現在又卷土重來了。
先判斷下compose_api_1
是不是真的跪了。。。雖然對這個沒抱什么希望。。。
直接訪問 后端api 接口
額。。。尷尬。。。仿佛冤枉錯好人了。這不對吧,抓包看看再次確認下先:
仿佛真的是。。。再 see see 容器日志:
額。。。好吧。。。我錯了,compose_api_1
沒跪。
于是問題來了。。。后端接口沒問題,前端訪問出錯了,見鬼了?
有種預感是容器的特性導致的問題。但愿不要。。
先進去compose_ui_1
容器抓包分析下,看看整個請求鏈有沒有問題:
似乎發現了點貓膩,Flags[R.]
是代表 tcp鏈接 被 reset
重置 了,但是為什么平白無故重置呢?
看到 172.17.0.5.8080
返回的, 先 telnet
問問先:
What???這就很迷了,首先這個 172.17.0.5.8080
哪來的呢?其次就是為毛端口不通?
突然想到一個很重要的問題:
容器之間是怎么知道它要把請求發給誰呢 ?
在前面已經交代過,這兩個容器是通過 link
的方式鏈接的,像下面這樣:
谷歌搜了下 link
工作原理:
link機制通過環境變量的方式提供了這些信息,除此之外像db的密碼這些信息也會通過環境變量提供,docker將source container中定義的環境變量全部導入到received container中,在received container中可以通過環境變量來獲取連接信息。使用了link機制后,可以通過指定的名字來和目標容器通信,這其實是通過給/etc/hosts中加入名稱和IP的解析關系來實現的
所以就是說在 compose_ui_1
的 根據指定的名字并在 /etc/hosts
翻譯出具體的ip然后進行通信咯?
看看容器的名字是啥?
compose_ui_1
的 /etc/hosts
root@e23430ed1ed7:/# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4 detectapi fc1537d83fdf compose_api_1
172.17.0.3 authapi ff83f8e3adf2 compose_authapi_1
172.17.0.3 authapi_1 ff83f8e3adf2 compose_authapi_1
172.17.0.3 compose_authapi_1 ff83f8e3adf2
172.17.0.4 api_1 fc1537d83fdf compose_api_1
172.17.0.4 compose_api_1 fc1537d83fdf
172.17.0.6 e23430ed1ed7
如果真是按照資料所說,那 172.17.0.4:8080
才是 compose_api_1
的地址隱射才對吧?,試下先
雖然返回了 auth product is None
,但其實這是有效的請求。
再看看 compose_api_1
容器的日志:
所以基本沒跑了, 為什么前端訪問直接就是 502, 原因就是 ui容器向錯誤的地址發送請求了
那么為什么會這樣呢?平白無故抽風了?
剛才根據 host 的記錄實驗了,按照它的映地址發起接口請求,是沒有問題的:
查看下 compose_ui_1
的 nginx
日志
尷尬。。。 nginx
日志居然直接連接到標準輸出和標準錯誤。。。
那為了簡單點,還是直接用 docker logs 查看吧
看來 nginx
的轉發已經是錯誤的,為什么會轉發到 172.17.0.5, 看看 nginx
關于轉發的配置:
這個 detectapi
和 上面貼出的 hosts
表能找到正確的地址 172.17.0.4
呀?搞不明白為什么會轉發到 172.17.0.5
難道是系統的域名解析錯誤了?
尼瑪這真是太神奇了。
男人的直覺告訴我 nginx
有貓膩!
重啟下容器的 nginx
,然而容器也被重啟了。。。
再訪問頁面,居然可以了。。。
再看看容器的nginx日志,已經轉發成功了
這樣子的話,其實應該能定位到,問題是出在了 nginx 上面?
故障定位
只是為什么 nginx
會有這樣的錯誤呢?不太應該呀。。 感覺應該是 nginx
內部域名解析緩存問題。
然后查了下資料,呵呵,還真有。https://www.zhihu.com/questio...
這就非常尷尬了。對這個問題抱有點懷疑,咨詢了資深大佬,然后大佬的回復就是:
如果 proxy_pass 后面跟的域名的話,在 nginx 啟動的時候就會初始化好,以后就只會復用這個值;參考:ngx_http_upstream_init_round_robin 函數
如果 proxy_pass 后面跟的是upstream,配置才會走解析和緩存的邏輯;
改善措施
- 不直接
proxy_pass
真實域名,而是轉發到upstream
配置; - 也可參考剛才的知乎鏈接處理方案:https://www.zhihu.com/questio...;
延展問題
- 為什么
compose_ui_1
指定的compose_api_1
會出錯? -
proxy_pass
如果后面跟真實域名,是真的直接復用還是有時間緩存?
本來想用 gdb
調試下這個問題,然而花了一天時間,毛都沒有。不過也有點小收獲,那就是如何配置nginx
來支持gdb
:
1.修改編譯配置文件:auto/cc/conf
ngx_compile_opt="-c" 改成 ngx_compile_opt="-c -g"
2../configure
時,增加編譯參數:--with-cc-opt='-O0'
, 避免編譯器優化;
例如:./configure --prefix=/usr/local/nginx --with-cc-opt='-O0' ....
如果不這樣的話,編譯器會優化代碼,導致調試過程中,循環中的一些變量值無法打印,會報下面的錯誤:
value optimized out
下面可以看下調試的效果:
nginx worker process 處理入口:ngx_http_static_handler
歡迎各位大神指點交流, QQ討論群: 258498217
轉載請注明來源: https://segmentfault.com/a/11...