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

20

我有一个Web应用程序,运行在Amazon AWS弹性负载均衡器后面,连接了3个实例。该应用程序具有一个/refresh端点,用于重新加载参考数据。每周会多次更新数据,因此需要在新数据可用时运行它。

我一直在做的是为所有实例分配公共地址,并独立执行刷新操作(使用ec2-url/refresh)。我同意Michael在不同话题上的答案,ELB后面的EC2实例不应允许直接公共访问。现在我的问题是如何使elb-url/refresh的调用到达负载均衡器后面的所有实例?

如果我可以从多个实例收集HTTP响应,那就太好了。但现在我不介意先盲目地执行刷新。


如果您将公共 IP 地址添加到 EC2,则如何执行刷新调用? - error2007s
@error2007s 我的每个实例都有类似于 ec2-123-123-123-123.compute-1.amazonaws.com 的公共 DNS 名称,我必须独立地在它们每个实例上调用 /refresh - Click2Death
你每次在有新数据时都要手动执行这个操作吗? - error2007s
@error2007s 是的,每次有新数据进来我都得这样做。当我在ELB上调用/refresh时,只有一个服务器会得到更新。因此,我正在寻找一种可以为我完成所有繁琐工作的解决方案 :) - Click2Death
就这个问题而言,这里有一个关于同样主题的另一个问题 - Click2Death
8个回答

12

我解决这个问题的其中一种方法是:

  1. 将数据写入AWS S3 Bucket
  2. 触发AWS Lambda函数自动从S3写入
  3. 使用AWS SDK从Lambda函数中标识ELB附加的实例,例如使用Python中的Boto3或AWS Java SDK
  4. 从Lambda调用单个实例上的“/refresh”
  5. 确保在创建新实例(由于自动缩放或部署)时,它会在启动期间从S3存储桶获取数据
  6. 确保实例所在的私有子网允许来自Lambda所附加的子网的流量
  7. 确保实例所附加的安全组允许来自Lambda所附加的安全组的流量

该解决方案的关键优势包括:

  • 整个过程从数据写入S3开始完全自动化,
  • 避免由于自动缩放/部署导致的数据不一致,
  • 易于维护(您无需在任何地方硬编码实例IP地址),
  • 不必将实例暴露在VPC之外
  • 高度可用(AWS确保在S3写入时调用Lambda,您无需担心在实例中运行脚本并确保实例正常运行)

希望这对你有用。


这是一个好答案。我只想指出,在这种情况下,lambda 将需要在 VPC 内部,否则它将无法通过其私有 IP 访问实例。当您执行此操作时,请不要忘记允许 Lambda 安全组的出站流量。 - Kevin Heidt

7
尽管可能由于应用程序和情况的限制而无法实现,但值得注意的是,对于运行在AWS ELB后面的实例(特别是如果它们是AutoScalingGroup的一部分),最佳实践应用程序架构是确保这些实例不具有状态。
其思想是使您可以通过添加新实例进行扩展或通过删除实例进行缩小,而不会影响数据完整性或性能。
一种选择是更改应用程序,将参考数据重新加载的结果存储到离线数据存储中,例如缓存或数据库(例如Elasticache或RDS),而不是存储在内存中。
如果应用程序能够做到这一点,那么您只需要在单个服务器上点击“刷新”端点-它将重新加载参考数据,执行必要的分析和操作以有效地存储它,以适合应用程序的方式存储到数据存储中,然后所有实例都可以通过共享数据存储访问已刷新的数据。
尽管向数据存储添加往返延迟会增加延迟,但通常为了应用程序的一致性而值得-根据当前模型,如果一个服务器落后于其他服务器刷新参考数据,如果ELB没有使用粘性会话,则通过ELB的请求将返回不一致的数据,这取决于它们被分配给哪个服务器。

6
您不能通过负载均衡器发出这些请求,因此您需要打开实例的安全组,以允许来自ELB以外源的流量。但这并不意味着您需要向所有直接流量开放它。您可以在安全组中只列出特定IP地址,以允许来自您特定计算机的请求。
如果您不想将公共IP地址添加到这些服务器,则需要在VPC内的EC2实例上运行类似于curl命令的东西。在这种情况下,您只需要打开安全组,以允许来自存在于VPC中的某个服务器(或一组服务器)的流量。

啊...也许我会创建一个新的端点,无论哪台机器接收到请求都会运行一堆curl命令来访问原始的/refresh。它们已经在同一个VPC中了。感谢您的快速回答,@Mark B。 - Click2Death
1
请注意,您仍然需要打开安全组。如果您有一个EB服务器向所有其他EB服务器发送请求,则应在实例所属的安全组中添加入站规则,指定允许来自同一安全组中其他实例的入站流量。 - Mark B
是的,我知道你在说什么。在安全组中添加新规则并不是什么大问题。 - Click2Death
我正在考虑让所有需要进行“刷新”活动的应用程序订阅企业消息总线上的“刷新”主题。 - jkerak

5
我以不同的方式解决了它,没有在安全组中开启新的流量或诉诸于外部资源如S3。它具有灵活性,因为它将动态地通知通过ECS或ASG添加的实例。
ELB的目标组提供了定期健康检查功能,以确保其后面的实例处于活动状态。这是您的服务器响应的URL。端点可以包括最近配置的时间戳参数。TG中的每个服务器都会在配置的Interval阈值内接收到健康检查ping。如果ping的参数发生变化,则表示刷新。
URL可能看起来像: /is-alive?last-configuration=2019-08-27T23%3A50%3A23Z 上面我传递了一个UTC时间戳2019-08-27T23:50:23Z 接收请求的服务将检查内存状态是否至少与时间戳参数一样新。如果不是,则会刷新其状态并更新时间戳。下一个健康检查将导致无操作,因为已刷新状态。 实现注意事项 如果刷新状态所需的时间比间隔窗口或TG健康超时更长,则需要将其卸载到另一个线程中,以防止并发更新或完全服务中断,因为健康检查需要及时返回。否则,该节点将被视为离线。
如果您正在使用流量端口进行此操作,请确保通过使其无法猜测来保护URL。任何公开暴露的内容都可能遭受DoS攻击。

enter image description here


2
由于您使用了S3,您可以使用S3的ObjectCreated通知来自动化您的任务。 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/dev/NotificationHowTo.html https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-notification.html 您可以安装AWS CLI并编写一个简单的Bash脚本来监视ObjectCreated通知。启动一个Cron作业,以查找S3通知的新对象创建。
在该脚本文件中设置一个条件,以便在检测到S3中创建新对象时,curl“http:// 127.0.0.1 / refresh”将会被执行,这样您就不必每次手动执行了。

数据包每个约为500MB,定期重新加载成本相当昂贵。更加复杂的是,“/refresh”需要一个参数来指定要加载哪个数据包,而且它们都非常大。 - Click2Death
你如何检测到有新的数据可用? - error2007s
@error2007s,另一个团队负责创建这些文件并将其放入S3。 - Click2Death
@error2007s 谢谢,我也会研究 S3 触发器。 - Click2Death
我有点困惑于如何编写一个bash脚本来监控推送通知?这是否假定有一个SQS队列或其他接收推送通知的东西?这个bash脚本具体轮询什么? - Kevin Heidt

1

我个人喜欢@redoc的回答,但想为有兴趣的人提供另一种选择,即他的回答与被接受的答案的结合。使用SEE对象创建事件,您可以触发一个lambda,但不是发现实例并调用它们,而是让lambda使用SSM(也称为Systems Manager)通过针对标签的EC2实例上的powershell或bash文档执行命令。然后,该文档将调用127.0.0.1/reload,就像被接受的答案一样。这样做的好处是您的lambda不必在vpc中,您的EC2不需要入站规则来允许来自lambda的流量。缺点是它需要实例安装SSM代理,听起来比实际工作要多。已经使用SSM代理内容优化的AWS AMI,但在用户数据中自己安装它非常简单。根据您的用例,另一个潜在的缺点是它使用指数级的同时执行增加,这意味着如果您针对20个实例,则运行一个1,然后同时运行2,然后4,然后8,直到所有实例完成,或达到您设置的最大值。这是因为它内置了错误恢复功能。如果出现问题,它不想摧毁您的所有东西,就像慢慢将您的重量放在一些冰上一样。

0

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

另一种选择是,如果您的EC2实例相当稳定,您可以为每个EC2实例创建一个目标组,然后在负载均衡器上创建一个侦听器规则,以基于某些条件(例如查询参数、URL或标头)针对这些单个实例组。


0
这是我们解决这个问题的方法:
  • 这个解决方案不仅适用于AWS,还可以移植到其他云平台。
  • 当实例启动时,会启动一个单独的线程来处理这种状态变化。
  • 这个线程的功能是:每隔n秒,使用If-modified-since头部进行S3的获取调用。If-modified-since头部有助于在文件未修改的情况下跳过更新。每当需要更新数据时,外部工具/用户会更新S3文件。
  • 如果文件被修改,该线程会加载新的数据。
  • 需要注意的是,在这n秒内,所有的实例都将使用不同的数据。因此,只能在不关注这种差异的情况下使用这种方法(最终一致性)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接