`wrk`を使った正確なパフォーマンステスト

API7.ai

November 25, 2022

OpenResty (NGINX + Lua)

この記事では、パフォーマンステストについて話します。この部分はOpenRestyに特有のものではなく、他のバックエンドサービスにも適用されます。

パフォーマンステストは広く普及しており、製品を提供する際には、QPS、TPS、レイテンシ、同時接続可能なユーザー数などのパフォーマンス指標が付属しています。オープンソースプロジェクトの場合も、バージョンをリリースする前にパフォーマンステストを行い、前のバージョンと比較して大幅な低下がないか確認します。また、中立的なウェブサイトが類似製品の比較パフォーマンスデータを公開していることもあります。パフォーマンステストは私たちにとって身近な存在です。

では、科学的で厳密なパフォーマンステストはどのように行うのでしょうか?

パフォーマンステストツール

良い仕事をするためには、まず良いツールを使う必要があります。良いパフォーマンステストツールを選ぶことが成功の半分です。

Apache Benchmarkツール、通称abは、最も簡単なパフォーマンステストツールですが、残念ながらあまり役に立ちません。これは、現在のサーバーサイドが並列および非同期I/Oに基づいて開発されており、そのパフォーマンスは悪くないためです。abはマシンのマルチコアを活用せず、生成されるリクエストも十分なストレスをかけません。この場合、abテストから得られる結果は現実的ではありません。

したがって、ストレステストツールの基準として次のことを選ぶことができます:ツール自体が堅牢なパフォーマンスを持ち、サーバーサイドプログラムに十分なストレスをかけることができること。

もちろん、多くのストレステストクライアントを起動し、分散型ストレステストシステムにすることもできます。しかし、その複雑さも増すことを忘れないでください。

OpenRestyの実践に戻ると、私たちが推奨するパフォーマンステストツールはwrkです。まず、なぜそれを選ぶのでしょうか?

第一に、wrkはツール選択の基準を満たしています。wrkが単一マシンで生成するストレスは、NGINXを簡単に100%のCPU使用率に達させることができます。他のサーバーサイドアプリケーションは言うまでもありません。

第二に、wrkはOpenRestyと多くの共通点があります。wrkはゼロから書かれたオープンソースプロジェクトではなく、LuaJITとRedisの肩に乗り、システムのマルチコアリソースを活用してリクエストを生成します。さらに、wrkはLua APIを公開しており、Luaスクリプトを埋め込んでリクエストヘッダーや内容をカスタマイズすることができ、非常に柔軟です。

では、wrkをどのように使うべきでしょうか?以下のコードスニペットを見るだけで簡単です。

wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html

これは、wrkが12スレッドを使用し、400の長い接続を30秒間保持して、指定されたAPIインターフェースにHTTPリクエストを送信することを意味します。もちろん、パラメータを指定しない場合、wrkはデフォルトで2スレッドと10の長い接続を開始します。

テスト環境

テストツールを見つけた後、すぐにストレステストを開始することはできません。テスト環境を一度確認する必要があります。テスト環境で確認すべき主な項目は4つあり、それらについて詳しく説明します。

1. SELinuxを無効にする

CentOS/RedHatオペレーティングシステムを使用している場合、SELinuxを無効にすることをお勧めします。そうしないと、多くの奇妙な権限問題に遭遇する可能性があります。

次のコマンドでSELinuxが有効になっているか確認しましょう。

$ sestatus
SELinux status: disabled

有効になっている(enforcing)と表示されている場合、$ setenforce 0で一時的に無効にすることができます。また、/etc/selinux/configファイルを変更して、SELINUX=enforcingSELINUX=disabledに変更することで永続的に無効にすることもできます。

2. 最大オープンファイル数

次に、現在のシステム全体の最大オープンファイル数を次のコマンドで確認する必要があります。

    $ cat /proc/sys/fs/file-nr
    3984 0 3255296

ここでの最後の数字3255296が最大オープンファイル数です。この数字がマシンで小さい場合、/etc/sysctl.confファイルを変更して増やす必要があります。

fs.file-max = 1020000
net.ipv4.ip_conntrack_max = 1020000
net.ipv4.netfilter.ip_conntrack_max = 1020000

変更後、システムサービスを再起動して有効にする必要があります。

sudo sysctl -p /etc/sysctl.conf

3. プロセス制限

システム全体の最大オープンファイル数に加えて、プロセスが開くことができるファイル数にも制限があります。これはulimitコマンドで確認できます。

$ ulimit -n
1024

この値はデフォルトで1024と小さいことに気づくでしょう。各ユーザーリクエストはファイルハンドルに対応し、ストレステストは多くのリクエストを生成するため、この値を増やして数百万に変更する必要があります。次のコマンドで一時的に変更できます。

ulimit -n 1024000

また、設定ファイル/etc/security/limits.confを変更して永続的にすることもできます。

* hard nofile 1024000
* soft nofile 1024000

4. NGINX設定

最後に、NGINX設定に小さな変更を加える必要があります。以下の3行のコードです。

events {
    worker_connections 10240;
}

これにより、各Workerの接続数を増やすことができます。デフォルト値は512しかないため、高ストレステストには十分ではありません。

ストレステスト前の確認

この時点で、テスト環境は準備が整っています。早速テストを開始したいと思っているでしょうが、wrkでテストを開始する前に最後にもう一度確認しましょう。人間はミスをするので、クロスチェックすることが重要です。

この最後のテストは2つのステップに分けることができます。

1. 自動化ツールc1000kを使用する

c1000kSSDBの作者によるものです。名前からわかるように、このツールの目的は、あなたの環境が10^6の同時接続要件を満たすことができるかどうかを確認することです。

このツールの使用も非常に簡単です。serverclientを起動し、serverプログラムがポート7000でリッスンし、clientプログラムがストレステストを開始して、実際の環境でのストレステストをシミュレートします:

. /server 7000
. /client 127.0.0.1 7000

すぐに、clientserverにリクエストを送信し、現在のシステム環境が100万の同時接続をサポートできるかどうかを確認します。自分で実行して結果を見ることができます。

2. サーバープログラムが正常に動作しているか確認する

サーバーサイドプログラムが正常に動作していない場合、ストレステストはエラーログのリフレッシュテストや404応答テストになる可能性があります。

したがって、テスト環境テストの最後で最も重要なステップは、サーバーサイドのユニットテストセットを実行するか、主要なインターフェースを手動で呼び出して、wrkテストのすべてのインターフェース、返り値、HTTP応答コードが正常であり、logs/error.logにエラーレベルのメッセージがないことを確認することです。

リクエストの送信

さて、これですべての準備が整いました。wrkでストレステストを開始しましょう!

$ wrk -d 30 http://127.0.0.2:9080/hello
Running 30s test @ http://127.0.0.2:9080/hello
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   595.39us  178.51us  22.24ms   90.63%
    Req/Sec     8.33k   642.91     9.46k    59.80%
  499149 requests in 30.10s, 124.22MB read
Requests/sec:  16582.76
Transfer/sec:      4.13MB

ここではパラメータを指定しなかったので、wrkはデフォルトで2スレッドと10の長い接続を開始します。wrkのスレッド数と接続数を非常に大きく調整する必要はありません。ターゲットプログラムが100%のCPU使用率に達することができれば十分です。

ただし、ストレステストの時間は短すぎてはいけません。数秒のストレステストは意味がありません。そうでないと、ストレステストがサーバープログラムのホットリロードが完了する前に終了してしまう可能性があります。同時に、tophtopなどの監視ツールを使用して、ストレステスト中にサーバーのターゲットプログラムが100%のCPU使用率で動作しているかどうかを確認する必要があります。

現象的に、CPUが完全にロードされ、テストが停止した後にCPUとメモリの使用率が急速に低下する場合、おめでとうございます、テストは成功しました。しかし、以下のような例外がある場合、サーバーサイド開発者として注意を払う必要があります。

  • CPUが完全にロードされない。これはwrkの問題ではなく、ネットワークの制限やコード内のブロッキング操作が原因である可能性があります。コードを確認するか、off CPUフレームグラフを使用してこれを判断できます。
  • ストレスが停止してもCPUが常に完全にロードされている。これは、正規表現やLuaJITのバグによる無限ループがコード内にあることを示しています。実際の環境でこれに遭遇したことがあります。この時点で、CPUフレームグラフを使用してこれを判断する必要があります。

最後に、wrkの統計を見てみましょう。この結果に関して、私たちは一般的に2つの値に注目します。

1つ目はQPS、つまりRequests/sec: 16582.76で、サーバーサイドで1秒間に処理されるリクエスト数を示す正確な数値です。

2つ目はレイテンシ:Latency 595.39us 178.51us 22.24ms 90.63%で、QPSと同じくらい重要で、システムの応答速度を反映しています。例えば、ゲートウェイアプリケーションの場合、レイテンシを1ミリ秒以内に保ちたいと考えています。

さらに、wrklatencyパラメータも提供しており、レイテンシのパーセンテージ分布を詳細に出力します。例えば:

Latency Distribution
        50% 134.00us
        75% 180.00us
        90% 247.00us
        99% 552.00us

ただし、wrkのレイテンシ分布データは不正確です。なぜなら、ネットワークやツールの摂動を人為的に追加してレイテンシを増幅しているためです。これには特別な注意が必要です。

まとめ

パフォーマンステストは技術的な仕事であり、正しくうまくできる人は多くありません。この記事がパフォーマンステストについてより包括的な理解を提供することを願っています。

最後に、wrkはカスタムLuaスクリプトをサポートしてストレステストを行うことができるので、そのドキュメントに基づいて簡単なLuaスクリプトを書くことができるかどうかという質問を残します。少し難しいかもしれませんが、完了するとwrkが公開するインターフェースの意図を理解できるでしょう。

この記事をより多くの人と共有し、一緒に進歩しましょう。