こんにちは、コドモンプロダクトチームの中野です!
コドモンでは、2020年から既存プロダクトの機能リプレイスを進めています。今回は第二弾機能リプレイスでの負荷テストについて書きます!
目次
リプレイスの背景
既存のプロダクトはこれまで成長を続けてきましたが、一方でコードの責務が曖昧なところや、FatなController層などの技術負債があります。 今後もプロダクトを成長させユーザーの方々に安心してご利用いただくためには、それらがボトルネックになってくると考えているため改善にも力を入れています。
今回のリプレイスの概要
今回のリプレイスでは、あるサービスを対象にマイクロサービス化しました。具体的に行ったことは以下になります!
- フロントエンド・サーバーサイドを新しい技術スタックで書き換え、マイクロサービス化
- DBもモデリングし直して、既存のものと分割
- 既存のデータを新しいデータにパースしてマイグレーション
負荷テストの概要
負荷テストとは、以下を確認し、可用・性能・拡張・運用・保守性を担保するためのテストです。
- ソフトウェアが想定ユーザー数(通常時)の利用で十分なパフォーマンスを出せるか
- ソフトウェアが想定ユーザー数(ピーク時)の利用に耐えられるか
- ソフトウェアが想定以上のユーザー数の利用に耐えられるか
k6の紹介と選定理由
k6は手軽に実行できるオープンソースのツールです。負荷テストをJavaScriptベースで書くことができます。 k6.io
負荷テストを作成・実行するツールだとApache JMeterやVegetaなども認知度があるかなと思いますが、今回は以下の観点でk6を選定しました!
- 実行環境のセットアップが比較的簡単か
- スクリプトベースでテスト作成が可能か
- ユーザーの遷移をシナリオとするテスト作成が可能か
- ドキュメントが充実しているか
- サードパーティとのIntegrationをサポートしているか
テスト設計・実施までのプロセス
テストシナリオ作成
まずは想定しているシナリオを整理します。 ユーザーがどのような操作を行うかを想定しながら、その手順をシナリオとして作成します。 今回は以下のようなシナリオを作成し、k6で実行するためのテストスクリプトを作成しました。
テストシナリオA
- 「〇〇にログイン」
- ⇨「〇〇の一覧画面を表示する」
テストシナリオB
- 「〇〇にログイン」
- ⇨「〇〇の一覧画面を表示する」
- ⇨「絞り込みをして〇〇を表示する」
- ⇨「〇〇の詳細画面を表示する」
テストシナリオC
- 「〇〇にログイン」
- ⇨「〇〇の一覧画面を表示する」
テストの目的
上記で整理したシナリオに対してテストの目的を設定します。今回は以下のように設定しました。
- サーバーへの負荷が平常時に十分なパフォーマンスが得られるか確認する
- サーバーへの負荷がピーク時に十分なパフォーマンスが得られるか確認する
※平常時・ピーク時のリクエスト数は、コドモンで導入しているDatadogから参照しました
検証観点
ここまでできたら、あとはテストを実行できます。
しかし、目的が達成されたかどうかを判断するための判断材料と基準が必要になるので、事前にそれらも整理しておきます。
今回は以下を検証観点としました。基準については「リプレイス前後でパフォーマンスが極端に劣化していない」としました。
WEB/APPサーバー
- レスポンスタイム(平均・ 最大)
- スループット(平均・最大)
- HTTPステータス(5XX)の数
- CPU/Memoryの使用率
- apacheのbusy_worker数
- ログ出力
DBサーバー
- コネクション数
- DB Load
- スロークエリー
検証環境
どのような環境でテストするかを整理します。今回は以下のような環境にしました。 * サーバー * 台数:1台 * スペック:本番環境と同等
- DB(read)
- 台数:1台
- スペック:本番環境と同等
- データ量:本番環境と同等
実行
k6でテストスクリプトを実行します。今回は計12回の実施を行いました。
負荷テストはネットワークのI/Oやツール自体の性能などが結果に影響してくるため、複数回実施することで、より信頼性のあるデータが取れると考えています。
以下は、実際にk6でテスト実行をした際のアウトプット例です。
default ✓ [======================================] 000/500 VUs 57m0s ✓ response code was 200 checks.........................: 100.00% ✓ 134714 ✗ 0 data_received..................: 444 MB 130 kB/s data_sent......................: 38 MB 11 kB/s http_req_blocked...............: avg=740.45µs min=2.21µs med=7.19µs max=6.67s p(90)=20.05µs p(95)=35.38µs http_req_connecting............: avg=215.72µs min=0s med=0s max=6.39s p(90)=0s p(95)=0s http_req_duration..............: avg=112.78ms min=218.33µs med=54.23ms max=13.41s p(90)=118.05ms p(95)=172.13ms { expected_response:true }...: avg=112.78ms min=218.33µs med=54.23ms max=13.41s p(90)=118.05ms p(95)=172.13ms http_req_failed................: 0.00% ✓ 0 ✗ 134714 http_req_receiving.............: avg=177.94µs min=-21207163ns med=129.43µs max=31.42ms p(90)=312.18µs p(95)=432.04µs http_req_sending...............: avg=74.56µs min=-20351962ns med=54.51µs max=11.6ms p(90)=137.06µs p(95)=187.97µs http_req_tls_handshaking.......: avg=509.62µs min=0s med=0s max=5.84s p(90)=0s p(95)=0s http_req_waiting...............: avg=112.53ms min=0s med=53.98ms max=13.41s p(90)=117.72ms p(95)=171.79ms http_reqs......................: 134714 39.411217/s iteration_duration.............: avg=18.44s min=1.18s med=16.69s max=41.27s p(90)=34.18s p(95)=36.05s iterations.....................: 44896 13.134537/s vus............................: 1 min=1 max=500 vus_max........................: 500 min=500 max=500
結果・考察
結果
以下の結果が得られ、「リプレイスして切り出した機能は、平常時・ピーク時において問題なく利用できる」という結論を出せました。
- レスポンスタイムやDBコネクション数、Load数は、リプレイスした後の方がパフォーマンスが向上していた
- HTTPエラーもピーク時に0.01%以下に留まっていた
考察
今回上記のような結果が得られたのは、以下が要因としてあると考察しました。
- リプレイスによってマイクロサービス化されたので、その分リソースに余裕ができた
- リソースに余裕ができたので、パフォーマンスが上がった
今回のテストをふりかえって
k6を初めて使用しましたが、JavaScriptベースで書けるので個人的にはとても楽でした。
当然ではありますが、リリース前にきちんと負荷テストをすることによって「安心感」が生まれますし、何より問題なくユーザーがサービスを使えることを担保できるので、コストは掛かりますが、それ以上にリターンが大きいと思いました。
また定量的なデータが取れるので、ステークホルダーや他エンジニアメンバーとも考察ができ、リリースジャッジの判断材料としても使いやすいかと思いました。
まとめ
今回はk6を用いて負荷テストを行った事例を紹介しました。
結果的にリプレイスという規模の大きなリリースを安全に行うことができ、知見も得られました。今後も今回以上に安全なリリースを追求し、「可用・性能・拡張・運用・保守性」を担保・品質向上させるための仕組みを充実させていきます。