SREの観点から考える定期的・継続的な負荷テスト

この記事は[SRE Advent Calendar 2019](https://qiita.com/advent-calendar/2019/sre)の24日目の記事になります。

## はじめに

こんにちは、[OPENREC.tv](https://www.openrec.tv/)でSREに所属している[@toro_ponz](https://twitter.com/toro_ponz)です。納豆が好きです。
今年の9月までアプリケーションエンジニアとしてサーバーサイドチームに所属していましたが、10月よりSREチームに所属することになり、Kubernetes回りの運用や既存インフラの改廃などを行っています。今期のOKRの内の1つに「負荷テスト環境の整備」というものがあり、自分なりに負荷テストについて調べる機会があったため、それをまとめてみようと思います。

## 負荷テストとは

Webシステムにおける負荷テストとは、そのシステムに対して多数のリクエストを送ることによって、システムが想定される性能を満たしているかどうか確認するテストのことを指します。
一口に負荷テストといえども、その種類はいくつかあります。後述する「負荷テストの目的」に則した負荷テストを計画・実施し、その性能を担保します。

* 性能テスト
    * システムが想定したリクエスト量を正常に処理できるかを確認する負荷テスト
    * 要件に合わせたワークロードの選出などに深く関わってきます
* ストレステスト
    * システムに想定以上のリクエストが到達した時にその挙動を確認する負荷テスト
    * スパイク時にユーザーに与える影響や機会損失に大きくかかわってきます
* ロングランテスト
    * システムに長時間多数のリクエストを処理させ続け、その挙動を確認する負荷テスト
    * たとえばメモリリークの有無など、短時間のテストでは顕在化しない項目を確認することができます
* ロードテスト
    * システムが想定するピーク帯のリクエストを送り、その挙動を確認するテスト

### 負荷テストの流れ

負荷テストは、まずどういった負荷テストを何を計測するために行うのか、という点を明確にしてから、それを実現するシナリオ・実行計画を作成します。
その実行計画に基づいて負荷テストの実施を行い、例えばレスポンスタイムやスループット、ワークロードのリソースをモニタリングし、計測します。
続いて得られた結果を、あらかじめ定めた目標値などに照らし合わせ、必要があればアプリケーションや構成の対応を行います。
そうして再び(必要であれば)目標値の見直しやシナリオの修正を行い、負荷テストを実施します。

このPDCAサイクルに基づいて継続的に負荷テストを行なっていくことが、システムの信頼性を損なわずに、本番環境に変更を適用していくために重要になります。

![pdca.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262177/6686a65e-421e-e3fe-0425-04e28091130a.png)
[Amazon Web Services負荷試験入門](https://gihyo.jp/book/2017/978-4-7741-9262-8)より作成

## 負荷テストの目的

負荷テストはただ単純に多数のリクエストを送信し、そのスループットやレスポンスタイムを計るために行うものではありません。そのシステムの要件に即した性能の目標値を決めそれを満たしているかどうかであったり、アプリケーションのスケーラビリティを確認したりするためなど、多岐にわたります。
負荷テストを行うにあたってまず始めに行うのが求められる要件から、負荷テストの目的・目標値を決めることです。目的や目標値を明確にしないまま負荷テストのシナリオ作成やその実施をしてしまうと、確認したい項目が計測できなかったり、要件に見合っていないデータしかとれなかったりと、テストが二度手間になることもあります。

## 負荷テストのツール

Webシステムに対する負荷テストを行うにはHTTPリクエストを送信するアプリケーションが必要です。一般的には、以下に挙げられるようなツールを利用して行います。詳しい解説はWeb上に素晴らしい記事がよくあると思いますのでそちらに任せる(というより比較できるほど使えていない)として、いくつか有名なものを挙げておきます。

### Apache Jmeter

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262177/f76623fa-9bf0-372a-4ce1-a9af68be5b3b.png)](https://jmeter.apache.org/)
出典：https://jmeter.apache.org/

JmeterはApacheによって作られている負荷テスト用Javaアプリケーションで、オープンソースで開発されています。
特徴としては、

* 豊富な統計レポートの作成や複雑なシナリオなど提供する機能が多い
* GUIアプリケーションからも操作でき、複雑な機能も比較的容易に利用できる
* 古くからあるツールのためWeb上の情報が豊富
* HTTP(S)だけでなく他のプロトコルでも負荷テストが行える

などが挙げられます。

### Locust

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262177/e969de1d-8810-cf30-c545-fffd18385a7e.png)](https://locust.io/)
出典：https://locust.io/

LocustはPythonで書かれた負荷テストツールで、以下の特徴が挙げられます。

* テストシナリオをPythonスクリプトで記載する
    * 取得したデータを次のリクエストに利用する等の処理も利用者のスクリプトでカスタマイズできる
* Web上から負荷テストの実施を行う
    * CLIのみで行えないわけではないが一般的にはWeb上から利用する
* 良くも悪くもシンプルで使いやすい

### Gatling

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262177/0d6bd9c3-fd77-b03e-5246-dbdcbbb5a3d8.png)](https://gatling.io/)
出典：https://gatling.io/

Gatlingは、ここ数年で注目されている比較的新しい、Scalaで書かれた負荷テストツールです。

* (Jmeterよりも)動作が軽快である
* テストシナリオをScalaで記載する
    * Locastと同じく柔軟なシナリオを作成できる
* 豊富な統計レポート、機能
* クラスターモードは[エンタープライズ版](https://gatling.io/gatling-frontline/)のみ

上に挙げた特徴があることもあり、最近ではJmeterよりも優れていると記載されていることも多く感じます。
(と言いつつ私は比較できるほど深く利用したことがありません。時間があれば使い倒したい。)

### 負荷テストツールの選定

負荷テストを行う上で、ツールの選定も1つの重要な要素です。目的に応じて適切なツールを選ぶことで、効率的にサイクルを回すことができます。
たとえばLocustは他のツールに比べてシナリオの作成やパラメーターが少なく設定が容易ですが、結果のレポート機能が他のツールに比べて劣ります。そのため、OPENRECでは視聴ページなどでの**レスポンスタイムやスループット以外の高負荷時の挙動確認**のために利用しています。
対してJmeterやGatlingでは豊富なパラメーターを設定でき、出力できる統計レポートも柔軟に行えるため、OPENRECでは**新規アプリケーションの性能テスト**などにJmeterを利用しています。統計レポートはHTMLで出力できるため、定期的にテストを実施し、S3にアップロードして比較できる様にすることも考えています。

## 負荷テスト結果の分析

負荷テストを実施した後はその結果の数値をもとに性能を分析し、あらかじめ定めた目標値を満たしていることを確認したり、場合によってはアプリケーションの修正等を行います。そうして必要に応じて再び負荷テストを実施する、というのが一連の流れになります。
使用するツールによって統計レポートの出力の有無やその内容が異なるため、用途に応じてツールを使い分けることも考えられます。ツールが出力するスループットやレスポンスタイムの情報だけでなく、CPU使用率やメモリ使用量、場合によってはスレッド数やコンテキストスイッチなどのシステムリソースの情報も必要になります。これらの情報は負荷テストツールでは計測することができないため、合わせて収集する仕組みを設けなければなりません。

## SREの観点での負荷テスト

『SRE サイトリライアビリティエンジニアリング - オライリージャパン』によると、SREチームが持つべき信条の1つに、 `サービスのSLOを下回ることなく、変更の速度の最大化を追求する` というものがあります。100%の可用性を求めるのではなく、例えば99.99%といったSLOを定め、残りの0.01%(エラーバジェット)の中で開発・リリースの速度を最大化することがSREに求められます。そのために段階的ロールアウトや一部トラフィックへのデプロイを行います。
負荷テストに関しても、これらの手法と同じようにサービスのSLOを担保する上で必要なことだと私は考えます。サービスのローンチ時やインフラの刷新時だけでなく、日々の開発でもアプリケーションに関する負荷テストを行うことで、システムの信頼性を上げることができるはずです。
例えば私たちのプロダクトは「ライブ配信プラットフォーム」のため、「会員登録」、「ログイン」、「TOPページの表示」、「配信ページの表示・視聴」、「コメント」などに関わる部分はサービスの要です。場合によってはビジネスクリティカルな変更が、リリースされてから高負荷時になって初めて顕在化するかもしれません。
もちろん全ての場合において負荷テストを実施し、それを通過した場合のみリリースを行う、というのは難しいですし、それを行うかどうかはプロダクトの特性や状態によって異なると思います。しかし、アプリケーションの変更によって性能に著しい劣化がないか、**デプロイする前に確認できる環境**はなくてはならないと、私は考えます。

## 負荷テストを容易にする環境

負荷テストを定期的、継続的に行ったり、必要に応じて任意で実施できるようにするには、負荷テストが行いやすい環境を整備する必要があります。アプリケーションの開発サイクルと合わせるのであれば、**Locustのシナリオファイルをアプリケーションのリポジトリに含めてCIで負荷テストを行える環境を整備する**ことも考えられます。SREチームが実施するSLI/SLOの運用に合わせて**毎週決まった時間に実行し、JmeterのHTMLレポートを比較する**、といったことも良いかもしれません。
また、先述した「負荷テストの目的・目標値」等に関しても、必要に応じて変更することが考えられます。SLI/SLOを合わせてこれらを運用していくことがSREの業務の一つとなるかもしれません。何れにせよ、負荷テストは一度実施すればその後の変更に対しての信頼性を担保できる訳ではなく、場合によってはSLI/SLOと同じ様に運用する必要がある要素なのです。

## まとめ

* 負荷テストはシステムの信頼性を確認するために目的を明確にして適した手法で行うことが重要
* 負荷テストは1度行えば終わるものではなく、システムの信頼性を担保するために継続的に行うことが望ましい
* 定期的、継続的に負荷テストを行う上で目標値やシナリオは場合に応じて変更していく必要がある

## 参考

* [SRE サイトリライアビリティエンジニアリング ―― Googleの信頼性を支えるエンジニアリングチーム](https://www.oreilly.co.jp/books/9784873117911/)
* [Amazon Web Services負荷試験入門](https://gihyo.jp/book/2017/978-4-7741-9262-8)
* [Jmeter とは](https://server.etutsplus.com/about-apache-jmeter/)
* [Locust コトハジメ](https://qiita.com/yamionp/items/17ffcc465272ad83c490)
* [初めてのGatling](https://qiita.com/syama1127/items/0c88b1afdad4b29bad5a)