如何使 HTTP 调用到达亚马逊 AWS 负载均衡器后面的所有实例?

How to make a HTTP call reaching all instances behind amazon AWS load balancer?

我有一个 Web 应用程序,它 运行 落后于 Amazon AWS Elastic Load Balancer,附加了 3 个实例。该应用程序有一个 /refresh 端点来重新加载参考数据。只要有新数据可用,它就需要 运行,每周发生几次。

我一直在做的是为所有实例分配 public 地址,并独立刷新(使用 ec2-url/refresh)。我同意 Michael's answer on a different topic,ELB 后面的 EC2 实例不应允许直接 public 访问。现在我的问题是如何使 elb-url/refresh 调用到达负载均衡器后面的所有实例?

如果我可以从多个实例收集 HTTP 响应,那就太好了。但我暂时不介意盲目刷新。

您不能通过负载均衡器发出这些请求,因此您必须打开实例的安全组以允许来自 ELB 以外的来源的传入流量。不过,这并不意味着您需要向所有直接流量开放它。您可以简单地将安全组中的 IP 地址列入白名单,以允许来自您特定计算机的请求。

如果您不想向这些服务器添加 public IP 地址,那么您将需要 运行 在 VPC 内的 EC2 实例上执行类似 curl 命令的操作。在这种情况下,您只需要打开安全组以允许来自 VPC 中存在的某些服务器(或服务器组)的流量。

当您使用 S3 时,您可以使用 S3 的 ObjectCreated 通知自动执行您的任务。

https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html

https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-notification.html

您可以安装 AWS CLI 并编写一个简单的 Bash 脚本来监控该 ObjectCreated 通知。启动一个 Cron 作业,该作业将查找用于创建新对象的 S3 通知。

在该脚本文件中设置条件以卷曲 "http: //127.0.0.1/refresh" 当脚本文件检测到在 S3 中创建的新对象时,它将卷曲 127.0.0.1/refresh 并且您不必每次都手动执行此操作时间.

虽然鉴于您的应用程序和环境的限制,这可能是不可能的,但值得注意的是,AWS ELB 后面的实例 运行 的最佳实践应用程序架构(特别是如果它们是 AutoScalingGroup 的一部分)是确保实例不是 有状态的.

我们的想法是让您可以通过添加新实例进行扩展,或通过删除实例进行扩展,而不会影响数据完整性或性能。

一个选择是更改应用程序以将参考数据重新加载的结果存储到实例外数据存储中,例如缓存或数据库(例如 Elasticache 或 RDS),而不是内存中。

如果应用程序能够做到这一点,那么您只需要点击单个服务器上的 refresh 端点 - 它会重新加载参考数据,执行存储它所需的任何分析和操作以适合应用程序的目的的方式有效地将其存储到数据存储中,然后所有实例都可以通过共享数据存储访问刷新的数据。

虽然增加到数据存储的往返会增加延迟,但为了应用程序的一致性,这通常是值得的 - 在您当前的模型下,如果一台服务器在刷新引用方面落后于其他服务器数据,如果 ELB 没有使用粘性会话,通过 ELB 的请求将 return 不一致的数据,具体取决于它们分配给哪个服务器。

我解决这个问题的方法之一是

  1. 将数据写入 AWS s3 存储桶
  2. triggering a AWS Lambda function automatically from the s3 write
  3. 使用 AWS SDK 从 Lambda 函数中识别附加到 ELB 的实例,例如使用 boto3 from python 或 AWS Java SDK
  4. 从 Lambda 对单个实例调用 /refresh
  5. 确保在创建新实例时(由于自动缩放或部署),它会在启动期间从 s3 存储桶中获取数据
  6. 确保实例所在的私有子网允许来自附加到 Lambda 的子网的流量
  7. 确保附加到实例的安全组允许来自附加到 Lambda 的安全组的流量

此解决方案的主要优势是

  • 从数据写入 s3 的那一刻起,该过程就完全自动化了,
  • 避免了由于autoscaling/deployment,
  • 造成的数据不一致
  • 易于维护(您不必在任何地方硬编码实例 ip 地址),
  • 您不必在 VPC 之外公开实例
  • 高可用(AWS 确保在 s3 写入时调用 Lambda,您不必担心 运行 实例中的脚本并确保实例已启动 运行)

希望这有用。

我个人喜欢@redoc 的回答,但想为任何感兴趣的人提供另一种选择,这是他的答案和已接受的答案的组合。使用 SEE 对象创建事件,您可以触发 lambda,而不是发现实例并调用它们,这需要 lambda 在 vpc 中,您可以让 lambda 使用 SSM(又名系统管理器)通过 powershell 执行命令或 bash 关于通过标签定位的 EC2 实例的文档。然后,该文档将像接受的答案一样调用 127.0.0.1/reload。这样做的好处是您的 lambda 不必位于 vpc 中,并且您的 EC2 不需要入站规则来允许来自 lambda 的流量。缺点是它需要实例安装 SSM 代理,这听起来比实际要多。 AWS AMI 已经使用 SSM 代理进行了优化,但您自己将其安装在用户数据中非常简单。另一个潜在的缺点,取决于您的用例,是它使用指数上升来同时执行,这意味着如果您的目标是 20 个实例,它会运行一个 1,然后一次运行 2,然后一次运行 4,然后 8,直到它们全部完成,或者达到您为最大值设置的值。这是因为它内置了错误恢复功能。如果出现问题,它不想毁掉你所有的东西,比如慢慢地把你的体重放在冰上。

我用不同的方式解决了它,没有在安全组中开辟新的流量,也没有求助于 S3 等外部资源。它的灵活性在于它会动态通知通过 ECS 或 ASG 添加的实例。

ELB 的目标组提供定期健康检查功能,以确保其背后的实例处于活动状态。这是您的服务器响应的 URL。端点可以包括最近配置的时间戳参数。 TG 中的每台服务器都将在配置的 Interval 阈值内收到健康检查 ping。如果 ping 的参数发生变化,则表示刷新。

A URL 可能看起来像: /is-alive?last-configuration=2019-08-27T23%3A50%3A23Z

上面我传递了 2019-08-27T23:50:23Z

的 UTC 时间戳

接收请求的服务将检查内存中的状态是否至少与时间戳参数一样新。如果不是,它将刷新其状态并更新时间戳。由于您的状态已刷新,下一次健康检查将导致无操作。

实施说明

如果刷新状态可能比间隔 window 或 TG 健康超时花费更多时间,您需要将其卸载到另一个线程以防止并发更新或直接服务中断,因为健康检查需要return 及时。否则该节点将被视为离线。

如果您为此目的使用流量端口,请确保 URL 安全,使其无法被猜测。任何公开暴露的内容都可能受到 DoS 攻击。

您可以快速连续多次调用以调用负载均衡器后面的所有实例。这是可行的,因为 AWS 负载均衡器默认使用没有粘性会话的循环,这意味着负载均衡器处理的每个调用都被分派到可用实例列表中的下一个 EC2 实例。因此,如果您进行快速调用,您很可能会命中所有实例。

另一种选择是,如果您的 EC2 实例相当稳定,您可以为每个 EC2 实例创建一个目标组,然后在您的负载均衡器上创建一个侦听器规则,以根据某些条件将这些单个实例组作为目标,例如作为查询参数,URL 或 header.