Webアプリ負荷試験ガイド

Webアプリ負荷試験ガイド

目次

前置き

Webアプリの負荷試験について書きます、それ以外の事は取り扱っていません。

一般的な Web/RDB/KVS なアプリを前提として書いています。

数万DAU程度でサーバ数十台な規模感を想定しています。

各個別の項目についてはざっくり書きますが掘り下げてはいません。

ざっくり試験の流れや注目点を書いた手引書的なものを目指して書いています。

新規プロジェクトでリリース前のものを想定して書いていますが、まともに負荷試験をしていないプロジェクトや稼働中のプロジェクトで高負荷に陥ってる場合にも参考になると思います。

時間がない人向け要約

  • スケジュールは十分に確保
  • サーバ1台に負荷掛けて限界点を調べた上で複数台に負荷掛ける

about me

六本木の会社で日々Webアプリのトラブル対応や負荷試験の監修とかしてます。

何故負荷試験を行うのか

状況によって異なりますが、凡そ次のような目的で実施するはずです

  • 大前提として目標rpsを出すため
  • アプリケーションのパフォーマンス計測、ボトルネックを調査するために行う。
  • 複数台構成で並べた的に適切にスケールするかの確認。
  • 許容されるレスポンスタイムで目標のrpsをさばくために何台のサーバが必要となるかを試算・計測する。

負荷試験ツール

負荷試験を行うにあたって知っていると便利なアプリ・サービス

負荷掛けるツール

Apache Benchは複雑なシナリオには不向きですが、単純な参照系APIの簡単な試験には使ったりします。*1

Jemeterは定番で負荷試験のシナリオは大半Jmeterで出てきます。

私の最近のおすすめはLocustです、シナリオが書きやすく簡単に分散環境で実行できます。

数万DAU規模の負荷を掛けるのであれば最初から分散実行を想定して下さい。

負荷計測

  • sar/top/iostat/etc..
  • Grafana/Prometheus
  • Datadog

cliはお手軽ですが記録出来てあとから見直せるものが良いです。 手に馴染んでるものなら何でもいいと思います。

負荷の可視化

Newrelic APMがお勧めです、特にphp環境では特別な改修も不要で導入するだけで非常に詳細な情報を取ることが出来ます。

負荷試験の流れ

よくある駄目な流れとして、作成・動作確認程度しか行っていないシナリオで一度に複数台のサーバを並べて1度だけ(短いスケジュールで)流そうとする人たちが居ますがお勧めできません。 *2

負荷試験は基本的に次のような流れで行ってください

  • シナリオ作成 参照
  • サーバ1台に対して掛けて1台辺りのrps/response timeを計測 参照
  • サーバを数台並べてresponse timeが悪化することなくrpsがスケールすることを確認
  • サーバを数十台並べてDBやKVS等のボトルネックを計測する 参照

これらの工程の合間合間にシナリオやプログラムの修正作業が随時入るはずです。

最初から複数台並べて大量の負荷を掛けようとすると、適切なrps/response timeの算出が大変ですし、複数台故の問題なのかの切り分けも大変になります。

まず1台で問題がないことを確認出来たものを複数に対して掛けてください。

負荷試験スケジュールについて

上でも触れていますが、1度流しただけで完了できるスケジュールが多いです。

一度で問題なくテストが終わり、目標のrpsを達成出来る前提となります。

2度目以降の試験であれば問題ないのですが、1度目であればそのようなスケジュールを取るべきではありません。

1度目の試験ではシナリオを正常に流せることが希です、多くの場合シナリオ改修に時間を取られます。

シナリオ自体が正常に動作したとしてもその状態で満足な負荷を掛けられることも希です、サーバ設定やプログラム改修を行う必要があります。

改修を行えば計測をやり直す必要があります、個別のコントローラへの改修であれば工数は少ないかもしれませんが共通部分に手を入れた場合は最初から全て計測の必要が出てくる事もあります。

そして改修計測は繰り返し行われます。

負荷試験の流れで解説した各項目1w確保すべきです、シナリオ改修を繰り返すことはめったにありませんが、改修・計測フェイズは何度繰り返すかはアプリに依存します。

ここまでの流れを読めば解るように、負荷試験をリリース直前に配置しないで下さい。もし最後に持ってくるのであれば十分な期間を設けて下さい。

大半のプロジェクトではプロジェクト大詰めの最後に負荷試験を配置します、それも1wのようなスケジュールで用意されることが多いですが、今までの流れを読めば解るようにそのようなスケジュールは最初から破綻していることが多いです。

注目すべきポイント

シナリオ作成

シナリオは個別の案件に依存することが多くざっくり述べるのは難しいのですが、殆どのシナリオで注意できる点をいくつか述べます。

本項目と併せて アプリの正常性の確認 も確認して下さい。

アカウント情報は自動生成出来るようにする

csv等で一定のユーザを与えたりするケースが見受けられますが、アカウント情報がスケールしないことが多いです。

100人データを作って1万人を想定する場合はそれらを使いまわしたりしますが、同じレコードに対してLockを取って失敗したり、同一の情報を参照するためキャッシュが想定より効いてしまったりします。

例えばプレゼントBox肥大化の検証等で同一のユーザで繰り返す必要があるシナリオでない限りはユーザデータは自動生成出来たほうが良いです。

つまりアカウント情報については リストで受け取る or 自動生成する の二通りで実行できることが望ましいです。

UID発行=>チュートリアル実行=>初回ボーナス付与、のように正常なフローで生成出来る場合は良いのですが、デバッグツール(API)等を使って前記のシーケンスを一度に実行したりする場合は負荷に気をつけてください。 そのような場合はsetup(pre-warmup)等で計測とは無縁の場所で発行すべきです。

DB分割を行ってる場合はDB分割を意識したシナリオを用意する。

前項目と若干被るのですが、シャードに対して適切に負荷を掛けられない場合があります。

例えばUserDBをN分割している場合に十分に散っていないユーザIDのリストを与えて試験を行った場合、特定のDBにしか負荷がかかりません。

上記のような場合は前項目のように自動生成出来れば自動生成ロジックが間違ってない限りは分散するはずです

全ての項目について適切に分散してデータを生成出来ればよいのですが、どうしても自動生成しにくいデータもあるはずです。例えばギルドはどうしても固定で生成しなくてはいけない(難しい)場合にはギルドIDで分散してるのであれば適切に散るようにデータを意識して下さい。

負荷試験

jmeterやlocustなりの負荷試験元の監視を忘れず行って下さい。

数千DAU程度の負荷なら良いのですが、万を超えるオーダーの場合よく負荷掛ける元が詰まってる事があります。

memory不足は負荷ツールがエラーを吐いて停止することが多いですが、CPUやTCP接続数不足によるスローダウンはエラーにならないことが多く見落としがちです。

http or https

開発時でもhttpsを推奨しますが、負荷試験時は可能であればhttpの利用をお勧めします。

負荷掛ける側のCPUに影響したり、tlsがLB終端だとしても接続がstickyされてしまって特定のLBに偏ったり等が起きたりします。

理想はhttp/httpsどちらでも負荷を掛けることです。

サーバ1台

必ず本項目の前に前の2項目に目を通して下さい。

1台ではrdb/kvsに問題が起きない前提で書いています。

ここではLB経由しない状態で掛けるべきです、LB経由したときに問題が起きた場合の問題の切り分けに役立ちます。

サーバ単体での負荷

Webサーバは基本的にcpu usage/load averageを見ましょう。

ざっくり cpu usageは100%未満でload averageはコア数と同程度までとすればよいはずです。

cpu usage/load averageが上記まで到達しない状態でresponse time/rpsが頭打ちするときは次を確認してください。

  • アプリの実行数が上限に達していないか

phpならapache/php-fpmだったり、rubyならunicorn等それぞれの言語のworkerのprocess/thread数です。

laやcpuが余ってる状態なら増やしましょう。

rpsが非常に出てたり、バックエンドへの接続が非常に多いようなアプリの場合TCP数が上限に達することがあります。

port rangeの拡張や net.ipv4.tcp_tw_reuse=1 にして対応しましょう。

都度名前解決してしまって問題になる事が多いです。

適切にDNSキャッシュを導入したり . 終わりにして無駄な検索を減らしたりすることを推奨します。

パブリッククラウドで顕著に現れますが、オンプレでも起きうる問題です。

最近ではコンテナが流行りで小さめのインスタンスを大量に並べるパターンも多く、以前より問題となりやすいです。

アプリの正常性の確認

試験用データが間違っていたり、開発環境と違い複数のユーザで同時実行した際の不整合等でアプリが正常に動作してない事が多々あります。

1件1件シナリオ作成時は画面を見ながら作るので良いのですが、負荷試験はheadlessで実行するため異常に気付けず試験を続けてしまってることが多いです。

これらを回避するために次の点に気をつけて下さい。

  • response statusが問題ないか(まれにstatus:200でも中身はerrorだったりします)。
  • レンスポンスサイズが一定ではないはずの応答結果(例えばmypage)が常に同じcontent-lengthで返っている。
  • シナリオで戻り値をちゃんと検証する

戻り値の検証は割と漏れています、特にjmeterではサンプラーエラー後のアクションのデフォルトが続行となっているため見落としやすいです。

サーバ複数台

ここに来るまでにWebに関しては既にある程度確認できてる状態のはずなので、KVS/RDBの様子を見ます。

基本的にKVS/RDBは可能であれば持続的接続を利用するようにして下さい、高負荷の場合では接続数/cpu usageが問題になることが多く大幅に負荷を削減することが可能です。

持続的接続を導入した場合、KVSであれば大きく問題になることはありませんがRDBの場合はトランザクション周りで問題が出ることがあるため、トランザクションを利用している場合は導入に注意が必要です。

それでも導入は検討すべきで、持続的接続に関する資料としてはphpになりますが次の資料がお勧めです

RDBへの持続的接続の導入に関しては上記のように懸念事項も多く、オンプレ時代はmayぐらいで出来ればば好ましいぐらいの感触でしたが、パブリッククラウド全盛の今ではshouldぐらいの感触です、費用で泣きたくなければmustでも。

専用のミドルウェア(mysql routerやtwemproxy)でもいいですが、多くの場合各言語に実装が用意されているはずです。

特に最近ではパブリッククラウドを利用することが多くネットワーク品質がオンプレに比べて低いです、持続的接続を利用すれば大幅なパフォーマンス改善が見込めます。

KVS

memcached/redis を使い分ける前提で書いています。

Memcached

サーバプールを増やすことでキャッシュ容量やコマンド実行性能はスケールはできるはずなのですが、往々にして利用方法で問題があり増やせないことが多いです。

接続数やコマンドが正常に分散しているかを確認してください。

特定Keyの利用が偏っていて特定サーバに負荷がかかるような利用をしている場合、1ノード辺りのスペック上限が頭打ちになります。

良くあるキーの偏りとしては次のような物が上げられます

  • マスタデータ
  • メンテナンス情報
  • 最新イベントの情報
  • ユーザ情報をキャッシュさせていてランキング上位ユーザの情報

これらは適切に散らせるようにするか、可能であればlocal cacheを利用すべきです。

偏りの調査は tcpdump でkeyの集計や、memcached側でcmd rateや転送量を注視して下さい。

Redis

Redisはマスタに関してはスケールアウトが出来ません、Redisに格納するようなデータはデータ分割にも不向きなことが多く基本的にアップ戦略になってしまい上限が比較的早く訪れます。

ほんとうにそのデータはRedisに格納する必要があるのか?Memcached等他のストレージで済まないか検討すべきです。

良くあるパターンとしてランキングでRedisを利用しているのでそのままキャッシュストレージとしてRedisを使ってしまうことがありますがお勧め出来ません。

多くの場合メモリサイズよりCPUの頭打ちが訪れるはずです。

メモリサイズで問題になる場合は実データを丸々置かないようにし、、基本的に数値や格納先のid以外は保存しないようにすることによって削減出来るはずです。

接続数やmultiコマンドやluaによってCPUの頭打ちが訪れることが多いと思います。

非常に良いまとめだと思うので参考リンクを貼っておきます

RDB

数万DAUを想定しているためmaster複数slave構成前提で書いています。

Masterに対する負荷は水平分割出来ない限りスケールアップでしか対応できません、数千DAU程度であれば垂直分割で対応可能ですが、万を超えるDAUを想定する場合は水平分割を必ず導入して下さい。

RDBで問題点をざっくり分けると cpu usage/接続数/メモリ/ストレージ容量/ストレージ性能 となります、そして Master/Slave 、Write/Read負荷となります、それぞれで問題を分けて考えるべきです。

多くの場合まず最初に問題になるのは接続数/cpu usageです、slaveに関しては並べて対応可能です。

上記を解決した場合次に問題となるのはwrite性能です、write性能に関しては次のような方法で対応できます

  • bulk insert
  • DB分割
  • 非同期書き込み*3

bulk insert/DB分割は必ず導入すべきです。

非同期書き込みはアプリの作りにも左右されることが多く、製造初期から検討しないと前2個に比べて導入が難しいです。

次に問題となるのはメモリ及びストレージです、基本的には水平分割で対応して下さい。

問題になりやすいDB
  • マスタ系DB

参照負荷が問題になります。

必ずキャッシュすべきです。

slaveを追加での対応も可能ですが、キャッシュを適切に利用して対応すべきです。

  • ユーザ系DB

数万DAUを想定する場合は必ず水平分割を導入すべきです。

  • ログ・調査系DB

ユーザ系DBは分割しているのにログや調査用DBが水平分割されていなくて問題になるパターンをよく見ます。

そもそもRDBへの保存はお勧めできません、これらは可能であればログファイルに書き出してtd-agent等で送信出来るようにするのがベターです。

エンジニアリソースや慣れたものを使いたいなどの理由があってRDBを使いたい場合はユーザデータと同様に分割をすべきです。

キャッシュの話

キャッシュするレイヤーは大まかに分けると次のように考えられます。

  • CDNやproxyレベルのHTTP層
  • アプリ層
    • Appサーバローカル内での格納
    • Appサーバ外での格納
  • RDB

大前提

キャッシュは可能な限りして下さい、10秒や5秒最悪1秒でも良いです。

1秒でもキャッシュ出来れば数千リクエストされたとしてもストレージへのアクセスは1で済みます。

キャッシュの削除(管理)をし始めると難易度が跳ね上がります、削除ではなくTTLを短くして(更新したら削除ではなく、前記の秒単位キャッシュを検討する)対応できたほうが難易度は低くお勧めです。

注意すべき点

キャッシュすべきデータは基本的に整形済みデータを保存して下さい。

多くの場合dbのデータをそのまま保存して取り出す度に整形していることが見受けられますが、整形のコストも馬鹿にはならないため整形済みデータを保存して下さい。

CDNやProxyレベル

数万DAUを想定する場合CDNは必ず導入しましょう。

共通で使われるデータは必ずキャッシュすべきです、マスタデータやメンテナンスフラグで用いられるjsonだったりhtmlで返される更新情報等です。

頻繁に更新されない静的なリソース類、もしくは動的なものでもリアルタイム情報でなく、ユーザ情報を必要とせず生成できるのであればCDN/Proxyでのキャッシュを検討しましょう。

この層でキャッシュができればapp側にリクエストがほぼ行かなくなります。

local cache or remote cache

マスタデータ等の頻繁に参照しデータサイズの大きなものはローカルキャッシュに格納すべきです。

マスタデータをリモートに格納するとkeyの偏りやネットワーク転送量が問題なることが多くおすすめ出来ません、そしてローカルに格納することでそれらの問題が発生しにくなり難易度が下がります。

キャッシュヒット率を考慮し保存先を決めて下さい。

一般的なLBでRoundRobinで振り分け構成の場合、各ユーザに属するデータはローカルに保存するとヒット率が悪いためリモートサーバに保存すべきです

local cache or memory cache(in app cache)

ローカルに保存出来てればおおよそ問題は無いのですが、問い合わせ回数が数十回を超えて多い場合はアプリ内部に保持したほうがいいです。

ローカルに保存できていたとしても場合によっては 10-20msec程度掛かってる事をよく見かけますが(集計関数ではなく単純なget等で時間がかかっている場合)同じ実装でもメモリ内キャッシュであれば数msecで済むことが多いです。

references

更新情報

  • 2020/11/09 初版公開
  • 2020/11/11
    • Web負荷試験にタイトル変更。
    • スケジュールに関する注意点を追記 diff
    • シナリオに関する項目を追記 diff

*1:LBがスケールするほどの負荷を出すのは単体実行では難しいため、あくまで簡易なテスト向けです。

*2:9割はそのような予定で建ててきます

*3:ログに書き出しあとから書き込こんだり、job workerに渡して書き込み数を制限したり

S.Yairi YM-02 / ヤイリ ミニアコースティックギター 1万円ミニギター所感

最近なんかやることないかなーと思い立って、6月ぐらいからギターを始めたのでギターカテゴリが新設されました、まぁめったに更新されない気がするけど…

あー、アコギ弾きたいなーって唐突に思ったので、その時に買ったのはYamahaの FG-830 。

一昔…二昔*1ぐらい前にも少しやってたのでコード辺りは一通り覚えてて*2何となくは弾ける勢。

でもアコギって生活時間的に週末ぐらいしか弾けないんだけど、幸いにして近所の馴染みの飲み屋とで客居ないときに弾いて良いよーって話に、何度か持ち運んで楽しんでるんだけど、流石に毎度運ぶのは近所といえ流石に辛い。

置きギターしても良いよって事なのでなんか一本欲しいなーと、誰かに弄られて壊されても良いやって思える値段でなんかないかなーと調べたらヤイリのミニギターがいいよ的な感じだったしまぁ値段も1万しないぐらいなので取りあえずポチっとな。

届いて開けて笑ったのが軽っ、そしてベニヤの匂いがするwwww

f:id:withgod:20180818134412j:plain:w250

ってか指板色分けしてるだけじゃん!!!ってwww

f:id:withgod:20180818134451j:plain:w250

取りあえず適当に試しに弾いてみた感じ、弦高が高め(その辺にあった精度の悪い定規で測った感じ3mmちょい?)だったので取りあえずネックでちょいいじって2.5mmぐらいに。

弦の滑りも悪いというかまぁそもそも最初から付いてる弦なので何時も使ってるElixirのコーティング弦に変更。

1万のギターに1500円の弦ってどーなんだろーと思いつつも置きギターにはコーティング弦だよなーと…w

で、変更しようと思ってブリッジピン外してたら6弦のピンかてーなーと嫌な予感がしつつ抜いてたら早速折れてしまった…

1万のギターに変に高いピンを買いたくなくて安めのを購入とはいえ900円ぐらい、別に1000円違おうが良いんだけど気持ち的な問題があるw

後はやっぱ安物ギターにありがちなペグが渋い症状が1個だけあったんだけど、分解掃除はめんどくさいというか手元にグリスが無かったのでシリコンスプレー適当にふったらちゃんと回るように。

ってことでたいしたことはしてないのだけど、取りあえずここまでやって使用感に特に問題の無いギターになりました。

まぁなんか不満げに書いてるけど、そもそも1万円なので何も文句言えないよなーってなるんだけどね。

自分は元々機械弄り好きなのでギターは初心者でも自分でメンテ出来るけど*3、そうじゃない人にはちょっと辛い気もするというかネットじゃなくてお店で買った方が良いかな感。

何故かタイムリーに瀧澤先生が格安ギターの動画を上げてたんだけど、フルサイズのギターで更に安いならそれでも良かった感あるかもw

流石にミニギターにピックアップ付けるの無理あるだろうしなー…


ギターレッスン【超激安アコギを瀧澤が弾くとこうなる】ZENN ZS18のご紹介

まぁこの紹介動画の後で件のギターは何故かWebから消えたみたいだけど…

*1:一昔=10年だと思ってるw

*2:指が動くわけではない…以前にもギター買ってろくにやらずに積みギターになってたりする...

*3:1万円だし思い切って出来るってのもある、これが10,20万するギターなら店出すかなw

D2 Clan Activity List を公開しました

最近はDestiny 2で光の戦士です。

自分の入ってるクランがほぼ満員で人員整理が必要になってきてるんですが、マスターが整理の為に

  • 「一覧で見る方法が無いからクランページから皆のオンライン状況をメモる」
  • 「プロフィールを個別に見て最終オンラインをメモる」

って言ってて可哀想なのでcsvで一発出してみたんですが、せっかくなので誰でも使えるようにしてみました。

D2 Clan Activity

  • D2 API を利用してクランメンバーのラストログインを表示します。
  • ログインしたユーザの所属クランのみです。
  • ログイン時にメンバ一覧の最終更新時刻を見て、1時間以上前なら更新を行います。
    • 1時間以内の場合は更新されません。
  • tokenはユーザセッションには保存していますが、rdbへの永続化は行っていません。
  • ソースコードgithubに置いています。
  • <b>ユーザが削除された場合にリストから消える機能が実装されていません 対応しました。
    • 自分のクラン無いので試せないんです、消した場合は連絡頂ければ対応します。
  • 同一クラスを複数キャラ持ってる場合は、最後にプレイした方を記録します。

初夏に牡蠣とレモン。

毎冬知り合いの牡蠣猟師で牡蠣を頼んでるのですが、夏に向けて牡蠣如何ですかというDMが来たので頼んでみることに。

何個ぐらい頼むかー、一人最低5個は食べたいよなーとか5個じゃ足りないとか言う奴が居たりして足りないのが一番寂しいので100個頼むことに。

佐川から電話連絡あり。

「本当に自宅で宜しいのでしょうか?お店と配送先間違ってませんか?」

と…

続きを読む

今年の浴衣をどれにしようか男性編

タイトルは釣りですスイマセン、私は去年作った浴衣着ます。

id:garamania さんが浴衣選びで盛りあがってるエントリを上げていて、まず思ったのが「そんなに選ぶほどあるんだ、女性物すげーな」って。

続きを読む

持ち込み可能なお店でのBBQパーティー

少し前なんですが、会社でBBQのお誘いを受けて開催概要を聞くと持ち込み可能なBarらしいのでこれは何か作って持っていかなければ男が?廃る。

勿論こういうときはAnovaの出番だと言うことで、近所の品揃えの悪いスーパーではなくハナマサへお出かけ。

精肉コーナーを見繕って目についたのがヒレとロースのこの2点

f:id:withgod:20170413105330j:plain:w250 f:id:withgod:20170413105336j:plain:w250

近所のスーパーだと高い上に品揃えが悪いからこのサイズはそもそも売って無く、ほとばしる肉感に感動して無駄に夜中にこんな写真を取ってる30代会社員男性…(金曜夜だったので木曜の夜中に仕込みをしていました。)

f:id:withgod:20170413222207j:plain:w250 f:id:withgod:20170413222233j:plain:w250 f:id:withgod:20170413222229j:plain:w250

一通り目を潤わせたところでいつも通り

f:id:withgod:20170413223208j:plain

肉が多いので予め浸して漏れないように水量を減らした所anovaがwater level too lowの警告を出すので最初からぶち込んで調理開始した所。

f:id:withgod:20170413224029j:plain

かなりでかいサイズになって持ち運びが大変だったので、でっかいタッパーにziplockごと放り込んで、こういう時は風呂敷が活躍。

f:id:withgod:20170414200823j:plain

仕上げの焼入れは現地で行い

f:id:withgod:20170414204642j:plain

包丁を入れたらこんな感じ

f:id:withgod:20170414205101j:plain

で、ここら辺りで切ったり盛ったり焼いたりしてて写真が残ってませんw

やっぱ肉はでかい方がテンションあがるし、現地で加工を行うのは周りのテンションも上がるので良かったなーと思ったりしながら次回もまたやろーっと。

タルトを焼き始めました。

シフォンケーキ一段落ついてから特にこれと言った物に打ち込んでなかったのですが、やっぱタルト焼くかって事で一念発起。

前にフィナンシェ作ろうかなーと思って買っといたアーモンドプードルもあるので丁度良いやと言うことでid:buchineko_okawariさんお勧めの本を買って挑戦。

タルト 私のとっておき

タルト 私のとっておき

続きを読む