NFTとNginxによる直接ルーティングとバランシング

負荷の高いネットワークアプリケーションを開発する場合、負荷分散が必要です。

人気のあるL7バランシングツールはNginxです。 回答をキャッシュし、さまざまな戦略を選択し、LUAでスクリプトを作成することもできます。

Nginxのすべての魅力にもかかわらず、次の場合:

  1. HTTPを使用する必要はありません。
  2. ネットワークから最大値を絞り出す必要があります。
  3. 何もキャッシュする必要はありません-バランサーには、ダイナミクスを備えたクリーンなAPIサーバーがあります。

疑問が生じるかもしれません:なぜNginxが必要なのですか? L7でリソースのバランスをとるのはなぜですか。SYNパケットを単純に転送する方が簡単ではないでしょうか。 (L4ダイレクトルーティング)。

レイヤー4のバランス調整または古代のバランス調整方法


一般的なパケット転送ツールはIPVSでした。 彼は、トンネルとダイレクトルーティングを介してバランスタスクを実行しました。

最初のケースでは、各接続に対してTCPチャネルが確立され、ユーザーからのパケットはバランサー、ミニオン、そして逆の順序で送信されました。



このスキームでは、主な問題が見えます。反対方向では、データは最初にバランサーに行き、次にユーザーに行きます(Nginxは同じように機能します)。 通常、より多くのデータがユーザーに送信されるという事実を考えると、不必要な作業が実行され、この動作はパフォーマンスの低下につながります。

ダイレクトルーティングと呼ばれる(ただし、新しい機能を備えた)バランシング方式はありません。 概略的には、次のようになります。



直接ルーティングの場合、戻りパケットはバランサーをバイパスしてユーザーに直接送信されます。 明らかに、バランサーのリソースとネットワークの両方が節約されます。 ネットワークリソースを節約しても、トラフィックはそれほど節約されません。通常、サーバーを別のグリッドに接続し、トラフィックを考慮しないのが一般的ですが、バランサーを介した転送でさえミリ秒単位の損失です。

このメソッドは特定の制限を課します:

  1. インフラストラクチャが配置されているデータセンターでは、ローカルアドレスのスプーフィングを許可する必要があります。 上の図では、各ミニオンはバランサーに割り当てられているIP 10.10.0.1に代わってパケットを送り返す必要があります。
  2. バランサーは、ミニオンの状態について何も知りません。 したがって、Least ConnおよびLeast Time戦略は、すぐに使用できるわけではありません。 次の記事のいずれかで、それらを実装して結果を示します。

NFTablesが登場


数年前、LinuxはIPTables、ArpTables、EBTables、およびその他すべての[az] {1、} Tablesの代わりとして、NFTablesを積極的に宣伝し始めました。 Adramの私たちがネットワークからのミリ秒ごとの回答を絞る必要がある瞬間に、チェッカーと検索を引き出すことにしました-多分ipTablesはiphash転送を行うことを学び、あなたはそれをスピードアップすることができます。 それから私はnftablesにつまずきました。これはそれだけでなく、iptablesでもこれを行うことができません。
数日間の試用後、テストラボのNFTablesを介して直接ルーティングとチャネルルーティングを取得し、nginxと比較してベンチマークを行うことができました。

だから、テストラボ。 5台の車があります。

  1. nft-router-クライアントとAppServerサブネットを接続するタスクを実行するルーター。 その上に2つのネットワークカードがあります。192.168.56.254-アプリサーバーのネットワークを調べ、192.168.97.254-クライアントを調べます。 Ip_forwardが有効になり、すべてのルートが登録されます。
  2. nft-client:ab、ip 192.168.97.2が追跡されるクライアント
  3. nft-balancer:バランサー。 これには、クライアントがアクセスする192.168.56.4とミニオンサブネットからの192.168.13.1の2つのIPがあります。
  4. nft-minion-aおよびnft-minion-b:minions ipy:192.168.56.2、192.168.56.3、192.168.13.2および192.168.13.3(バランスをとるために同じネットワークと異なるネットワークの両方を使用してみました)。 テストでは、手先は「外部」タイプであるという事実で停止しました-192.168.56.0/24サブネット

すべてのMTU 1500インターフェイス。

ダイレクトルーティング


バランサーのNFTables設定:

table ip raw { chain input { type filter hook prerouting priority -300; policy accept; tcp dport http ip daddr set jhash tcp sport mod 2 map { 0: 192.168.56.2, 1: 192.168.56.3 } } } 

-300の優先度で、フック上に生のチェーンが作成されます。

宛先アドレスがhttpのパケットが到着した場合、送信元ポートに応じて(1台のマシンからテストするために実際にip saddrが必要です)、56.2または56.3が選択され、パッケージの宛先アドレスとして設定され、さらにルートに沿って送信されます。 大まかに言って、偶数ポート56.2、奇数ポート、それぞれ56.3(実際にはそうではありません。偶数/奇数ハッシュのためですが、正確に理解する方が簡単です)。 ターゲットIPを設定した後、パケットはネットワークに戻ります。 NATは発生しません。パッケージは、バランサーではなく、クライアントのソースIPを使用してミニオンに到達します。これは、ダイレクトルーティングに重要です。

Minion NFT設定:

 table ip raw { chain output { type filter hook output priority -300; policy accept; tcp sport http ip saddr set 192.168.56.4 } } 

-300の優先度で生の出力フックが作成されます(ここでは優先度が非常に重要です。より高いレベルでは、応答パケットに対して必要なメングリングが機能しません)。

httpポートからのすべての発信トラフィックは、56.4(ipバランサー)によって署名され、バランサーをバイパスしてクライアントに直接送信されます。

すべてが正常に機能するかどうかを確認するために、クライアントを別のネットワークに持ち込み、ルーターを通過させました。

また、arp_filter、rp_filterを無効にして(スプーフィングが機能するように)、バランサーとルーターの両方でip_forwardを有効にしました。

ベンチでは、NFTの場合、Nginx + php7.2-FPMが各ミニオンのUNIXソケットを介して使用されます。 バランサーには何もありませんでした。

Nginxの場合、バランサーのnginxと手先のTCP上のphp7.2-FPMを使用しました。 その結果、私はバランサーの背後でWebサーバーのバランスを取りませんでしたが、すぐにFPM(nginxにより正直になり、実際の生活により一貫したものになります)。

NFTの場合、ハッシュ戦略(表のnft dr )のみが使用され、nginxの場合:ハッシュ( ngx eq )および最小conn( ngx lc

いくつかのテストが行​​われました。

  1. スモールクイックスクリプト(スモール)

     <?php system('hostname'); 

  2. ランダムな遅延(rand)を伴うスクリプト。

     <?php usleep(mt_rand(100000,200000)); echo "ok"; 
  3. 大量のデータ(サイズ)を送信するスクリプト。

     <?php $size=$_GET['size']; $file='/tmp/'.$size; if (!file_exists($file)) { $dummy=""; exec ("dd if=/dev/urandom of=$file bs=$size count=1 2>&1",$dummy); } fpassthru (fopen($file,'rb')); 

    次のサイズが使用されました。
    512.1440.1460.1480.1500.2048.65535.655350バイト。
    テストの前に、各ミニオンの静的ファイルをウォームアップしました。

各テストでabを3回テストしました。

 #!/bin/bash function do_test() { rep=$3 for i in $(seq $rep) do echo "testing $2 # $i" echo "$2 pass $i" >> $2 ab $1 >> $2 echo "--------------------------" >> $2 done } do_test " -n 5000 -c 100 http://192.168.56.4:80/rand.php" "ngx_eq_test_rand" 3 do_test " -n 10000 -c 100 http://192.168.56.4:80/" "ngx_eq_test_small" 3 size=512 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1440 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1460 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1480 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1500 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=2048 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=65535 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=655350 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 

最初に、テスト時間、ミリ秒、および残りをRPSに決めた結果、持参する予定でした。これらは代表的なものであり、時間インジケータと相関しています。

次の結果を得ました:

サイズテスト-列-指定されたデータのサイズ。



ご覧のとおり、nftダイレクトルーティングが大きなマージンで勝ちます。

イーサネットフレームのサイズに関連する他のいくつかの結果を期待していましたが、相関関係は見つかりませんでした。 おそらく512ボディは1500 MTUに収まらないでしょうが、小さなテストは指標となるでしょう。

大容量(650k)では、nginxが分離を減らすことに気付きました。 おそらくこれはバッファとTCP Windowsサイズに関係しているのでしょう。

randテストの結果。 ミニオンでのスクリプト実行の速度が異なる条件で、最小connがどのように処理するかを示します。



驚くべきことに、nginxハッシュは最小connよりも速く動作し、最終パスでのみ最小connが少し先に進みましたが、これは統計的に有意なふりをしていません。
パスの数は、100個のスレッドが一度に出るという事実と、開始からのFPM-okが約10をロードするという事実のために非常に異なります。3番目のパスまでに、彼らは慣れる時間がありました-これはバーストの戦略の適用性を示しています。

NFTはこのテストを失ったと予想されます。 Nginxは、このような状況でのFPMとの相互作用を最適化します。

小テスト



nftはRPSでわずかに勝ち、最小のconnは再び部外者です。

ちなみに、このテストでは400-500RPSが発行されていることがわかりますが、512バイトを送信するテストでは1500でした-システムはこの1000を消費しているようです。

結論


NFTは、均一な負荷を最適化する状況でうまく機能しました。大量のデータが与えられ、アプリケーションの動作時間が決定され、クラスターのリソースがテールスピンに入らずに着信ストリームを処理するのに十分な場合。

各要求の負荷が混oticとしており、サーバーの負荷をハッシュ分割の残りのプリミティブと均等にバランスさせることができない状況では、NFTが失われます。

Source: https://habr.com/ru/post/J441348/


All Articles