turnutils_uclientによるTURNサーバーの負荷試験を通して得られた知見 #nttcom_ac2022

この記事は、 NTT Communications Advent Calendar 2022 14日目の記事です。

はじめに

皆様こんにちは。イノベーションセンター所属の @sublimer です。

普段はWebRTCプラットフォーム「SkyWay」の開発・運用の業務に取り組んでおり、現在は新しいSkyWayの正式リリースに向けて、インフラ・バックエンド・フロントエンドのコードをガリガリ書く楽しい日々を送っています。 一方のプライベートでは、自宅Kubernetesクラスターを盆栽のごとく愛情を持って育てています。

今回は、新しいSkyWay正式リリースに向けて、TURNサーバー(P2P通信においてNAT越えのために使用されるサーバー)の負荷試験を行った際に得られた知見をご紹介します。

⚠️ 注意

この記事では、負荷試験の実施方法について書いています。

負荷試験は、管理下にないシステムやサービスに対して行った場合は、DoS攻撃とみなされ罪に問われる可能性があります。 また、利用しているクラウドサービスが負荷試験を認めていない場合もあります。 もし負荷試験を実施する際は、十分に注意して実施するようにしてください。

今回の負荷試験についても、問題が生じないよう十分検討してから実施しています。

TURNサーバーの負荷試験の必要性

冒頭でも述べた通り、TURNサーバーはP2P通信においてNAT越えのために使用されます。 クライアントと通信先の間に位置してデータを中継することで、NAT越えを実現するのです。 例えばWebRTCを使ってP2Pで通信する際に、間にNATが入っていて直接通信できない場合でも、TURNサーバーがデータを中継することでNATを越えて通信できます。

このように、TURNサーバーは通信を中継する役割を担っているため、大量のトラフィックをさばく高い性能が求められます。 そのため、サービスリリース前に負荷試験を行い、どのくらいの負荷までならサービスに影響が出ないのか、想定通りにスケールするのかを確認しておくことが重要です。

試験方法の検討

プロトコルとしてHTTPを用いるケースであれば、k6Locustといったツールや、k6 CloudAzure Load Testingといったサービスを用いることで、比較的容易に負荷試験を始めることができます。

しかし、TURNのプロトコルはHTTPほど一般的なものではないため、負荷試験の方法を検討するところからはじめました。

今回検討した方法は、以下の2つです。

  1. turnutils_uclientを用いる
  2. ヘッドレスブラウザを用いる

2つを比較検討した結果、今回はturnutils_uclientを用いる方法を採用しました。以下で詳細を説明します。

まずturnutils_uclientについてです。 turnutils_uclientは、OSSのTURNサーバー、「coturn」に付属している、TURNのテスト用のCLIクライアントです。

turnutils_uclientを使う利点としては、様々なコマンドラインオプションがあるため、負荷試験の際のトラフィックを柔軟に制御できるという点が挙げられます。 またCLIツールであるため、動作のオーバーヘッドが比較的小さく、出力された試験結果の集計も容易にできます。

一方で、目的のトラフィックを発生させるためのオプションの指定がやや複雑であるという課題があります。

次にヘッドレスブラウザについてです。ヘッドレスブラウザは、GUIが無いブラウザのことで、PuppeteerPlaywrightが有名です。 予めWebRTCで通信するWebアプリケーションを作り、それをヘッドレスブラウザ上で動かすことで、テスト用のクライアントとして使うことができます。

ヘッドレスブラウザを使う利点としては、ブラウザ経由でWebRTCの通信ができるため、エンドユーザーの使い方に、より近い状況を再現して負荷試験ができるという点が挙げられます。

一方で、クライアント毎にブラウザを起動することになるため、負荷試験のように大量のクライアントを生成する際は、比較的大きなオーバーヘッドが生じるという課題があります。

両者を比較した結果、今回はturnutils_uclientを用いた方法を採用しました。

ヘッドレスブラウザを用いた方法の場合、ヘッドレスブラウザを制御するアプリケーションと試験用のWebアプリケーションの両方を作る必要があり、実装のためのコストがやや大きくなると想定されたためです。

turnutils_uclientの「オプションの指定がやや複雑である」という課題は、後述する負荷試験用のスクリプトを作成することで解決しました。

ここで、「WebRTCの通信を実際に行うヘッドレスブラウザの方が、より現実に即した試験が行えるのでは?」と思われた方もいるかもしれません。

もちろん、ヘッドレスブラウザを使った方が、よりエンドユーザーの使い方に近い状況で試験が行なえます。

ただ、今回はトラフィックが増加した際の負荷についての試験が目的ですので、指定しただけのトラフィックを発生させられれば問題ありません。 また、TURNサーバーは、中継しているデータがWebRTCの通信によるもなのかどうかに関わらず、単なるバイナリのデータとして中継しています。

以上を踏まえ、turnutils_uclientによって生成したトラフィックで試験を実施しても、問題は無いと判断しました。

試験観点の検討

今回は、以下の5つの観点について負荷試験を実施することとしました。

  1. 負荷を線形に増加させた際の挙動の確認
  2. TURNのプロトコルごとの比較
  3. 新規接続のスパイクの影響の調査
  4. VMスペックごとの比較
  5. スケールアウト時の挙動の確認

それぞれの試験観点について、なぜ試験が必要なのかを簡単に説明します。

1. 負荷を線形に増加させた際の挙動の確認

TURNサーバーがさばけるトラフィックの限界値を調べるために実施します。

限界値が分かれば、その値に合わせて監視システムのアラート条件を決めたり、スケールアウトさせる基準を決めることができます。

2. TURNのプロトコルごとの比較

クライアント・サーバー間の通信で用いられるプロトコルごとの、特性を調べるために実施します。

TURNは、以下の3通りのプロトコルでクライアント・サーバー間の通信ができます。

  1. TURN-UDP
  2. TURN-TCP
  3. TURN-TLS

どのプロトコルが使われるかは動的に決まるケースがほとんどであるため、予め特性を把握しておく必要があります。

ちなみに、TURNと中継先のクライアントの間の通信は、WebRTCにおいてはUDPのみが用いられます。

3. 新規接続のスパイクの影響の調査

サービスを提供する際に、事前にエンドユーザーの使い方を予測するのは困難であるため、どの程度の新規接続のスパイクであればさばけるのかを調べるために実施します。

スパイクはある程度のトラフィックをさばいている状態で突然発生するため、定常状態として一定量のトラフィックを流した状態で試験を実施します。

4. VMスペックごとの比較

VMのスペックによってさばけるトラフィックは異なるため、どのスペックのVMを使えば最も費用対効果が高くなるのかを調べるために実施します。

5. スケールアウト時の挙動の確認

負荷が高くなった際にスケールアウトを実施して、きちんとスケールするかどうかを検証するために実施します。 TURNサーバーは1台1台が独立して動くため、基本的には台数を増やしてあげるだけでスケールします。

また、上記の5つの試験観点に加えてWebRTCのループバックの通信をTURN経由で行い、目視、及びwebrtc-internalsの値を確認してWebRTCの通信品質が大きく低下していないかを確認することとしました。

ループバックの通信では、ストップウォッチの画面を画面共有することで、簡易的にRTTの計測を実施しました。 正確なRTTの値を確認する際は、webrtc-internalsに表示される remote-inbound-rtproundTripTime 1の値を確認しました。

負荷試験用のスクリプトの実装

turnutils_uclientはTURNのテスト用クライアントとして汎用的に使えるようになっているため、負荷試験用に用いる際にはオプションの指定について工夫が必要になります。

ここで、turnutils_uclientのオプションについて、いくつか抜粋して簡単に説明します。

  • -l: TURNのメッセージのサイズをbyte単位で指定します。
  • -n: TURNのメッセージの送信回数を指定します。
  • -z: TURNのメッセージの送信間隔をミリ秒単位で指定します。
  • -t: TURNサーバーとの間の通信をTCPで行います。
  • -S: TURNサーバーとの間の通信を暗号化します。 -t と組み合わせることで、TURN-TLSの通信になります。

このように、turnutils_uclientにはトラフィックの帯域幅や通信する時間を指定するオプションはありません。 ですので、「  x bpsの通信を  t 秒間行う」という条件を指定したい場合は、メッセージの送信回数や送信間隔を計算してからオプションとして渡してあげる必要があります。

送信回数を  n 、送信間隔を  i msとすると、以下の計算式で  x t から  n i を求めることができます。

 n = \dfrac{\frac{x \times t}{8}}{1024}

 i = \dfrac{1}{\frac{n}{t \times 1000}}

今回は、TURNのメッセージのサイズは1024 byteで固定としています。

したがって、例えば1 Mbps(1,000,000 bps)の通信を60秒間流す場合は、  \lceil n \rceil = 7325 \lfloor i \rfloor = 8 msとなります。

今回負荷試験を実施するにあたって、上記の方法で値を計算しturnutils_uclientのオプションとして指定するためのスクリプトを、TypeScriptで実装しました。 試験用のスクリプトは、指定されたパラメーターを元にturnutils_uclientのオプションを計算し、 Node.jsの child_process 2モジュールを使って、turnutils_uclientを子プロセスとして大量に起動する仕組みになっています。

今回負荷試験に用いた試験用のスクリプトには、オプションの値の計算に加えて、以下のような機能も実装しました。

  • 試験の各パラメーターをコマンドライン引数として指定できる機能
  • TURNの認証情報を、APIから取得する機能
  • 試験結果をJSONファイルとして出力する機能
  • 複数の試験結果のJSONファイルを集計し、CSVファイルとして出力する機能

今回は、条件を変えて繰り返し試験をする必要があったため、試験の条件をコマンドライン引数として設定できるようにしました。 また、複数の試験結果を集計してCSVファイルとして出力することで、容易にグラフを作成できるようにしました。

負荷試験の実施

負荷試験を実施する前に、以下の2点の確認を実施しました。

  1. 負荷試験用のスクリプトが想定通りのトラフィックを生成できているか
  2. 負荷をかける側のVMが飽和状態になっていないか

スクリプト内で行ってるパラメーターの計算処理が本当に正しいのかを、実際に試験用のトラフィックを生成して、そのトラフィックを計測して確認しました。 計測にはdstatコマンドとvnstatコマンドを使用し、負荷をかける側とかけられる側の両方のVMでトラフィックを計測しました。 その結果、想定通りのトラフィックを生成できていることが確認できました。

また、負荷をかける側がボトルネックとなり試験結果に影響することが無いよう、負荷をかける側のVMのメトリクスをチェックしました。 その結果を踏まえ、負荷をかける側のVMのスペックを設定しました。

実際の負荷試験では、予め検討した試験用パラメーターを指定してスクリプトを実行し、その結果をJSONファイルとして出力しました。 試験後は、出力されたJSONファイルを元に集計処理を行い、複数の試験結果をCSVファイルに出力した後、Google SpreadsheetにCSVファイルをインポートしてグラフ化を行いました。

最後に、試験結果やグラフをまとめ、負荷試験のレポートを作成しました。

この流れで、前述した5つの観点について試験を実施しました。

負荷試験の結果

今回は、「負荷を線形に増加させた際の挙動の確認」の観点についての試験結果を、抜粋してご紹介します。

トラフィック量を増やして繰り返し試験を実施した際のCPU Idleと送信トラフィックの値は、それぞれ以下のグラフのようになりました。

(上段:CPU Idle 下段:送信トラフィック)

トラフィックが増えるに従ってCPU使用率も上昇していることが見て取れます。 また、CPU使用率がほぼ100%で張り付くと、ネットワークトラフィックも頭打ちとなり、性能が飽和状態になっていることが分かります。

このときの通信のRTTやパケロス率、jitterといった品質を示す指標は、いずれも非常に悪化していました。 加えて、TURN経由で行っているWebRTCのループバックの通信の映像にもカクつきが生じており、性能が上限に達していたことが分かりました。

この結果より、TURNサーバーはトラフィックの増加とともに負荷も高くなるものの、CPU使用率が張り付くまではトラフィックをさばける事が分かりました。

他の4つの観点についても試験し、TURNサーバーに性能面で大きな問題はないことが分かりました。

ところで、今回試験を実施する中で、turnutils_uclientには「標準出力にログを二重に出力してしまう」という意図しない挙動があることに気づきました。 折角の機会だと思い、この挙動を修正するプルリクエストを出してみました。

github.com

OSSにプルリクエストを出した経験はあまりないのですが、すぐにマージしていただけて、貴重な経験ができたかなと思います。

おわりに

TURNの負荷試験はインターネット上に公開されているノウハウがHTTPほどは無いため、手探りで進めた部分もありましたが、無事に試験を終えることができました。

今回の試験を通して、以下のような知見を得ることができました。

  • turnutils_uclientの各オプションの役割
  • 負荷試験で確認すべき観点
  • TypeScriptでのツール開発の流れ

昨年、自分でTURNサーバーを実装してみたことに加え、今回turnutils_uclientを用いて負荷試験を行い、TURNについての理解がより深まったかなと思います。

負荷試験を実施した後、今回の負荷試験で得られたデータを元に、TURNサーバーの台数やVMのスペックなどの検討を実施しました。 お客様に安定して利用していただけるよう、十分なリソースを確保しましたので、正式にリリースされた際はぜひ使っていただければと思います。

「新しいSkyWayを今すぐに使ってみたい!!」という方向けに、Beta版としてもリリースしていますので、気になる方はこちらもどうぞ!!

また、SkyWayの開発チームでは、現在インターンシップの参加者を募集しています。 以下のリンク先の、「ビデオ通話プラットフォーム「SkyWay」、低遅延ライブ配信プラットフォーム「Smart vLive」、テレプレゼンスロボット(遠隔操作ロボット)のいずれかの技術開発」に詳細な内容が記載されています。

information.nttdocomo-fresh.jp

締切は、なんと今日!! 12月14日 13:00ですので、少しでも興味がある方はお早めに応募してみてください!!

それでは、明日もお楽しみに!!

参考サイト

© NTT Communications Corporation All Rights Reserved.