2011-10-05 40 views
23

我有一羣運行在亞馬遜的服務器實例使用其負載平衡器來分配流量。現在我正在尋找一種良好的方式來平滑網絡,而不會導致瀏覽器端的連接錯誤。如何正常關閉或從ELB組中刪除AWS實例

據我所知,從負載平衡器中移除實例的任何連接都會被粗暴終止。

我想有一種方法來通知我的實例,例如在關閉它之前一分鐘,或讓負載平衡器停止向正在死亡的實例發送流量,但不終止與它的現有連接。

我的應用程序是基於node.js在Ubuntu上運行的。我也有一些特殊的軟件在運行,所以我不想使用許多PAAS提供的node.js託管。

感謝您的任何提示。

+0

您是否在使用ELB維護僅在特定EC2實例上有效的用戶會話?如果是這樣,這些會議持續多久? –

+0

我不使用ELB進行用戶會話管理 - 也許我會這樣做僅出於性能原因,但我不依賴此功能。會話管理由所有節點都可以訪問的中央數據庫完成。 –

+6

下面是關於ELB在刪除實例時粗暴丟棄實時連接的主題:https://forums.aws.amazon.com/thread.jspa?threadID=61278亞馬遜請求反饋意見,所以請隨時添加+1以修復這個。 –

回答

16

這個想法使用ELB的能力來檢測一個不健康的節點並將它從池中刪除,但它依賴於ELB在下面的假設中按預期行事。這是我一直想要爲自己測試的東西,但還沒有時間。當我這樣做時,我會更新答案。

過程概述

下面的邏輯可以被包裝並在節點需要被關閉的時間運行。

  1. 座新的HTTP連接到節點X,但繼續讓現有的連接
  2. 等待現有連接,或者通過監測到應用程序的現有連接或允許的時間「安全」量流失。
  3. 直接使用EC2 API或Abstracted腳本在nodeX EC2實例上啓動關閉。

根據您的應用程序「安全」,這可能無法確定某些應用程序。需要

假設進行測試

我們知道,ELB removes unhealthy instances from it's pool我希望這是優雅的,因此:

  1. 到最近關閉的端口一個新的連接將會優雅地重定向到池中的下一個節點
  2. 當某個節點標記爲Bad時,已建立的與該節點的連接不受影響。

可能的測試的情況:在ELB

  • 消防HTTP連接(例如,從捲曲腳本)中記錄所述 結果腳本化打開節點 HTTP端口中的一個的閉合。您需要試驗才能找到允許ELB始終確定狀態 更改的可接受時間。
  • 保持一個很長的HTTP會話(例如,文件下載),同時阻止新的HTTP連接,長期會議應該有望繼續。

1.如何阻止HTTP連接

使用本地防火牆節點X以阻止新的會話,但繼續允許建立的會話。

例如IP表:

iptables -A INPUT -j DROP -p tcp --syn --destination-port <web service port> 
+0

感謝您的想法!不幸的是,第2號假設似乎是失蹤的重要事件。據我所知,一個節點在被檢測爲病態後約40-60秒存在,沒有保證。但令人遺憾的是,它已經被立即刪除,沒有ELB的任何警告,並且任何現有的連接被終止並且不被轉發到另一個節點。這就是我所知道的,但我可以試着用它來試驗它...... –

+0

它很好,它可以檢測到它並刪除它,這就是我們想要的。但是,刪除現有的連接肯定會給我們帶來麻煩,我不會在沒有測試的情況下排除這一點,因爲我已經看到其他負載平衡軟件以這種方式工作......否則,您是否可以使用負載平衡器的子域它只建立初始連接?例如。 balance.domain.com轉向nodeX.domain.com?其中nodeX是循環池中的下一個。 –

+0

ELB本身不支持使用子域 - 但機器可以知道它自己的名稱。我甚至可以通過dns條目將一組機器映射到域名 - 不知道如何自動完成。由於我將大部分資金支付給正在運行的實例,並且暫停的實例相當便宜,因此這可能是一種選擇。所以我會使用ELB進行初始分配,從那時起可能會使用用戶分配給的節點。這可能有用!有關如何最好地使用子域而不是AWS機器網址的想法? (我想爲一個域使用wildcard-ssl)。 –

7

從您的ELB分配流量的推薦方法是具有跨多個可用區的實例數量相等。例如:

ELB

  • 實例1(US-東-a)的
  • 實例2(US-東-a)的
  • 實例3(US-東-b)的
  • 實例4(us-east-b)

現在有兩個感興趣的ELB API可以讓您以編程方式(或通過控制面板)分離實例:

  1. 註銷一個實例
  2. 禁用的可用性區域(其隨後禁用區域內的情況下)

ELB Developer Guide都有一個描述禁用的可用性區域的效果的部分。該部分的註釋特別引人注目:

您的負載均衡器始終會將流量分配給所有啓用的可用區域 可用區域。如果負載均衡器的可用區域被禁用 之前,可用區域中的所有實例都是 取消註冊或不健康,則發送到該可用區域 的所有請求都將失敗,直到DisableAvailabilityZonesForLoadBalancer調用該可用區域爲止。

請告訴我有趣的上述值得注意的是,這可能意味着,如果你調用DisableAvailabilityZonesForLoadBalancer中,ELB可以立刻開始發送只可用區域請求 - 這可能導致一個0停機的經驗,而你在服務器上執行維護禁用的可用區域。

上述'理論'需要亞馬遜雲工程師的詳細測試或確認。

+0

聽起來很有希望,我沒有想到!我一定會檢查一下!謝謝。 –

1

在現有答案中沒有討論的一個警告是,ELB還使用60秒TTL的DNS記錄來平衡多個ELB節點(每個節點都有一個或多個附加到它的實例)之間的負載。

這意味着如果您的實例位於兩個不同的可用區域中,那麼您的ELB可能有兩個IP地址,並且它們的A記錄上有60秒的TTL。當您從這樣的可用區域刪除最終實例時,您的客戶端「可能」仍舊使用舊的IP地址至少一分鐘 - 錯誤的DNS解析器可能會表現得更糟。

另一次,ELB使用多個IP並且存在相同的問題,那就是在單個可用區中,您有大量實例對於一個ELB服務器來說太多了。在這種情況下,ELB還將創建另一臺服務器,並將其IP添加到60秒TTL的A記錄列表中。

+0

根據我的理解,所述合同是由ELB將轉發(由於過時的DNS)轉換爲無AZ健康實例的流量,然後轉發給確實具有健康實例的AZ。您可以通過在不同的AZ中設置2個實例來進行測試,關閉一個實例,然後強制流量到達關閉AZ的ELB IP並查看它是否仍然能夠提供健康的響應。 –

4

似乎這裏已經有很多回復,其中一些有很好的建議。但我認爲一般來說你的設計是有缺陷的。無論您如何設計關機程序以確保客戶端連接在關閉服務器之前關閉,仍然存在漏洞。

  1. 服務器可能會失去電源。
  2. 硬件故障導致服務器出現故障。
  3. 連接可能因網絡問題而關閉。
  4. 客戶端丟失了互聯網或wifi。

我可以繼續列表,但我的觀點是,而不是設計系統始終正常工作。設計它來處理故障。如果您設計的系統可以隨時處理服務器失去電源的情況,那麼您已經創建了一個非常強大的系統。這對於ELB來說並不是問題,這是您現有系統架構的一個問題。

+2

你是對的,有很多可能導致連接瞬間丟失的場景,但我認爲這是一個學位問題。自動縮放設計是常見的;實例是按小時計費的,所以你可能每小時都會放大或縮小......這是很多失去聯繫的東西。 – Stephen

15

我知道這是一個老問題,但應該注意的是,亞馬遜最近增加了對connection draining的支持,這意味着當一個實例從負載均衡器中移除時,該實例將完成在該實例之前正在進行的請求已從負載均衡器中移除。沒有新的請求將被路由到已刪除的實例。您還可以爲這些請求提供超時,這意味着任何運行時間超過超時窗口的請求都將終止。

要啓用此行爲,請轉至負載均衡器的Instances選項卡,然後更改Connection Draining行爲。

2

我不能評論我低信譽的原因。以下是我製作的一些片段,可能對那裏的人非常有用。它利用aws cli工具來檢查一個實例何時連接中斷。

您需要在ELB後面提供python服務器的ec2實例。

from flask import Flask 
import time 

app = Flask(__name__) 

@app.route("/") 
def index(): 
    return "ok\n" 

@app.route("/wait/<int:secs>") 
def wait(secs): 
    time.sleep(secs) 
    return str(secs) + "\n" 

if __name__ == "__main__": 
    app.run(
     host='0.0.0.0', 
     debug=True) 

然後從本地工作站向ELB運行以下腳本。

#!/bin/bash 

which jq >> /dev/null || { 
    echo "Get jq from http://stedolan.github.com/jq" 
} 

# Fill in following vars 
lbname="ELBNAME" 
lburl="http://ELBURL.REGION.elb.amazonaws.com/wait/30" 
instanceid="i-XXXXXXX" 

getState() { 
    aws elb describe-instance-health \ 
     --load-balancer-name $lbname \ 
     --instance $instanceid | jq '.InstanceStates[0].State' -r 
} 

register() { 
    aws elb register-instances-with-load-balancer \ 
     --load-balancer-name $lbname \ 
     --instance $instanceid | jq . 
} 

deregister() { 
    aws elb deregister-instances-from-load-balancer \ 
     --load-balancer-name $lbname \ 
     --instance $instanceid | jq . 
} 

waitUntil() { 
    echo -n "Wait until state is $1" 
    while [ "$(getState)" != "$1" ]; do 
     echo -n "." 
     sleep 1 
    done 
    echo 
} 

# Actual Dance 
# Make sure instance is registered. Check latency until node is deregistered 

if [ "$(getState)" == "OutOfService" ]; then 
    register >> /dev/null 
fi 

waitUntil "InService" 

curl $lburl & 
sleep 1 

deregister >> /dev/null 

waitUntil "OutOfService" 
+0

請參閱http://docs.aws.amazon.com/autoscaling/latest/userguide/as-enter-exit-standby.html#standby-instance-health-status - 我認爲這包含更好的方法,應該更快。據我瞭解,上述方法可能會導致自動調節組創建一個新的節點,因爲您取消註冊一個更新... –