我最近在工作中做一個設置,我有一個面向用戶的 Nginx 服務,它將訪問轉發到運行在AWS Elastic Load Balancer (如你所知. ELB)上的一個服務。這本身似乎不是一個困難的任務,你只需要找到 ELB 的主機名,將 ngin x指向它,這樣不就搞定了,對吧?
location / { proxy_pass http://service-1234567890.us-east-1.elb.amazonaws.com; }
測試沒有問題,再正確設置一下防火牆/安全組配置,它就應該可以很好的工作了。幾個小時之後,你可能會發現,服務不再工作了,盡管沒有做任何改變。直接訪問 ELB 端點是可以工作的,但訪問 Nginx 卻總是超時提醒。
為了弄清楚為什麼服務突然中止,需要先了解一下 ELB 是如何工作的:
當你創建一個彈性負載均衡(Elastic Load Balancer),你將會得到 DNS 的返回記錄,AWS 會告訴你所有在使用的訪問服務。DNS 記錄是一個輪詢 DNS(round robin DNS)記錄,它指向兩個或更多的 IP 地址——這取決於你有多少可用的區域。DNS 記錄被設置成 60 秒的存活時間(time to live),這意味幾乎不會有記錄緩存。
短 TTL 可以讓 AWS 快速改變機器的運行負載,在不中斷服務的情況下,不會有任何復雜的虛擬 IP 問題。這也是他們特別告訴你不要查找主機名和發送流量到其中某個 IP 地址的原因,那樣的話,你的服務可能會在未來某個未定義的時間,IP 地址可能會停止為負載均衡工作。
問題在於,對於 Nginx 來說,當它讀取到一個配置時,它就會立刻向 DNS 請求主機名,然後使用其結果,直到下次重新加載配置。在這段時間到來之前,ELB 可能改變 IP 地址,讓你的 Nginx 把請求轉發到一些不為你服務的地址。
解決這個問題的方式是為 Nginx Plus 付費,它添加 resolve 標記對在 upstream 分組上的服務器進行指示。那就是讓 Nginx 驕傲的 DNS 對 TTL 的記錄,偶爾按順序重新處理記錄,並取得服務器使用的更新列表。
為這個功能花費每年每服務器 $1.500,看起來花費很多。當然這是你希望得到 Nginx Plus 帶來的其他功能,如果你不需要它們,這將會是一個昂貴的升級。
一個更加實惠的選擇是寫這樣一個配置:
resolver 172.16.0.23; set $upstream_endpoint http://service-1234567890.us-east-1.elb.amazonaws.com; location / { proxy_pass $upstream_endpoint; }
它將會生效並且 Nginx 會遵循記錄 DNS 記錄的 TTL,萬一一個請求進來,會重新解釋它而且緩存的記錄會過期。為什麼會這樣?
答案可以在 proxy_pass 指令文檔結尾找到,它聲明了:
服務器名,端口以及傳遞的URI也可以使用變量被指定:
proxy_pass http://$host$uri;甚至像這樣:
proxy_pass $request;在這個案例中,服務器名會在所描述的 server groups 中被查找,如果沒找到,會使用 resolver 來決定.
當我們給 proxy_pass 提供一個變量的時候,我們基本上是利用其改變行為,但這樣確實需要我們在配置中指定一個 DNS resolver。例子裡邊用到的 DNS resolver應該能夠在 AWS 上面跑在默認 VPC或者 EC2 中的所有服務器工作(適用)。你也可以隨時查看 /etc/resolv.conf 找出哪些 AWS 為你的服務器提供並使用了哪些 DNS 服務器。
如果你在 Nginx 中設置的 Location 不只是 /,那麼你需要注意到當給定一個變量作為參數時,proxy_pass 細微的改變行為。
先說重要的,快速概括 proxy_pass 如何在正常在操作中工作:
正常的表現行為
設想我們有一個 Nginx 配置包括這些:
location /foo/ { proxy_pass http://127.0.0.1:8080; }
當我們發送一個 /foo/bar/baz 的請求到這個站點,Nginx 會轉發請求到 http://127.0.0.1:8000/foo/bar/baz。
location /foo/ { # Note the trailing slash ↓ proxy_pass http://127.0.0.1:8080/; }
Nginx 會在 Location 記錄裡邊去掉部分指定的 URI,然後把剩下的部分傳給 upstream 服務器。所以請求 /foo/bar/baz 會被轉發到 http://127.0.0.1:8080/bar/baz。
改變行為
當我們使用一個變量作為 proxy_pass 的參數的時候,上面帶有尾部斜槓的行為會改變。例如我們有這樣的配置。
resolver 172.16.0.23; set $upstream_endpoint http://service-1234567890.us-east-1.elb.amazonaws.com/; location /foo/ { proxy_pass $upstream_endpoint; }
當我們向那個配置發送請求 /foo/bar/baz,轉發請求將不會去到/並且不是預想中的 /bar/baz。
為此解決方案就是從 upstream 的 endpoint 去掉尾部斜槓,然後像這樣手動重寫:
resolver 172.16.0.23; set $upstream_endpoint http://service-1234567890.us-east-1.elb.amazonaws.com; location /foo/ { rewrite ^/foo/(.*) /$1 break; proxy_pass $upstream_endpoint; }
然後當你發送請求 /foo/bar/baz,upstream 會接受到我們想要的請求 /bar/baz。
要知道這不單單只適用於設置用 elb 做 upstream 服務器,它適用於配置所有在 nginx 做 upstream 服務器的修改 DNS 配置的情況。
希望這對你有用,如果你有任何建議或者只是想單純聯系我,用 twitter 聯系吧 Tenzer。
碼農必須要加班?NO!
知道碼農們都想擺脫加班狗、外賣臉的稱號,所以我們來了!
我們做了一個能讓程序員之間共享知識技能的APP,覺得可以顛覆程序員的工作方
式!
有人說我們癡心妄想,但我們不那麼認為。
為了能煽爛說我們癡心妄想的人的臉,現在我們急需程序員業內的牛哔-人物來給
我們“號脈”!“診斷費”豐厚!畢竟我們不差錢兒,只是想做到最好!
圈圈字典中講到,牛哔-人物是指群成員數高於1000人的QQ群主或關注人數高於
2000人的貼吧吧主或粉絲人數高於10000人的微博博主或成員數高於2000主題貼的版主
或單帖閱讀量高於2000博客主或人脈超級廣的圈內紅人。
對於未能達標的未來大神們,我們只能含淚表示:蜀黍,咱們來日方長,這次暫
時不約好嗎?待他日你立地成神,我必生死相依!
來?還是不來?
圈圈互動 接頭暗號:1955246408 (QQ)