2011-10-05 39 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 - 我认为这包含更好的方法,应该更快。据我了解,上述方法可能会导致自动调节组创建一个新的节点,因为您取消注册一个更新... –