Nginx 動態 upstreams 實現

jopen 9年前發布 | 9K 次閱讀 Nginx Web服務器

我最近在工作中做一個設置,我有一個面向用戶的 Nginx 服務,它將訪問轉發到運行在AWS Elastic Load Balancer (如你所知. ELB)上的一個服務。這本身似乎不是一個困難的任務,你只需要找到 ELB 的主機名,將 ngin x指向它,這樣不就搞定了,對吧?

location / {
    proxy_pass http://service-1234567890.us-east-1.elb.amazonaws.com;
}

測試沒有問題,再正確設置一下防火墻/安全組配置,它就應該可以很好的工作了。幾個小時之后,你可能會發現,服務不再工作了,盡管沒有做任何改變。直接訪問 ELB 端點是可以工作的,但訪問 Nginx 卻總是超時提醒。

Nginx 動態 upstreams 實現

ELB端啟蒙

為了弄清楚為什么服務突然中止,需要先了解一下 ELB 是如何工作的:

當你創建一個彈性負載均衡(Elastic Load Balancer),你將會得到 DNS 的返回記錄,AWS 會告訴你所有在使用的訪問服務。DNS 記錄是一個輪詢 DNS(round robin DNS)記錄,它指向兩個或更多的 IP 地址——這取決于你有多少可用的區域。DNS 記錄被設置成 60 秒的存活時間(time to live),這意味幾乎不會有記錄緩存。

短 TTL 可以讓 AWS 快速改變機器的運行負載,在不中斷服務的情況下,不會有任何復雜的虛擬 IP 問題。這也是他們特別告訴你不要查找主機名和發送流量到其中某個 IP 地址的原因,那樣的話,你的服務可能會在未來某個未定義的時間,IP 地址可能會停止為負載均衡工作。

回到 Nginx

問題在于,對于 Nginx 來說,當它讀取到一個配置時,它就會立刻向 DNS 請求主機名,然后使用其結果,直到下次重新加載配置。在這段時間到來之前,ELB 可能改變 IP 地址,讓你的 Nginx 把請求轉發到一些不為你服務的地址。

Nginx Plus

解決這個問題的方式是為 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 服務器。

關于轉發 URI 的 Caveat(警告)

如果你在 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 配置的情況。

希望這對你有用,如果你有任何建議或者只是想單純聯系我,用 推ter 聯系吧 Tenzer

來源:oschina

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!