こんにちは。よっしーです(^^)
今日は、k6を利用した大規模なテスト実行についてご紹介します。
背景
Dockerで構築したWebアプリの開発環境において、k6を利用した負荷テストについて調査したときの内容を備忘として残しました。
開発環境のソースは下記のリポジトリにあります。
ファイルのアップロードに関する考慮事項
ファイルのアップロードは、リソースの使用とクラウドコストの両方に影響を与える可能性があります。
ネットワークスループット
ファイルのアップロードにおいて、ロードジェネレーターマシンとSUTのネットワークスループットはボトルネックになる可能性が高いです。
メモリ
k6はファイルをアップロードする際に大量のメモリが必要です。各VUは独立しており、それぞれのVUは自身のメモリを持っており、ファイルはそれらをアップロードするすべてのVUにコピーされます。この状況を改善しようとするissue #1931 をフォローすることができます。
データ転送コスト
k6は非常に短時間で大量のデータをアップロードできます。大規模なテストを開始する前にデータ転送コストを理解してください。AWS EC2では、アウトバウンドデータ転送が高価です。地域に応じて、1GBあたりの価格は0.08ドルから0.20ドルまで変動します。最も安い地域を使用する場合、1GBあたりのコストは約0.08ドルです。したがって、1TBをアップロードするコストは約80ドルです。長時間実行されるテストだと、データ転送だけで数百ドルかかることもあります。 既にAWSでホストされている場合、ロードジェネレーターマシンを同じAWSリージョンと可用性ゾーン内で実行することを検討してください。場合によっては、このトラフィックがはるかに安くなるか、無料になることもあります。データコストの節約に関する追加のヒントについては、AWSでデータ転送コストを削減する方法に関する記事を参照してください。私たちの例はAWSを考慮して作成されていますが、同じ提案は他のクラウドプロバイダーにも適用されます。
仮想サーバーのコスト
AWS EC2インスタンスは比較的安価です。このベンチマークで使用した最大のインスタンスであるm5.24xlargeでさえ、1時間あたりわずか4.6ドルです。 テストが完了したら、ロードジェネレーターサーバーをオフにすることを忘れないでください。忘れられたEC2サーバーは、毎月数千ドルのコストがかかる可能性があります。ヒント:同じハードウェアの「スポットインスタンス」を起動することで、コストの10-20%で実行できることがしばしばあります。
一般的なエラー
実行中にエラーが発生した場合、それがロードジェネレーターによるものか、失敗したSUTによるものかを知ることは役立ちます。
read: connection reset by peer
これは、ターゲットシステムがTCP接続をリセットしたことに起因します。これは、ロードバランサーまたはサーバー自体がトラフィックを処理できない場合に発生します。
WARN[0013] Request Failed error="Get http://test.k6.io: read tcp 172.31.72.209:35288->63.32.205.136:80: read: connection reset by peer"
context deadline exceeded
これは、k6がリクエストを送信できるが、ターゲットシステムがタイムリーに応答しない場合に発生します。k6のデフォルトのタイムアウトは60秒です。システムがこの時間内にレスポンスを生成しない場合、このエラーが表示されます。
WARN[0064] Request Failed error="Get http://test.k6.io: context deadline exceeded"
dial tcp 52.18.24.222:80: i/o timeout
このエラーは、context deadline exceededに類似していますが、この場合、k6はHTTPリクエストを行うことすらできませんでした。ターゲットシステムはTCP接続を確立できませんでした。
WARN[0057] Request Failed error="Get http://test.k6.io/: dial tcp 52.18.24.222:80: i/o timeout"
socket: too many open files
このエラーは、ロードジェネレーターがオープンファイル記述子の制限に達したため、TCPソケットを開けないことを意味します。制限が十分に高く設定されていることを確認してください。 “Fine tuning OS” の記事を参照してください。
WARN[0034] Request Failed error="Get http://test.k6.io/: dial tcp 99.81.83.131:80: socket: too many open files"
備考
受け入れ可能なエラーレベルを決定します。大規模なテストでは、一部のエラーが常に存在することがあります。たとえば、5000万のリクエストを行って100回の失敗がある場合、これは一般的には良好な結果です(0.00002%のエラー)。
k6のベンチマーク
異なるEC2マシンでいくつかの大規模なテストを実行し、k6がどれだけの負荷を生成できるかを確認しました。一般的な観察結果は、k6がハードウェアに比例してスケーリングするというものです。リソースが2倍多いマシンは、おおよそ2倍多くのトラフィックを生成できます。このスケーラビリティの制限は、オープンな接続の数です。1台のLinuxマシンはIPアドレスごとに最大65,535個のソケットを開くことができます(詳細についてはRFC 6056を参照してください)。これは、1台のマシンで同時に送信できるリクエストの最大数が65,000件であることを意味します。これは設定されたエフェメラルポートの範囲、HTTP/2の使用状況(リクエストマルチプレキシングを使用し、より高いスループットを達成できる)など、さまざまな要因に依存する理論的な制限です。
ただし、RPS(リクエスト数/秒)の制限は主にSUTの応答時間に依存します。応答が100msで提供される場合、HTTP/1リクエストの理論的なRPS制限は、単一のリモートアドレスへの650,000です。
私たちはk6をベンチマークし、レポートを作成するために使用されるいくつかのスクリプトを含むリポジトリを保持しています。これらのテストはすべての新しいk6バージョンで実行され、結果はresults/ディレクトリで確認できます。
分散実行
負荷テストでは、分散実行は負荷を複数のマシンに分散するときに行われます。
ユーザーは通常、大規模なテストを実行するために分散実行モードを探します。単一のk6インスタンスでも膨大な負荷を生成できますが、分散実行は次の場合に必要です。
- 同時に複数の場所から負荷をシミュレートする。
- 1つのマシンだけでは処理できないテストの負荷をスケーリングする。
k6では、execution-segmentオプションを使用して、テストの負荷を複数のk6インスタンスに分割することができます。例えば:
## split the load of my-script.js across two machines
k6 run --execution-segment "0:1/2" --execution-segment-sequence "0,1/2,1" my-script.js
k6 run --execution-segment "1/2:1" --execution-segment-sequence "0,1/2,1" my-script.js
ただし、現時点では、k6の分散実行モードは完全に機能していないことに注意してください。現在の制限事項は次のとおりです。
- k6は分散実行のテストを調整するための「プライマリ」インスタンスの機能を提供していません。代替として、k6 REST APIと–pausedを使用して、複数のk6インスタンスの実行を同期させることができます。
- 各k6インスタンスは、独自に閾値を評価します。閾値の実行を無効にする場合は、–no-thresholdsを使用してください。
- k6は各インスタンスに対してメトリクスを個別に報告します。負荷テストの結果をどのように保存するかに応じて、一部のメトリクスを正しく計算するためにいくつかのメトリクスを集計する必要があります。
上記の制限を考慮して、k6テストの負荷をKubernetesクラスタ全体に分散するKubernetesオペレータがあります。詳細な手順については、Kubernetes上で分散k6テストを実行するためのチュートリアルをご覧ください。
クラウドでの大規模テスト
Grafana Cloud k6の商用オファリングは、大規模なテストを実行するための即座のソリューションを提供しています。その他の利点もあります。
プロジェクトにOSS(オープンソースソフトウェア)またはクラウドがどちらが適しているか確信が持てない場合、スケーラブルなソリューションを構築する際に考慮すべきリスクや機能について詳しく学ぶために、このホワイトペーパーを読むことをお勧めします。
おわりに
今日は、k6を利用した大規模なテスト実行についてご紹介しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント