`wrk`を使った正確なパフォーマンステスト
API7.ai
November 25, 2022
この記事では、パフォーマンステストについて話します。この部分は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=enforcing
をSELINUX=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
を使用する
c1000kはSSDB
の作者によるものです。名前からわかるように、このツールの目的は、あなたの環境が10^6の同時接続要件を満たすことができるかどうかを確認することです。
このツールの使用も非常に簡単です。server
とclient
を起動し、server
プログラムがポート7000
でリッスンし、client
プログラムがストレステストを開始して、実際の環境でのストレステストをシミュレートします:
. /server 7000
. /client 127.0.0.1 7000
すぐに、client
がserver
にリクエストを送信し、現在のシステム環境が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使用率に達することができれば十分です。
ただし、ストレステストの時間は短すぎてはいけません。数秒のストレステストは意味がありません。そうでないと、ストレステストがサーバープログラムのホットリロードが完了する前に終了してしまう可能性があります。同時に、top
やhtop
などの監視ツールを使用して、ストレステスト中にサーバーのターゲットプログラムが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ミリ秒以内に保ちたいと考えています。
さらに、wrk
はlatency
パラメータも提供しており、レイテンシのパーセンテージ分布を詳細に出力します。例えば:
Latency Distribution
50% 134.00us
75% 180.00us
90% 247.00us
99% 552.00us
ただし、wrk
のレイテンシ分布データは不正確です。なぜなら、ネットワークやツールの摂動を人為的に追加してレイテンシを増幅しているためです。これには特別な注意が必要です。
まとめ
パフォーマンステストは技術的な仕事であり、正しくうまくできる人は多くありません。この記事がパフォーマンステストについてより包括的な理解を提供することを願っています。
最後に、wrk
はカスタムLuaスクリプトをサポートしてストレステストを行うことができるので、そのドキュメントに基づいて簡単なLuaスクリプトを書くことができるかどうかという質問を残します。少し難しいかもしれませんが、完了するとwrk
が公開するインターフェースの意図を理解できるでしょう。
この記事をより多くの人と共有し、一緒に進歩しましょう。