我已经使用Amazon的vpc创建了一个中等复杂的网络。这是一个跨越两个可用性区域的三层网络。每个层在区域a和区域b中都有一个子网。表示层位于顶层,中间是应用程序层,底层是核心层。
子网的所有安全组和ACL当前都允许所有入站和出站流量帮助我减少问题的表面积。
表示层的路由表将所有流量都指向Internet网关。 NAT网关位于单独的子网中,该子网也将所有流量都指向Internet网关。它们被部署为docker映像。每个模块前面都有一个经典的负载均衡器。
UI-ELB面向Internet,位于表示层中,将流量从80/443路由到端口8080,并与放置在应用程序层子网中的我的app-ec2关联。
我的API前面有一个内部负载平衡器。 API-ELB在应用程序层中(与app-ec2在同一子网中),并在端口80/443上获取流量,并将其路由到端口3000核心中的api-ec2。 br />两个负载均衡器都在将流量传递到其实例之前卸载证书。 /app.website.com)。每个负载平衡器都会通过已定义的运行状况检查并报告所有正在使用的ec2实例。
最后,在API上,我使用cors nodejs软件包启用了cors。我的网络的肮脏图。
问题:
APP-ELB成功将我路由到该应用程序。但是,当应用尝试将GET请求发送到API-ELB时,它首先发送一个OPTIONS请求,该请求的错误代码为408。
它变得很奇怪的地方
调试时遇到的一些最奇怪的事情是:
我可以SSH进入app-ec2实例,并且可以对API-ELB运行成功的curl。我已经尝试了很多,它们都起作用。一些示例是:
curl -L https://api.website.com/system/healthcheck
和curl -L -X OPTIONS https://api.website.com/system/healthcheck
。它总是返回所需的信息。我已将整个应用程序从网络中移出到公共默认vpc中,并且按预期方式工作。
我让api-ec2将所有网络请求写入控制台。虽然显示运行状况检查请求,但不显示来自app-ec2的任何请求。这使我相信访问量甚至没有到达api。
真正让我完全不知所措的最大事情是,卷曲内部api elb是有效的,但是axios要求使用相同的确切网址才不是。这对我来说根本没有意义。
我尝试过的事情
我最初花了很多时间与ACL规则和安全小组一起玩,认为我做了一些事情错误。最终,我只是说了一下,“拧紧了”,然后打开一切,尝试将其排除在外。
我花了很多时间在api上玩Cors。最终登陆我现在拥有的配置,这是cors节点包提供的默认
app.use(cors())
回调。我还提供了文档中建议的app.options('*', cors())
。但似乎找不到任何东西。另外,当我将应用程序移出网络时,它运行良好。我确定我已经尝试了许多其他方法,但是这些似乎是最相关的。我想念什么?我意识到这可能是一个非常模糊和广泛的问题,而且是大量的文章,但是我非常感谢您的宝贵见解以及您在阅读中所花费的时间!
#1 楼
因此,您实际上是这样的:由于您的API ELB位于私有区域中,因此无法从Internet访问它。在React.js中,它只运行在用户的浏览器中,而不是在UI服务器上运行,这些服务器仅提供静态文件。
您有两个选择,配置前端服务器以将API调用重定向到API ELB或将API ELB更新为面向互联网。
JavaScript应用程序的常见陷阱是忘记它们在用户的浏览器中运行,而不是像JEE应用程序那样在前端服务器上运行。
#2 楼
这听起来像是非对称或n路径路由问题。这可能是正在发生的情况:IP地址192.168.1.1的机器A通过192.168.1.10的LB发起[SYN]请求。然后LB将有效负载代理到192.168.1.2的计算机B,因此有效负载现在具有源:192.168.1.1和目的地:192.168.1.2(以前为192.168.1.10)。
那么,当192.168.1.2用[SYN,ACK]响应时,现在会发生什么?应该发生的是,机器B应该通过负载平衡器对机器A做出响应-通常是由于服务器上通过LB路由流量的默认路由或网关。但是,在这种情况下,计算机位于同一子网中,因此不使用路由/网关,服务器将忽略路由表。这意味着当服务器响应时,机器A的[SYN,ACK]似乎来自与机器A发起请求的IP不同的IP-期望源IP为192.168.1.10(LB),但是正在看到来自192.168.1.2(计算机B)的[SYN,ACK],因此在这种情况下LB无法与计算机B建立连接,因为响应发往了错误的设备。 >之所以对外部流量有效,是因为您使用了默认路由-对其他所有人的响应都通过ELB路由。 ELB看到它正在发起连接,并自动拦截了响应并将192.168.1.2的源交换回192.168.1.10。
因此,对于该问题的一种解决方案,您可以实现武装负载平衡(也称为棍子上的负载平衡器)。这样做是在负载均衡器的内部接口上使用源NAT(因此,假设您在负载均衡器上具有外部接口192.168.1.10,在内部接口上具有192.168.1.11)。从机器B的角度来看,这将使所有流量似乎都来自192.168.1.11,这应该可以解决您的连接问题。
但是,您的AWS ELB似乎不支持SNAT,因此您要么需要将主机和ELB放在不同的子网中,要么使用支持SNAT的功能,例如按小时或BYOL形式提供的F5虚拟版。不过请注意SNATing的连接限制-如果您需要大约30k以上的并发连接,则会遇到SNAT端口耗尽的问题,并且需要开始使用SNAT池。防止将来出现问题)将是确保客户端和服务器位于不同的子网中。
确认的最佳方法是在连接主机和/或后端服务器上使用tcpdump并查找响应直接来自后端服务器/来自后端服务器,而不是通过负载平衡器。然后,您可以将转储文件加载到WireShark中,以弄清楚到底发生了什么。
评论
ELB不转发数据包。它建立新的TCP连接并转发有效负载。路由不对称是问题不可能解决的一件事。
– Michael-sqlbot
17年5月5日19:02
F5也是如此,它们仍然遭受非对称路由问题的困扰。即使具有完整的代理体系结构和单独的新TCP连接,默认情况下,F5负载平衡器仍将连接客户端的源地址,因此问题仍然完全如上所述发生。我假设ELB以类似的方式工作。我知道A10的行为确实相同。
–詹姆斯·谢威(James Shewey)
17-6-5在19:13
他们没有,ELB具有单独的IP。的
–机器人
17年6月6日在9:58
AWS ELB不仅可以充当TCP负载平衡器,还可以充当反向代理。正如OP所说,ELB正在进行SSL卸载,它不能是TCP平衡器,而必须是HTTP反向代理。您的答案不适用于上下文,并且ELB永远不会用于传出数据包(它们根本不是路由器)。此外,如果您尝试使用具有2个接口的F5代理并在每个接口上设置相同的子网,则确实确实会造成问题,使用SNAT解决问题只是一个不好的解决方案。
–滕西拜
17年6月6日在15:16
好了,这里从您的示例中看到的机器B是ELB IP,客户端IP将在X-Forwarded-Port标头中。客户端可以放在服务器旁边,这不会有问题。在HTTP模式下,ELB不能用作带有SSL终止的F5。 (即使在TCP模式下,它仍然像负载平衡器一样是nginx,完全没有远程可比性)。我认为您一直在“代理”,我们实际上是在谈论代理数据包而不是转发数据包。如果需要,我可以在与2台计算机相同的子网中向您发送ELB的tcpdump,它可以正常工作。
–滕西拜
17年6月6日在15:51
评论
那就是每个区域两个子网:表示层,应用程序层和核心层。三点你能澄清一下吗?我有表示层的路由表,并且NAT驱动器通过Internet网关路由所有流量。我既有表示层又有核心层,它们通过nat驱动器路由所有流量。这似乎是自相矛盾的。如果表示层正在通过NAT(驱动器?)(网关?)进行路由,则它也不是通过Internet网关进行路由。您的哪一层在您的哪个子网上,每个子网的默认路由是什么?...特别是,面向外部的ELB必须位于默认路由指向Internet网关的子网中,这几乎总是意味着将其与平衡流量的实例放置在同一子网中是不正确的。这些实例将位于默认路由为NAT网关的子网中,而NAT网关本身也将与为其提供出站服务的实例不在同一子网中,但可能位于同一子网中子网作为ELB。
是的,对此感到抱歉,开始有些话混淆了。在三层中的每层中,我都有两个子网(一个在a区,一个在b区)。表示层通过nat路由通过Internet网关。我没有提到的一件事是NAT在它自己的单独子网中。然后应用程序和核心路由通过nat网关。
您可能需要彻底审查该问题并相应地进行澄清-这是一个好问题,但是要遵循一些困难的难题,需要检查很多事情。当您说“在进行api调用时,应用程序UI在选项请求上超时”时,谁会看到此错误?外部呼叫者?在app-ec2上使用curl -X OPTIONS 127.0.0.1 ...?只有选项坏了吗? ELB是“经典”而不是“应用”,对吗?是否所有实例都能通过NAT正确访问Internet,例如卷曲ipv4.icanhazip.com? (是的,我要求的原因可能看起来不太明显。)
除非我完全误解,否则react.js应用程序在浏览器中运行并且需要联系API服务器,您的前端服务器仅提供html和js文件,并且不会路由/代理对API的请求