摘要:近日發(fā)現(xiàn)一個(gè)問(wèn)題應(yīng)用程序在返回的時(shí)候丟失了原先訪問(wèn)的端口。于是懷疑問(wèn)題出在這幾個(gè)上。在中,在描述的時(shí)候提到,其返回的必須是。修改的端口為靠譜這個(gè)方法比較靠譜,只要將的端口改成就沒(méi)有問(wèn)題了。使用靠譜使用提供的,將的值做文本替換。
github
近日發(fā)現(xiàn)一個(gè)問(wèn)題:應(yīng)用程序在返回Http Redirect的時(shí)候丟失了原先訪問(wèn)的端口。比如,我們這樣訪問(wèn)http://IP-A:Port-A/app/delete,這個(gè)url會(huì)響應(yīng)302,但是它返回的Response header Location里丟失了端口,正確的結(jié)果應(yīng)該是這樣:http://IP-A:Port-A/app/index,但返回的卻是:http://IP-A/app/index,把端口丟失了。
基本情況我們的部署情況是這樣的:
部署了Nginx Ingress,并使用NodePort的方式把Nginx Ingress Service暴露出來(lái)
配置了App的Ingress
服務(wù)器信息:
Server Name | NAT Server | K8S Node | Nginx Ingress Svc | Nginx Ingress Pod | App Svc | App Pod |
---|---|---|---|---|---|---|
IP | IP-A | IP-B | IP-C(Cluster IP/VIP) | IP-D(Cluster IP) | IP-E(Cluster IP/VIP) | IP-F(ClusterIP) |
Port | Port-A | Port-B(Nginx Ingress Svc"s NodePort) | Port-C | 80(Container Port) | Port-E | Port-F |
其實(shí)以上也不全是服務(wù)器,其中有兩個(gè)K8S Service不是服務(wù)器,它們是VIP,關(guān)于這個(gè)請(qǐng)看K8S - Using Source IP一文,當(dāng)訪問(wèn)http://IP-A:Port-A/app/delete的時(shí)候,這個(gè)請(qǐng)求從左到右貫穿了這些服務(wù)器。
順便一提上面的NAT Server是一臺(tái)普通的服務(wù)器,我們用它做了PAT使我們的Nginx Ingress能夠被外網(wǎng)訪問(wèn)到。
觀察我們使用之前提到過(guò)的Echo Server來(lái)觀察透過(guò)Ingress訪問(wèn)Echo Server時(shí)傳遞給Echo Server的Request header:http://IP-A:Port-A/echo-server,得到了這些有趣的Request header:
host=IP-A:Port-A x-original-uri=/echo-server x-forwarded-for=IP-B x-forwarded-host=IP-A:Port-A x-forwarded-port=80 x-forwarded-proto=http
然后直接訪問(wèn)Echo Server Svc,發(fā)現(xiàn)是沒(méi)有上面提到的x-*Request header的。于是懷疑問(wèn)題出在這幾個(gè)header上。
名詞解釋來(lái)講一下這些頭各自代表什么意思。
x-forwarded-for,client訪問(wèn)proxy的時(shí)候,client的ip。
在這里之所以是K8S Node的IP,是因?yàn)樵贜ginx Ingress看來(lái)請(qǐng)求是來(lái)自K8S Node的(好好看看之前提到的K8S - Using Source IP一文),在這之前的NAT它是不知道的。
x-forwarded-host,client訪問(wèn)proxy的時(shí)候,訪問(wèn)的原始host。
x-forwarded-proto,client訪問(wèn)proxy的時(shí)候,訪問(wèn)的原始http scheme。
x-forwarded-port,client訪問(wèn)proxy的時(shí)候,訪問(wèn)的port。
x-original-uri,查不到權(quán)威資料。
注意,前三個(gè)是事實(shí)標(biāo)準(zhǔn),MDN有收錄,x-forwarded-port和x-original-uri似乎是私有擴(kuò)展。
實(shí)驗(yàn)找一個(gè)趁手的Http Request工具(我用的是Postman),記得把Follow redirect關(guān)掉,然后模擬Nginx請(qǐng)求的方式(就是把上面提到的x-* header帶上/去掉/修改值)直接請(qǐng)求App Svc。
結(jié)果發(fā)現(xiàn)x-forwarded-port是Response header Location的關(guān)鍵,即如果x-forwarded-port=Port-A的話,Location就會(huì)帶上正確的端口。
分析 Redirect url是如何構(gòu)造的可以推測(cè),App利用了host和x-forwarded-*這些header來(lái)構(gòu)造redirect url。
在Java Servlet API中,在描述HttpServletResponse#sendRedirect的時(shí)候提到,其返回的URL必須是Absolute URL。
Tomcat的org.apache.catalina.connector.Response的toAbsolute方法負(fù)責(zé)構(gòu)造Absolute URL。
那么它又是如何知道選用什么Port的呢?這個(gè)和RemoteIPValve有關(guān),有興趣的話你可以查閱相關(guān)文檔。
上面只是講了Tomcat是如何構(gòu)造redirect url的,但這個(gè)方法不是標(biāo)準(zhǔn)的,不同的容器有各自的實(shí)現(xiàn),畢竟Java Servlet API也沒(méi)有規(guī)定如何構(gòu)造Absolute URL。
我之前也寫(xiě)過(guò)一篇相關(guān)話題的文章《反向代理使用https協(xié)議,后臺(tái)tomcat使用http,redirect時(shí)使用錯(cuò)誤協(xié)議的解決辦法》,你可以看一看。
為何x-forwarded-port是80那么問(wèn)題來(lái)了,我明明訪問(wèn)的是IP-A:Port-A,為何Nginx取到的值是80?
這是因?yàn)樵谡麄€(gè)請(qǐng)求鏈路的前段:NAT Server > K8S Node > Nginx Ingress Svc 都是在第4層工作的,可以認(rèn)為它們干的事情都是NAT,Nginx Ingress Pod是不知道這些服務(wù)器/網(wǎng)絡(luò)節(jié)點(diǎn)的端口,因此它只能把自己的端口80(容器內(nèi)Port)給x-forwarded-port。
關(guān)于這個(gè)邏輯你可以查看Nginx Ingress的配置文件就能夠知道了:
kubectl -n kube-system exec -it解決辦法 請(qǐng)求時(shí)帶上x(chóng)-forwarded-port(不靠譜)-- cat /etc/nginx/nginx.conf
查看Nginx Ingress配置文件發(fā)現(xiàn)如果最初請(qǐng)求的時(shí)候帶上x-forwarded-port的話,就能夠改變它傳遞到后面的值,但是這有兩個(gè)問(wèn)題:
通過(guò)瀏覽器訪問(wèn)時(shí),你沒(méi)有辦法加上這個(gè)header
這個(gè)header一般都是反向代理加的,也就是在我們的Nginx Ingress之前還得有一個(gè)反向代理
所以這個(gè)方法不好。
修改tomcat的代碼(不靠譜)雖然可以通過(guò)修改tomcat的代碼,讓它從x-forward-host/host header來(lái)取port,但是這個(gè)不現(xiàn)實(shí)。
修改NAT Server的端口為80(靠譜)這個(gè)方法比較靠譜,只要將NAT Server的端口改成80就沒(méi)有問(wèn)題了。
事實(shí)上,如果你直接訪問(wèn)K8S Node的話(NodePort方式),也是要將NodePort設(shè)置為80,記得前面說(shuō)的嗎?Nginx Ingress無(wú)法知道上層NAT的端口。
總而言之,就是你最初請(qǐng)求的URL不能是80之外的端口,必須是http://some-ip/app才可以。
使用Nginx Ingress Annotations(靠譜)使用Nginx Ingress提供的Proxy redirect annotations,將Location的值做文本替換。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/32668.html
摘要:近日發(fā)現(xiàn)一個(gè)問(wèn)題應(yīng)用程序在返回的時(shí)候丟失了原先訪問(wèn)的端口。于是懷疑問(wèn)題出在這幾個(gè)上。在中,在描述的時(shí)候提到,其返回的必須是。修改的端口為靠譜這個(gè)方法比較靠譜,只要將的端口改成就沒(méi)有問(wèn)題了。使用靠譜使用提供的,將的值做文本替換。 github 近日發(fā)現(xiàn)一個(gè)問(wèn)題:應(yīng)用程序在返回Http Redirect的時(shí)候丟失了原先訪問(wèn)的端口。比如,我們這樣訪問(wèn)http://IP-A:Port-A/ap...
摘要:參與者流量來(lái)自于內(nèi)部系統(tǒng)和外部流量,其中大部分來(lái)自于外部流量。水平擴(kuò)容服務(wù)的水平擴(kuò)容重要性不言而喻。 背景 目前微店中臺(tái)團(tuán)隊(duì)為了滿足公司大部分產(chǎn)品、運(yùn)營(yíng)以及部分后端開(kāi)發(fā)人員的嘗鮮和試錯(cuò)的需求,提供了一套基于圖形化搭建的服務(wù)端接口交付方案,利用該方案及提供的系統(tǒng)可生成一副包含運(yùn)行時(shí)環(huán)境定義可立即運(yùn)行的工程代碼,最后,通過(guò) 某種serverless平臺(tái) 實(shí)現(xiàn)生成后代碼的部署、CI、運(yùn)行、反...
摘要:中暴露服務(wù)訪問(wèn)自己實(shí)現(xiàn)了一個(gè),它本質(zhì)上是包裝了,在真正創(chuàng)建負(fù)載均衡器上它會(huì)調(diào)用來(lái)創(chuàng)建自身的。 Kubernetes概述 最近的一年,kubernetes的發(fā)展如此閃耀,正被越來(lái)越多的公司采納用于生產(chǎn)環(huán)境的實(shí)踐。同時(shí),我們可以在最著名的開(kāi)發(fā)者問(wèn)答社區(qū)StackOverflow上看到k8s的問(wèn)題數(shù)量的增長(zhǎng)曲線(2015.5-2016.5),開(kāi)發(fā)者是用腳投票的,從這一點(diǎn)看也無(wú)疑證明了k8s的...
摘要:中暴露服務(wù)訪問(wèn)自己實(shí)現(xiàn)了一個(gè),它本質(zhì)上是包裝了,在真正創(chuàng)建負(fù)載均衡器上它會(huì)調(diào)用來(lái)創(chuàng)建自身的。 Kubernetes概述 最近的一年,kubernetes的發(fā)展如此閃耀,正被越來(lái)越多的公司采納用于生產(chǎn)環(huán)境的實(shí)踐。同時(shí),我們可以在最著名的開(kāi)發(fā)者問(wèn)答社區(qū)StackOverflow上看到k8s的問(wèn)題數(shù)量的增長(zhǎng)曲線(2015.5-2016.5),開(kāi)發(fā)者是用腳投票的,從這一點(diǎn)看也無(wú)疑證明了k8s的...
摘要:,托管于騰訊云容器平臺(tái)容器編排工具。適配我們目前的服務(wù)部署在騰訊云托管,節(jié)點(diǎn)使用核的網(wǎng)絡(luò)增強(qiáng)型機(jī)器,所有的后端服務(wù)都以部署,集群外部署高可用支持集群內(nèi)服務(wù)發(fā)現(xiàn),數(shù)據(jù)庫(kù)以為主,消息隊(duì)列采用。 距離2017年的見(jiàn)聞技術(shù)架構(gòu)調(diào)整接近2年,隨著業(yè)務(wù)線的發(fā)展,見(jiàn)聞技術(shù)部的項(xiàng)目數(shù)量、項(xiàng)目架構(gòu)類型、基礎(chǔ)設(shè)施規(guī)模、服務(wù)變更頻率都在不斷地增長(zhǎng),帶給SRE的挑戰(zhàn)是如何能更快地助力于開(kāi)發(fā)人員更快更穩(wěn)定地部署...
閱讀 1037·2021-10-19 11:42
閱讀 2991·2021-09-10 10:51
閱讀 697·2021-09-09 09:33
閱讀 1781·2021-09-01 10:43
閱讀 2784·2019-08-30 12:43
閱讀 3530·2019-08-30 11:24
閱讀 2136·2019-08-30 10:56
閱讀 2788·2019-08-29 11:00