25

我有一个在 Amazon 上运行的服务器实例云,使用它们的负载均衡器来分配流量。现在我正在寻找一种很好的方法来优雅地缩小网络,而不会导致浏览器端出现连接错误。

据我所知,从负载均衡器中删除实例的任何连接都会被粗暴地终止。

我想有一种方法在我的实例关闭前一分钟通知我的实例,或者让负载均衡器停止向垂死的实例发送流量,但不终止与它的现有连接。

我的应用程序是基于在 Ubuntu 上运行的 node.js。我还运行了一些特殊的软件,所以我不喜欢使用许多提供 node.js 托管的 PAAS。

感谢您的任何提示。

4

6 回答 6

17

我知道这是一个老问题,但应该注意的是亚马逊最近添加了对 的支持connection draining,这意味着当从负载均衡器中删除一个实例时,该实例将完成在实例从负载均衡器中删除之前正在进行的请求. 不会将新请求路由到已删除的实例。您还可以为这些请求提供超时,这意味着任何运行时间超过超时窗口的请求都将被终止。

要启用此行为,请转到Instances负载均衡器的选项卡并更改Connection Draining行为。

于 2014-04-09T09:46:55.650 回答
16

这个想法使用 ELB 的能力来检测不健康的节点并将其从池中删除,但它依赖于 ELB 的行为,如以下假设中预期的那样。这是我一直想为自己测试但还没有时间的东西。当我这样做时,我会更新答案。

流程概述

在需要关闭节点时,可以包装并运行以下逻辑。

  1. 阻止与 nodeX 的新 HTTP 连接,但继续允许现有连接
  2. 等待现有连接耗尽,方法是监视与应用程序的现有连接或允许“安全”时间。
  3. 直接使用 EC2 API 或抽象脚本启动 nodeX EC2 实例的关闭。

根据您的应用程序“安全”,对于某些应用程序可能无法确定。

需要测试的假设

我们知道 ELB会从它的池中删除不健康的实例,我希望这是优雅的,所以:

  1. 到最近关闭的端口的新连接将被优雅地重定向到池中的下一个节点
  2. 当一个节点被标记为坏时,与该节点的已建立连接不受影响。

可能的测试用例:

  • 在 ELB 上触发 HTTP 连接(例如,来自 curl 脚本)在脚本打开关闭节点 HTTP 端口之一期间记录结果。您需要进行试验以找到允许 ELB 始终确定状态更改的可接受时间量。
  • 保持一个长的 HTTP 会话(例如文件下载),同时阻止新的 HTTP 连接,长会话应该有望继续。

1. 如何阻止 HTTP 连接

在 nodeX 上使用本地防火墙来阻止新会话,但继续允许已建立的会话。

例如 IP 表:

iptables -A INPUT -j DROP -p tcp --syn --destination-port <web service port>
于 2011-10-11T10:20:55.957 回答
7

从 ELB 分配流量的推荐方法是在多个可用区中拥有相同数量的实例。例如:

电子负载均衡器

  • 实例 1 (us-east-a)
  • 实例 2 (us-east-a)
  • 实例 3 (us-east-b)
  • 实例 4 (us-east-b)

现在提供了两个感兴趣的 ELB API,它们允许您以编程方式(或通过控制面板)分离实例:

  1. 注销实例
  2. 禁用可用区(随后禁用该区域内的实例)

ELB 开发人员指南有一节描述了禁用可用区的影响。该部分中的一个注释特别有趣:

您的负载均衡器始终将流量分配到所有启用的可用区。如果在为负载均衡器禁用该可用区之前,该可用区中的所有实例都已注销或运行状况不佳,则发送到该可用区的所有请求都将失败,直到 DisableAvailabilityZonesForLoadBalancer 调用该可用区。

上述注释的有趣之处在于,它可能暗示如果您调用 DisableAvailabilityZonesForLoadBalancer,ELB 可能会立即开始仅向可用区域发送请求 - 在您对禁用的可用区域中的服务器执行维护时,可能会导致 0 停机体验。

上述“理论”需要亚马逊云工程师的详细测试或确认。

于 2011-11-03T07:47:02.520 回答
4

似乎这里已经有很多回复,其中一些有很好的建议。但我认为总的来说你的设计是有缺陷的。无论您设计的关闭程序多么完美,以确保在关闭服务器之前关闭客户端连接,您仍然容易受到攻击。

  1. 服务器可能会断电。
  2. 硬件故障导致服务器出现故障。
  3. 连接可能因网络问题而关闭。
  4. 客户失去互联网或无线网络。

我可以继续列出该列表,但我的观点是,不是为系统设计始终正常工作。设计它以处理故障。如果您设计的系统可以随时处理服务器断电,那么您就创建了一个非常强大的系统。这不是 ELB 的问题,而是您拥有的当前系统架构的问题。

于 2012-10-04T16:03:43.187 回答
2

现有答案中未讨论的一个警告是,ELB 还使用具有 60 秒 TTL 的 DNS 记录来平衡多个 ELB 节点(每个节点都附加一个或多个实例)之间的负载。

这意味着,如果您在两个不同的可用区中拥有实例,则您的 ELB 可能有两个 IP 地址,其 A 记录上的 TTL 为 60 秒。当您从此类可用区域中删除最终实例时,您的客户端“可能”仍会使用旧 IP 地址至少一分钟 - 有故障的 DNS 解析器可能表现得更糟。

另一次 ELB 使用多个 IP 并遇到同样的问题时,当您在单个可用区域中拥有非常大量的实例时,这对于一个 ELB 服务器来说太多了。在这种情况下,ELB 还将创建另一个服务器并将其 IP 添加到具有 60 秒 TTL 的 A 记录列表中。

于 2012-10-04T14:15:27.410 回答
2

我无法评论我的声誉低下的原因。这是我制作的一些片段,可能对那里的人非常有用。它利用 aws cli 工具检查实例何时耗尽连接。

您需要一个 ec2 实例,并在 ELB 后面提供 python 服务器。

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"
于 2014-12-23T10:46:50.293 回答