Enjoy Architecting

Twitter: @taisho6339

これだけは知っておこう負荷試験 ~その1 基本とお試し試験~

記事の概要

負荷試験はシステム運用において避けて通れないタスクであるが難易度が高いタスクでもある。

本記事ではまず、実際にWordpressに対して簡単な負荷試験を行いながら、 負荷試験における基本的な観点を整理していく。

負荷試験の主な目的

負荷試験は何のために行うのだろうか? 主な観点としては下記のような項目が挙げられる。

  • 負荷に耐えうる構成か?
    • システムが想定した負荷に耐えられることを確認する
    • 長時間負荷をかけ続けた場合にメモリリークなどの潜在的なバグがないかを確認する
  • 要件に対して最適な構成になっているか?
    • 突然スパイクしたときにも自動でスケールし耐えられる構成になっているか?
    • 通常時に必要以上にインフラリソースを確保していないか?
  • スケール特性を把握する
    • まずどこにボトルネックが来て、どこをチューニングすればシステムがスケールするかを確認する

負荷試験の大事なルール

  • 必ずどこかがボトルネックになっている状態を作ること

  • スコープを絞ってステップごとに区切って実施すること

    • 問題の原因特定を効率的に行うことができるため、少しずつ負荷のかけ方を変えながら実施する
  • ネットワーク的に近いところから攻撃する

    • クラウドであれば同じVPC内から攻撃するのが望ましい
    • ネットワーク環境は千差万別であり、コントロールできないため、純粋にシステムの負荷を可視化したいのであればネットワーク起因の問題は排除した上で試験するべきである
  • 最初は最小の構成 & 自動スケールしないようにして実施する

    • ボトルネックがどこにあるかがわからない状態でオートスケールする構成で実施してしまうと、ボトルネックの特定が困難になる。よって、スケール特性を把握するフェーズで初めてオートスケールするようにする。

負荷試験をする上で参考にする性能指標

負荷試験実施の際は、下記指標を確認していき、問題があった場合はCPU使用率やメモリ使用率、コネクション数などの細かい指標を追っていくことになる。

  • スループット

    • 単位時間あたりどのくらいの処理をさばくことができるのか
  • レイテンシ

    • 処理をリクエストして、結果がクライアントに返ってくるまでどのくらいの時間がかかったか

実際の負荷試験のステップ例

  • 攻撃サーバのセットアップ

    • 最大攻撃性能を把握することで攻撃のパフォーマンスが十分に発揮できているかを確認する。
  • フレームワークの素の性能の把握

  • DBへの参照を含めた性能の把握

    • DBとの接続方法、クエリの実行方法などが適切に設定、実装されているかを確認する。
    • 前工程と比較してDB参照に大幅な劣化がある場合は、下記観点で確認する。
      • Web, DB, 攻撃サーバで負荷が正しくかかっているかを確認する
      • バッファプールにデータが乗り切っているか、キャッシュヒット率はどのくらいか
      • コネクションを永続化できているか確認する(コネクションプーリングの設定)
      • アプリケーション側でN+1やスロークエリなどが出ているか
  • DBへの更新を含めた性能の把握

    • DBとの接続方法、クエリの実行方法などが適切に設定、実装されているかを確認する。
    • 前工程と比較してDB参照に大幅な劣化がある場合は、下記観点で確認する。
      • Web, DB, 攻撃サーバで負荷が正しくかかっているかを確認する
      • 必要以上に偏ったユーザデータのシナリオになっていないか? ※例えばテストユーザを一人だけにして更新テストを行う場合、ロックの傾向が偏ってしまい、本番のシナリオと剥離してしまう。
      • 不必要なロック、トランザクション分離レベルなどを見直しつつ調整
  • 外部サービスとの通信を含めた性能の把握

    • 外部サービスとネットワークを経由して通信しているケースの負荷シナリオ
    • 自サービスの管轄外の場合、ここがボトルネックになりやすい
    • 負荷をかける場合は、意図せず外部サービスに過負荷をかけてしまわないよう気をつける
  • 実際の使用を想定したシナリオでの性能の把握

    • 実際のユーザのシナリオを想定し、負荷をかける
    • このシナリオでの負荷試験でしっかり想定どおりのパフォーマンスがでるかどうかを検証する
  • スケール特性試験

    • リソースが想定より余裕がある場合はスケールイン、スケールダウンして試してみることで必要以上にコストがかかっていないかを検証する

    • 負荷をあげていったときにどこがボトルネックになり、実際にそのボトルネックをスケールアウト、スケールアップ、パラメータチューニングにより解消したときに、システムが想定どおりスケールすることを確認する

実践してみよう!

環境

この環境で出せる攻撃時スループットの上限値を知る

現時点で、MAXでどのくらいのスループットがでるのかを検証するため、 一番処理の軽い静的ファイルを返すエンドポイントに対し、 Apache Benchを使って負荷をかけてみる。 ネットワーク的な要因を排除した純粋なスループットを計測したいのでホストから実行する。 ネットワークはスループットをかなり落としてしまう要因になるので、実際の負荷試験においてもなるべくネットワーク的に近いところから試験することはかなり重要になってくる。

ab -n 20000 -c 200 -k http://10.108.252.89/readme.html

大体この数値がこの環境における攻撃時に出せる最大のスループットということになる。

Requests per second:    7711.03 [#/sec] (mean)

検証の妥当性

この結果が妥当であることをどのように判別すればよいだろうか? まず最初に見るべき観点は以下になる。

  • 対象システムのCPU使用率が100%近くまで上がっているか?

    • ※DBサーバへの負荷の場合はもう少し下がることが多い
  • 攻撃サーバ側のCPU使用率は100%近くまで上がっているか?

    • 余裕がある場合は、同時接続クライアント数が足りていない

極端に同時接続クライアントが多すぎる場合スループットには出ないが、レイテンシが極端に落ちる。(リクエストが実行されるまでの待ち時間も含まれてしまうため)

こうなると問題の切り分けが大変になってしまうので、同時接続クライアント数は多すぎず、少なすぎない状態で実施するのがベスト。

(十分に負荷がかかっているのに必要以上にクライアント数をあげない)

今回は静的ファイルのため、Webサーバ単体への負荷となり、WebサーバはCPU使用率が90%以上で張り付いていたため、 十分に負荷をかけられていると判断した。

Wordpressの実際のスループット

今度は静的ページではなく、MySQLへのアクセスを伴う実際のページにアクセスしてみる。

ab -n 20000 -c 200 -k http://10.108.252.89

スループットは下記まで落ち込んだ。

Requests per second:    30.93 [#/sec] (mean)

また、DBもWebもCPU使用率90%以上で張り付いているので負荷はしっかりかかっていると見て良さそうである。

DB側がボトルネックになっていそうなので、DBのPodをスケールアップしてみる。

DBをスケールアップしてみた後

もともとが1コアを割り当てていたので、2コア割り当てるようにしてみた。

kubectl describe node

  Namespace                  Name                                CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                ------------  ----------  ---------------  -------------  ---
  default                    wordpress-5bbd7fd785-gswtm          0 (0%)        0 (0%)      0 (0%)           0 (0%)         22h
  default                    wordpress-mysql-88898b8ff-jhj9g     2 (50%)       2 (50%)     0 (0%)           0 (0%)         22h
ab -n 20000 -c 200 -k 10.108.109.63

両者ともにCPU使用率は90%以上で、スループットが純粋に向上した。

Requests per second:    70.14 [#/sec] (mean)

ただ、またもやDBもWeb側もCPUが90%以上になっているため、 まずDB側をスケールアップしないとスループットは上がらなさそうである。 今回はアプローチを変えてDBを一定時間キャッシュさせ、DBアクセスをしないようにしてみる。

DBキャッシュ適用後

DBキャッシュを有効にした結果、スループットが劇的に改善している。

Requests per second:    3120.29 [#/sec] (mean)

DB側のCPU使用率も一瞬はねたが、一気に下がった。 一方Web側は変わらずCPU使用率が90%超えであり、 ボトルネックがDBからWebに移ったのが分かる。

まとめ

ボトルネックがWeb側に移動したのでWeb側をスケールしたいところだが、ローカルの貧弱な環境だと限界なのでローカルでの試験は一旦ここまでとする。

基本的な流れをこのさきのステップも同じで、

を繰り返していくことになる。

次回はKubernetesにアプリケーションをデプロイし、複数ノード & 攻撃サーバも負荷対象システムもスケールできる環境で実際にやってみる。

~ TO BE CONTINUED ~