同じサブネットでAWS内部ロードバランサーを呼び出すアプリケーションがタイムアウトになる


7

背景:

Amazonのvpcを使用して、やや複雑なネットワークを作成しました。2つのアベイラビリティーゾーンにまたがる3層ネットワークです。各レイヤーは、zone-aとzone-bにサブネットを持っています。プレゼンテーション層は上部にあり、アプリケーション層は中央にあり、コア層は下部にあります。

すべてのセキュリティグループとサブネットのACLは現在、すべてのインバウンドおよびアウトバウンドトラフィックを許可しており、問題の表面領域を減らすのに役立ちます。

プレゼンテーション層のルーティングテーブルは、すべてのトラフィックをインターネットゲートウェイに向けています。NATゲートウェイは分離されたサブネット内にあり、すべてのトラフィックをインターネットゲートウェイに向けています。

私のアプリケーションには、UI(React.js)とAPI(Node / Express)の2つのコンポーネントがあります。これらは、Dockerイメージとして展開されます。それぞれの前には、古典的なロードバランサーがあります。

UI-ELBはインターネットに面し、プレゼンテーションレイヤーに常駐し、トラフィックを80/443からポート8080にルーティングし、アプリケーションレイヤーサブネットに配置されている私のapp-ec2に関連付けられています。

私のAPIの前には内部ロードバランサーがあります。API-ELBはアプリケーション層(app-ec2と同じサブネット内)にあり、ポート80/443のトラフィックを受け取り、ポート3000のコアのapi-ec2にルーティングします。

どちらのロードバランサーも、インスタンスにトラフィックを渡す前に証明書をオフロードしています。

私は両方のロードバランサーをRoute53でエイリアスとして関連付け、アプリケーションできれいなURL(https://app.website.com)で参照しています。各ロードバランサーは、定義されたヘルスチェックに合格し、使用中のすべてのec2インスタンスを報告します。

最後に、APIで、cors nodejsパッケージを使用してcorsを有効にしました。

これが私のネットワークの簡単で汚い図です。

問題:

APP-ELBは私をアプリケーションに正常にルーティングします。ただし、アプリがGETリクエストをAPI-ELBに送信しようとすると、最初にエラーコード408でタイムアウトするOPTIONSリクエストが送信されます。

変になるところ

デバッグ中に遭遇した奇妙なことのいくつかは次のとおりです。

  1. app-ec2インスタンスにSSH接続して、API-ELBに対して正常にcurlを実行できます。私は多くを試しました、そしてそれらはすべてうまくいきます。いくつかの例は以下の通りですcurl -L https://api.website.com/system/healthcheckcurl -L -X OPTIONS https://api.website.com/system/healthcheck。常に必要な情報を返します。
  2. アプリケーション全体をネットワークからパブリックデフォルトvpcに移動しましたが、想定どおりに機能します。
  3. すべてのネットワーク要求をコンソールに書き込むapi-ec2があります。ヘルスチェックリクエストは表示されますが、app-ec2からのリクエストは表示されません。これは、トラフィックがapiに到達していないことを私に信じさせる。

本当に私を完全に失った最大のことは、内部api elbのカーリングが機能することですが、同じ正確なURLへのaxiosリクエストは機能しません。これは私にはまったく意味がありません。

私が試したこと

私は当初、ACLルールとセキュリティグループで遊んでいて、何か間違ったことをしていると思っていました。結局、私は「ねじって」と言って、すべてを開いて、その部分を方程式から外そうとしました。

私はAPIでCorsをいじるのに多くの時間を費やしてきました。最終的に、私が持っている構成に着陸しapp.use(cors())ます。これは、corsノードパッケージによって提供されるデフォルトのコールバックです。app.options('*', cors())ドキュメントで推奨されているものも含めました。

私は太陽の下ですべてをググっていますが、具体的にはエルブでいくつかの特別なカスタムヘッダーを定義する必要があるかどうか?しかし、何かを見つけることができないようです。さらに、アプリをネットワークの外に移動したところ、問題なく動作しました。

私は他にもたくさんのことを試したと思いますが、これらが最も適切なようです。何が欠けていますか?これは非常に曖昧で広範な問題であり、膨大な投稿になる可能性があることは承知していますが、それを読む際の洞察と時間に感謝します!


つまり、ゾーンごとに2つのサブネット(プレゼンテーションレイヤー、アプリケーションレイヤー、コアレイヤー)があります。 それは3つです。それを明確にできますか? プレゼンテーションレイヤーのルートテーブルと、インターネットゲートウェイを介してすべてのトラフィックをルーティングするNATドライブがあります。プレゼンテーション層とコア層の両方で、NATドライブを介してすべてのトラフィックをルーティングしています。 それは自己矛盾しているようです。プレゼンテーション層がNAT(ドライブ?)(ゲートウェイ?)を介してルーティングしている場合、インターネットゲートウェイもルーティングしていません。どのレイヤーがどのサブネット上にあり、各サブネットのデフォルトルートは何ですか?
マイケル-sqlbot 2017年

1
...具体的には、外部に面したELBは、デフォルトルートがインターネットゲートウェイを指すサブネット上にある必要があります。これは、ほとんどの場合、トラフィックを分散するインスタンスと同じサブネット上に配置することが正しくないことを意味します。これらのインスタンスは、そのデフォルトルートNATゲートウェイである...とNATゲートウェイ自身のサブネット上になり、順番になりません、それはアウトバウンドサービスを提供されているインスタンスとして同じサブネットのいずれかになりますが、同じであるかもしれませんELBとしてのサブネット。
マイケル-sqlbot 2017年

ええ、それについて申し訳ありませんが、いくつかの単語が混同され始めました。3つのレイヤーのそれぞれに2つのサブネット(ゾーンaに1つとゾーンbに1つ)があります。プレゼンテーション層は、natと同様に、インターネットゲートウェイを介してルーティングされます。私が言及しなかったことの1つは、NATが独自の独立したサブネットにあることです。次に、アプリとコアがNATゲートウェイを経由します。
David Meents 2017年

1
質問を十分に確認し、それに従って明確にすることをお勧めします。これは良い質問ですが、いくつかの難しい畳み込みや、確認する必要のある事項があります。あなたが言うとき、「アプリケーションUIはオプションにタイムアウトされるAPI呼び出し際に要求」このエラーを見ていますか?外部の発信者ですか?curl -X OPTIONS 127.0.0.1...app-ec2?OPTIONS壊れてるだけ?ELBは「クラシック」であり、「アプリケーション」ではありませんか?すべてのインスタンスは、NATを介してインターネットに正しくアクセスできcurl ipv4.icanhazip.comますか?(はい、わかりにくい理由を尋ねます。)
Michael-sqlbot 2017年

1
私が完全に誤解していない限り、react.jsアプリはブラウザーで実行され、APIサーバーに接続する必要があります。フロントエンドサーバーはhtmlファイルとjsファイルを提供するだけで、APIへのリクエストのルーティング/プロキシを行いません
Tensibai

回答:


7

だからあなたが実際に持っているのはこれです:

OPのアーキテクチャのスキーマ

API ELBはプライベートゾーンにあるため、インターネットからはアクセスできません。
React.jsのフロントエンドは、UIサーバーではなくユーザーのブラウザーで実行されるだけで、これらのサーバーは静的ファイルを提供するだけです。

2つのオプションがあります。API呼び出しをAPI ELBにリダイレクトするようにフロントエンドサーバーを構成するか、インターネットに面するようにAPI ELBを更新するだけです。

JavaScriptアプリの通常の落とし穴は、JEEアプリケーションのようにフロントエンドサーバーではなく、ユーザーのブラウザー内で実行することを忘れていることです。


1

これは、非対称またはnパスのルーティングの問題のように聞こえます。これはおそらく起こっていることです:

IPアドレス192.168.1.1のマシンAは、192.168.1.10のLBを介して[SYN]要求を開始します。次に、LBはペイロードをマシンBの192.168.1.2にプロキシするため、ペイロードにはソース:192.168.1.1と宛先:192.168.1.2(以前は192.168.1.10)があります。

では、192.168.1.2が[SYN、ACK]で応答するとどうなりますか?何をすべき起こることはマシンBがマシンAに応答しなければならないということであるロード・バランサを介して-通常、LBを介してトラフィックをルーティングするサーバー上のデフォルトルートまたはゲートウェイが原因です。ただし、この場合、マシンは同じサブネット上にあるため、ルート/ゲートウェイは使用されず、ルーティングテーブルはサーバーによって無視されます。これは、サーバーが応答すると、[SYN、ACK]がマシンAに表示され、マシンAがリクエストを開始したIPとは異なるIPから送信されているように見えることを意味します。は192.168.1.2(マシンB)からの[SYN、ACK]を確認しているため、このシナリオでは、応答が間違ったデバイスに送信されたため、LBはマシンBとの接続を確立できません。

これが外部トラフィックに対して機能する理由は、デフォルトルートが原因です。他の全員への応答はELBを介してルーティングされます。ELBは接続を開始していることを認識し、自動的に応答をインターセプトし、192.168.1.2のソースを192.168.1.10にスワップします。

したがって、この問題の1つの解決策として、ワンアームロードバランシング(スティック上のロードバランサーとも呼ばれる)を実装できます。これは、ロードバランサの内部インターフェイスで送信元NATを使用することになります(したがって、ロードバランサに外部インターフェイス192.168.1.10があり、内部インターフェイスに192.168.1.11があると仮定します)。これにより、接続の問題を解決する必要があるマシンBの観点から、すべてのトラフィックが192.168.1.11から送信されているように見えます。

ただし、AWS ELBはSNATをサポートしていないようです。そのため、ホストとELBを別のサブネットに配置するか、F5の仮想エディション(時間単位またはBYOLフレーバー)のようなSNATをサポートするものを使用する必要があります。ただし、SNATの接続制限に注意してください。約30kを超える同時接続が必要な場合は、SNATポートが使い果たさ、SNATプールの使用を開始する必要があります。

したがって、最善の解決策(コストと将来の問題を防ぐため)は、クライアントとサーバーが異なるサブネット上にあることを確認することです。

確認する最良の方法は、接続しているホストやバックエンドサーバーでtcpdumpを使用し、ロードバランサーを経由する代わりに、バックエンドサーバーとの間で直接やり取りされる応答を探すことです。次に、ダンプファイルをWireSharkにロードして、何が起こっているのかを正確に把握できます。


ELBはパケットを転送しません。新しいTCP接続を作成し、ペイロードを転送します。ルートの非対称性は、問題になり得ないものの1つです。
マイケル-sqlbot 2017年

F5もそうですが、それでも非対称ルーティングの問題があります。完全なプロキシアーキテクチャと別個の新しいTCP接続を使用しても、F5ロードバランサーはデフォルトで接続クライアントのソースアドレスになるため、問題は上記のとおり正確に発生します。ELBは同様の方法で動作すると想定しています。A10も同じように動作することは知っています。
James Shewey 2017年

ELBには別のIPがあります。The
Robo

1
AWS ELBは、TCPロードバランサーとしてだけでなく、リバースプロキシとしても機能します。OPがELBがSSLオフロードを実行していると述べたように、それはTCPバランサーにすることはできず、HTTPリバースプロキシでなければなりません。あなたの答えはコンテキストに適用できず、ELBは発信パケットには使用されません(ルーターではありません)。さらに、2つのインターフェイスを持つF5でプロキシしようとして、各インターフェイスに同じサブネットを設定すると、実際に問題が発生します。SNATでそれを解決することは、悪い解決策です。
Tensibai

1
さて、ここであなたの例からマシンBが見るのはELB IPです。クライアントIPはX-Forwarded-Portヘッダーにあります。クライアントはサーバーのそばにいても問題ありません。HTTPモードでは、ELBはSSL終了のF5として機能しません。(TCPモードでも、ロードバランサーのようなnginxであり、リモートで比較できるものはありません)。私があなたがハングしていると私が思うのは「プロキシ」です。私たちは確かにパケットのプロキシについて話しているのであって、パケットを転送することについては話していません。必要に応じて、2つのマシンと同じサブネット内のELBのtcpdumpを送信できます。
Tensibai
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.