k8s部署nginx的url反向代理初探

背景

??團(tuán)隊(duì)中做一個(gè)項(xiàng)目,使用Django開(kāi)發(fā)多個(gè)應(yīng)用,如app1、app2等,均采用docker鏡像部署到k8s環(huán)境中,整體架構(gòu)為:

用戶client => nginx反向代理路由分發(fā) => app1/app2/app3/...

??常規(guī)處理方案:

  • 使用3個(gè)域名,如a.com.cn表示訪問(wèn)app1,b.com.cn表示訪問(wèn)app2,c.com.cn表示app3,且這3個(gè)域名都指向同一IP地址IP_ABC。
  • 當(dāng)nginx接收到這3個(gè)域名請(qǐng)求后,會(huì)根據(jù)nginx配置中server_name進(jìn)行匹配,然后直接代理proxy_pass到app1或app2或app3的地址上。
  • nginx 的app1簡(jiǎn)單示例如下:
server {
    listen     80  ;
    server_name    a.com.cn;
    location / {
        proxy_pass   http://app1_addr:app1_port/;
    }
}

??此種方案簡(jiǎn)單清晰,對(duì)于api接口及靜態(tài)頁(yè)面(如django中的admin管理臺(tái)頁(yè)面)不會(huì)產(chǎn)生任何問(wèn)題,因?yàn)橐粋€(gè)app對(duì)應(yīng)一個(gè)域名,從邏輯上看是一對(duì)一的訪問(wèn),app之間互相隔離。

問(wèn)題源起及解決思路

  • 當(dāng)app逐漸增多時(shí),域名數(shù)量隨之增多,雖然都部署在同一個(gè)k8s集群命名空間中。域名管理起來(lái)費(fèi)事。
  • 當(dāng)app經(jīng)常會(huì)變化時(shí),除了修改nginx代理配置外,還得與調(diào)用端(可能是其它外部系統(tǒng))一同修改,調(diào)用端配置起來(lái)也麻煩。

??有了這些問(wèn)題,那么我們就嘗試用url前綴去識(shí)別路由分發(fā),實(shí)現(xiàn)通過(guò)url前綴進(jìn)行反向代理,而不是通過(guò)域名去反向代理。
??比如/A/xxx這種url就路由到app1,/B/xxx這種url就路由到app2,依次類推。
??剛開(kāi)始配置還是很簡(jiǎn)單的,nginx配置如下:

server {
    listen     80  ;
    server_name    abc.com.cn;  //統(tǒng)一的一個(gè)域名
    location ^~ /A/ {
        proxy_pass   http://app1_addr:app1_port/;
    }
    ** app2和app3的照著上面/A/的配置修改下。**
}

??此配置大概意思是當(dāng)匹配到帶/A/這種前綴的url時(shí),就將請(qǐng)求路由到app1_addr:app1_port/中。具體location指令請(qǐng)參考nginx手冊(cè)。

問(wèn)題一:admin登錄無(wú)法跳轉(zhuǎn)

??api接口訪問(wèn)正常,但在請(qǐng)求http://abc.com.cn:8080/A/admin/,使用django自帶的管理臺(tái)時(shí),無(wú)法進(jìn)行302的跳轉(zhuǎn)。在新的url頁(yè)面無(wú)法訪問(wèn)。
??經(jīng)分析,過(guò)程是這樣的:

  1. 瀏覽器請(qǐng)求http://abc.com.cn:8080/A/admin/;
  2. nginx進(jìn)行代理,去掉了前綴/A/,請(qǐng)求django應(yīng)用app1時(shí),url為http://app1_ip:app1_port/admin/
  3. app1(django)接收到請(qǐng)求,重定向到/admin/login/?xxxxx,此時(shí)返回給瀏覽器端的response header中l(wèi)ocation為/admin/login/?xxx;
  4. 瀏覽器當(dāng)遇到301或302時(shí),直接根據(jù)location值,進(jìn)行新請(qǐng)求,此時(shí)新的url為http://abc.com.cn:8080/admin/login/?xxx;
  5. 新請(qǐng)求失敗了,因?yàn)樾碌膗rl確實(shí)不存在。

??此問(wèn)題出現(xiàn)在第4步,需要nginx在遇到301或302時(shí),重新修改response header中l(wèi)ocation值,這樣瀏覽器就能按添加前綴/A/后的url進(jìn)行請(qǐng)求了。
??修改配置后如下:

server {
    listen     80  ;
    server_name    abc.com.cn;  //統(tǒng)一的一個(gè)域名
    location ^~ /A/ {
        proxy_pass   http://app1_addr:app1_port/;
        proxy_redirect ~^/(.*)$   http://abc.com.cn:8080/A/$1;
    }
    ** app2和app3的照著上面/A/的配置修改下。**
}

??新增加一行proxy_redirect指令,具體指令說(shuō)明請(qǐng)參見(jiàn)nginx手冊(cè),大概意思是告訴nginx,當(dāng)遇到301或302服務(wù)器端重定向時(shí),按^/(.*)$進(jìn)行正則匹配,即匹配/xxxx這種url。當(dāng)匹配成功后,將響應(yīng)header中l(wèi)ocation值修改成http://abc.com.cn:8080/A/$1,再將響應(yīng)繼續(xù)返回給瀏覽器。其中$1表示正則匹配中(...)中的元組序號(hào)。
??當(dāng)然,根據(jù)實(shí)際需求,還可以再添加多個(gè)proxy_rediect指令,原理一樣:

proxy_redirect ~^http://.*/(.*)$   http://abc.com.cn:8080/A/$1;
proxy_redirect ~^http://.*:\d+/(.*)$   http://abc.com.cn:8080/A/$1;

問(wèn)題二:靜態(tài)頁(yè)無(wú)法訪問(wèn)

??現(xiàn)在api、admin管理臺(tái)能訪問(wèn)了,但發(fā)現(xiàn)管理臺(tái)的js/css/html/jpg這些靜態(tài)資源都訪問(wèn)失敗。
??分析如下,當(dāng)django返回html到瀏覽器后,瀏覽器會(huì)解析html中靜態(tài)資源url并請(qǐng)求,此時(shí)靜態(tài)資源的url為/static/admin/xx/x.js或/admin/xxx/xxx這種格式。瀏覽器去請(qǐng)求這些url當(dāng)然不存在,nginx便會(huì)直接報(bào)不存在資源。
??此時(shí),可以有兩種方案:

  • 修改django工程,將admin中返回的html或js文件中的請(qǐng)求統(tǒng)一添加前綴;
  • 在nginx代理中統(tǒng)一處理響應(yīng)內(nèi)容,將響應(yīng)內(nèi)容中url進(jìn)行替換,再返回給瀏覽器使用。

??第一種方法找了找,發(fā)現(xiàn)不好修改,畢竟admin是django集成在安裝包中的,工程里并不會(huì)去直接繼承或二次封裝admin模塊,因此放棄了。
??第二種方法找到了nginx中sub_filter指令,再次修改如下:

server {
    listen     80  ;
    server_name    abc.com.cn;  //統(tǒng)一的一個(gè)域名
    location ^~ /A/ {
        proxy_pass   http://app1_addr:app1_port/;
        proxy_redirect ~^/(.*)$   http://abc.com.cn:8080/A/$1;
        sub_filter   /admin/    /A/admin/;
        sub_filter   /static/     /A/static/;
        sub_filter_once  off;
    }
    ** app2和app3的照著上面/A/的配置修改下。**
}

??新增加sub_filter*三行指令,具體指令用法請(qǐng)參見(jiàn)手冊(cè),大概意思是nginx根據(jù)sub_filter_types(本文沒(méi)寫,默認(rèn)是text/html),去檢查每次的響應(yīng)內(nèi)容,若內(nèi)容為text/html,則進(jìn)行替換操作,將響應(yīng)內(nèi)容中的【/admin/】字符串替換成【/A/admin/】字符串,即增加前綴操作,static同理。最后再添加sub_filter_once off;表明上面的替換是替換響應(yīng)內(nèi)容的所有地方,如果不加這句指令,則只會(huì)替換一次。
??若工程中還存在其它子應(yīng)用的靜態(tài)工程,也可照此來(lái)進(jìn)行替換操作。
??這種替換后,原工程不用修改,不影響本地開(kāi)發(fā)調(diào)試,感覺(jué)還是不錯(cuò)的。

結(jié)果

??現(xiàn)在api接口、admin管理臺(tái)都能正常訪問(wèn)了。完成了通過(guò)url前綴方式來(lái)反向代理功能。
??實(shí)現(xiàn)了瀏覽器或外部系統(tǒng)只需要通過(guò)
http://abc.com.cn:8080/A/xxxx
http://abc.com.cn:8080/B/xxxx
http://abc.com.cn:8080/C/xxxx
??這種格式的URL訪問(wèn)即可,只需一個(gè)域名,僅通過(guò)/A/這種前綴來(lái)區(qū)分不同的子應(yīng)用。
??一般我們會(huì)使用前后端分離去開(kāi)發(fā)項(xiàng)目,若有必要,也可以使用sub_filter去替換相應(yīng)的請(qǐng)求內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容