Nginx中關於虛擬主機的一點冷門知識

三國夢迴發表於2022-02-26

一些閒聊

座標深圳南山。

前兩天公司晚上9點過,通知第二天要48小時核酸才能進辦公樓。看到訊息,已經是9點半多了,走到公司附近的核酸點,是10點過。然後發現那個點人好少,走近了才發現核酸點已經下班了,不過醫護人員還沒走,旁邊也圍了一些群眾,應該是想讓醫護人員再做幾個。

看樣子做不了,我就準備換個點了,還沒走遠,就聽到工作人員很大聲地在說:“誰來理解我們?我們從大年初一就開始在這裡測核酸。誰來理解我們”,“誰明天來這裡做一天義工試試?”

除夕晚上,深圳出現了疫情,看深小衛下面的留言,確實是不少人,剛到家,吃了個飯,就開始買票/開車回深圳。

也不說啥了,互相多些理解吧。

事後我就想起來那句話:中國真的是被這個國家最勇敢的人保護得很好。(ps,最勇敢的人保護了這塊土地,也保護了這塊土地上的人)

前言

nginx的虛擬主機,不知道大家瞭解不。以前吧,如果在nginx上要反向代理多個服務,我一般是讓nginx監聽多個不同埠,比如8080/8081,不同埠,反向代理到不同的服務。

server {
	listen 9981 so_keepalive=on;
	proxy_pass service1;
}

server {
	listen 9982 so_keepalive=on;
	proxy_pass service2;
}

來了現在公司,發現這邊是基於域名(埠都是80)來反向代理到不同服務,如下:

基於以上的nginx配置,域名a和b,分別代表了系統a和系統b,實際dns是指向同一臺nginx機器。當你用域名a訪問時,就會走上面的配置;域名b訪問時,就會走下面的配置。

對線上配置的一個小疑問

問題背景

我的一位同事,和我差不多時間入職,接手了一個幾年前的系統,看到線上環境的nginx配置,表示有點懵,不知道how it works。

這裡把這個問題,簡單描述下。

為了方便我這邊模擬,假設機器ip為10.0.0.6,機器上有個python指令碼,會去訪問一個api:http://10.0.0.6:80

機器上的/etc/hosts如下:

[root@VM-0-6-centos nginx]# cat /etc/hosts
......

10.0.0.6 bbbb.com

也就是說,訪問bbbb.com,就相當於訪問這臺機器了。

[root@VM-0-6-centos nginx]# ping bbbb.com
PING bbbb.com (10.0.0.6) 56(84) bytes of data.

基於以上資訊,這個api訪問本機的80,是會到本機的nginx(nginx監聽80埠),nginx配置如下:

這個配置,基於我們對虛擬主機的瞭解,也就是說,訪問aaaa.com,就會到第一段的配置,aaaa.access.log裡面就會有訪問日誌;如果是訪問bbbb.com,就會到第二段的配置,bbbb.access.log就會有訪問日誌。

但是,客戶端不按套路出牌啊,用的是,10.0.0.6:80/xx這樣的url來訪問該nginx,同時,/etc/hosts裡面有配置bbbb.com指向本機,那麼,大家覺得最終的訪問結果如何?

實測結果

結果是,訪問了aaaa那一段。

這。。。我們就有點想不通了,沒理由啊,為啥呢,why?不是不能接受結果,而是不知道為什麼會這樣。

探索

排查網路

按理說,用域名形式訪問,應該才會去檢視/etc/hosts檔案和dns系統,找到域名背後的ip;但是,按ip訪問,貌似java建立裡面的socket底層實現利,也會有根據ip去獲取host的程式碼(還導致一些超時問題之類的)。

所以,我們猜測,難道是,訪問10.0.0.6時,檢視了/etc/hosts,把10.0.0.6轉換成了aaaa.com?但是,/etc/hosts裡面只有把10.0.0.6轉換為bbbb.com的可能性吧?

本著不管三級二十一,先抓個包再說的想法,於是在nginx的80埠,開了個tcpdump:

[root@VM-0-6-centos ~]# tcpdump -i lo tcp port 80 -Ann

然後再次訪問:

[root@VM-0-6-centos nginx]# curl 10.0.0.6:80

抓包結果:

看到這邊host是 10.0.0.6.

這讓我開始懷疑,可能和/etc/hosts沒什麼關係,問題應該變成了:nginx在拿到上面這段http報文時,為啥要路由到aaaa.com那一段配置裡面去。

排查nginx

怎麼才能知道nginx做了啥呢,我們又沒有程式碼,但是,好歹,我們還有shell 命令啊。 strace這個命令,可以檢視一個程式的系統呼叫,還是比較好用的。

為啥要查這個?因為我懷疑是不是nginx拿到10.0.0.6後,把它轉成了域名,不過,轉的話,應該也是轉換為bbbb.com。另外,這個命令有沒有用,我也不確定,因為該命令只能看這個程式發起了哪些系統呼叫(不是java裡的rpc),而是對核心發起的系統呼叫(system call)。萬一,ip轉域名的部分,沒有通過系統呼叫實現的話,那這個命令就失效了。

不過還是試試?

先拿到了nginx的worker程式的pid:8845

[root@VM-0-6-centos nginx]# ps -ef|grep nginx
root       610     1  0 12:15 ?        00:00:00 nginx: master process ./nginx
nobody    8845   610  0 13:13 ?        00:00:00 nginx: worker process

再開啟一個strace:

[root@VM-0-6-centos ~]# strace -p 8845 -s 1024 -q -f -v -e  trace=network

結果如下:

好像只有接收網路請求的系統呼叫(recvfrom),系統呼叫,大家可以拿這個函式名去網上查。

此時,排查陷入了僵局,於是,我只能提議,我先回座位上找找nginx相關資料。

然後就開始在網上查,運氣也還不錯,就找到了:

https://docs.nginx.com/nginx/admin-guide/web-server/web-server/

上面這段話,大概就是說,虛擬主機的匹配,是通過取req報文裡的host欄位,來和nginx.conf中server裡的server_name做匹配,

因為server_name可以是萬用字元之類的,所以這裡有個優先順序,完全匹配》模糊匹配。

上圖的最後那幾行,就是關鍵了:

如果完全沒匹配上(我們這裡就是,拿了個ip來匹配,然而nginx.conf裡配置的是aaaa.com和bbbb.com),就會路由到這個埠的預設server。預設server是哪個呢,就是:nginx.conf裡埠為80的、且寫在第一個的server。

ok,打完收工。

總結

知識點可能很小,但排查也比較麻煩,因為線上環境不好動,然後配置的域名其實不止兩個,有7/8個,中間繞的路比上面其實還多一點。

不過這邊的大概思路是這樣的,希望對大家也有一點點幫助。

相關文章