【学習メモ】 Webパフォーマンスチューニング ISUCONから学ぶ高速化の実践
Chapter 1 基礎知識 高速であることとは Core Web Vitals Googleが検索エンジンのインデックス付けに利用している指標 3つの評価軸がある LCP (Largest Contentful Paint) ページロードのパフォーマンスを計測する指標 一番大きなコンテンツが表示されるまでの時間 2.5秒以内 にするのが好ましい FID (First Input Delay) 双方向性を計測する指標 ユーザーが最初にページを操作してから、ブラウザがその操作に対応した処理を開始するまでの時間 100ミリ秒を下回るようにする CLS (Cumulative Layout Shift) 視覚的な安定性を計測する指標 累積レイアウトシフト(一度表示された内容が移動しているかどうか) 0.1を下回るようにする 他にFCP (ブラウザがコンテンツの描画を開始するまでの時間)、TTFB (操作してからレスポンスの受信を開始するまでの時間)などがある 高速なシステム→コスト対性能効率が良い!エネルギー対性能効率も良いのでECO! 高速なWebサービスとは 「Webサービス利用者(クライアント・ベンチマーカー)のリクエスト送信開始〜Webサービス利用者のレスポンス受信完了」までの所要時間が短いWebサービス このような待機時間をレイテンシ (Latency)と呼ぶ 単位は時間で、msec (ミリ秒=1/1,000秒)かμsec (マイクロ秒=1/1,000,000秒) を用いる リクエスト送信開始〜レスポンス受信完了までの一周の所要時間の長さをRTT (Round Trip Time)と呼ぶ LCPの2.5秒以内はコンテンツ取得・パース・レンダリングなどを含めた時間であるため、実際のWebサービスとしてRTTは1秒以内に収めるべき 同システムで受け付ける多様なリクエストを俯瞰して、多様なリクエストを高速に処理することが求められる 同時並行で行われる大量のリクエストを少ないシステムリソースで処理することが求められる この同時並行処理性能をスループットと呼び、単位は単位時間あたりのリクエスト処理数rps (request per second) 同時接続数と呼ばれることもある Webサービスを高速化するにはその構造を理解する、構造には論理的な構造と物理的な構造がある 論理的な構造 Webサービスの動作や仕組み いわゆるソフトウェアアーキテクチャ 物理的な構造 いわゆるインフラストラクチャ Webサービスの負荷 高負荷な状態とは、Webサービスを提供するシステムリソースのうち、短時間で利用料が大きく変わる、時間流動性が高いシステムリソースの利用率が高い状態 代表的なシステムリソースとして、CPU時間・メモリ領域・メモリI/O帯域・ネットワークI/O帯域・ストレージI/O帯域がある キャパシティ リクエストの文脈では、処理可能なスループットや同時接続数 システムリソースの文脈では利用可能なリソース量 キャパシティの見積もり Webサービスのキャパシティは、その時に利用可能な計算資源の量となる 個々の性能×数 で表せる キャパシティを調整するアプローチは2種類ある 垂直スケーリング: 個々の性能を変える 水平スケーリング: 数を変える 必要十分なキャパシティとは、需要に対して不足せず・過剰でない量 必要十分なキャパシティが供給されていると、レイテンシとスループットは維持され、エラーにならず、目に余る余剰がない 安定的な指標が出ているのであれば必要十分もしくは余剰である可能性が高い キャパシティ供給が不足すると、レイテンシ増加・接続不可・データ破壊など、ひどい状況になる キャパシティが余剰だと無駄なお金を払うことになる キャパシティの需給をバランスさせるためには、需要側の調整、供給側の調整のどちらもがある 需要側の調整 キューイング: 処理要求を順番待ちさせる レートリミット: 単位時間あたりの処理要求の受け付けに上限を設ける ランダマイズ: 処理要求の発生にランダムな待ちを潜ませ、タイミングを分散させる 技術以外の手法 先着順を辞めて抽選にする チケットを時間差で配る 供給側の調整 ユーザーに影響なく実施できるので、対応の軸になることが多い スケーリング 従来は事前に負荷を予測してスケーリングするプロアクティブな手法が用いられてきた クラウド全盛になりオートスケーリングを用いてイラスティックにスケーリングできるようになった ただし、以下のような課題がある システムリソース需要の変化とスケール完了までの間にリードタイム(時差)がある(リードタイムによってユーザーを待たせてしまう) インフラ基板側のキャパシティが売り切れており、スケーリングできない そのため、プロアクティブ+リアクティブな対応が必要 必要なキャパシティの見積もり 原則として事前予測は不可能 「試す」アプローチが必要 負荷試験を行う(ただし、必要十分量の保証は不可能) 土台となるデータが取得できる パフォーマンスチューニングの基本 原則 いきなり手を動かさない 勘で行動しない 考えて行動する 推測せず計測する 負荷試験などでは被負荷側だけでなく与負荷側のモニタリングもすべき 高速になったという結果もならなかったという結果も等しく重要 公平に比較する 比較するときは前提条件を揃える 複数回実施する(1セットあたり3~5回) 1つずつ比較する 同時に複数の項目に手をつけない どれが効いたかわからなくなる 制約理論 一番にボトルネックとなっている箇所のスループットが全体のスループットになるという考え方 ボトルネックにだけアプローチする ボトルネック以外に手を出しても制約理論からスループットは変わらない ボトルネックの特定は外側から順番にやる まずそれぞれの要素の入口と出口で所要時間を計測し、システムリソースの利用状況を確認し、ボトルネックを見つける システムリソース上限の問題がある場合は特徴的な時系列推移になることがある 現代のWebサービスにおいてボトルネックになりがちな箇所はCPU、メモリ、ディスクI/O、ネットワークI/Oである ボトルネックの対処には3つの基本パターンがある 解決: 課題になっている事象を根本から解決する 該当箇所がボトルネックでなくなるよう処理方法を変更する 速いWebアプリケーションを書き直す 回避: 課題になっている事象がボトルネックにならないよう迂回・省略する 構造や仕組みを変更し、処理そのものを不要にする 処理結果をキャッシュし使い回す 緩和: 課題になっている事象の影響を和らげる 配置変更、設定変更、スケールアウト、スケールアップなどを行い、ボトルネックの程度を緩和する 解決が望ましいが、回避した場合のほうがコストは小さい→解決も回避もできない場合は緩和を狙う 対応した結果該当の箇所がボトルネックでなくなった場合、その要素を更に高速化するより別のところに手を付けるべき パフォーマンスチューニングの具体的な活動は、負荷試験→改善→負荷試験のループ 負荷試験計画 実施準備 負荷施行→結果確認→改善→負荷施行→結果確認→改善… 負荷試験計画では目的、手段、目標を決めるのが重要 目的が最重要要素 負荷施行・結果確認フェーズのポイント 負荷をかけながら手動でも利用して使用感を確かめてみる 実施時間・実施結果・メトリクス・ログをセットで自動的に記録しておく 実施結果の内容を都度解釈する パフォーマンス: X並列でYユーザーがN分間で操作完了 異常の有無: エラーレスポンス、システムエラー、不審な挙動、不安定なレスポンスタイム ボトルネックの移動 値が想定通りに変化したか 与負荷側のメトリクスも同時に確認する 与負荷側がボトルネックになっており、十分な負荷がかけれていない場合もある Chapter 2 モニタリング サービスをモニタリングすることは、高速であることを保証し続けること モニタリングは一貫した変わらない視点でも続けることが重要 公平に比較する原則 目的を確実に定める モニタリングは外形監視と内部監視に大別することができる 外形監視 動作しているアプリケーションの外側からモニタリングする手法 ユーザーと同じ経路からサービスを利用し、動作を確かめることが主な目的 Synthetic Monitoringとも呼ばれる なるべくユーザーの近くからモニタリングを行う よりユーザー体験に近づけるため、シナリオテストを行う場合もある 内部監視 動作しているアプリケーションの内側からモニタリングする手法 ユーザーが見えない部分の状態をモニタリングする メトリクスを取得し、リソースが過不足なく存在しているかなどを確認する サービスが動作している環境にて、エージェントと呼ばれるモニタリング用のデーモンを用いてメトリクスを取得する 手動でのモニタリング top CPU使用率を確認できる free メモリ使用量を確認できる stress CPUに負荷をかけ、サーバーの性能を試験できる vmstat リソース全体の概要が見れる 自動モニタリング・モニタリングツール メトリクスを自動で収集し、保存する 保存したメトリクスをWebブラウザなどで時系列順に表示する 集計用のクエリなどで表示を切り替えられる メトリクスが特定の閾値に達すると通知を行う モニタリングツールのアーキテクチャ Pull型とPush型の2つに大別できる 内部監視をする場合にはどちらの環境においてもエージェントを常駐させる Pull型 モニタリングアプリケーションがエージェントへメトリクスを要求するアーキテクチャ エージェントはリクエストを受け取った時のみメトリクスを収集する メリット メトリクスの取得間隔をモニタリングアプリケーションで管理できる エージェントの実装をシンプルにできる Push型 エージェントがモニタリングアプリケーションへメトリクスを送信するアーキテクチャ 1分おきなど、所定のタイミングでメトリクスを収集する メリット エージェントが動作しているサーバにおいて、ポートに対する接続を許可する必要がない(むやみに穴を開けなくて良い) モニタリングアプリケーションの設定を変更する必要なく、モニタリング対象の増減が可能になる この特性により、モニタリング対象数やそのリソースの変動が激しい環境(サーバーレス環境など)では、Push型のほうが管理が楽になる 収集したメトリクスはディスク容量を圧迫するため、目的を持って収集し、不要なメトリクスは集めないでおくべき 何を対象としてモニタリングを行うかであり、何を用いてモニタリングを行うかは大きな問題ではない このようなアプリケーションにおけるリソースに対するモニタリングをAPM(Application Performance Management)と呼ぶ モニタリングの注意点 グラフ上での変動に惑わされず、実際に値としてどのくらい変動したかを見る 縦軸横軸両方の上限値・下限値を確認しておく 多くの場合、縦軸を固定して表示すると良い 収集したデータに対してどのような加工が行われているかに気をつける 例えばrate() 関数は瞬間的な値の変化を丸めてしまい、グラフとして現れにくい等といった特徴を把握しておく 2つのグラフを比較するときは他の条件を合わせる 異なる原点を定めたグラフを比較しないようにするため、絶対値でグラフに描き条件を合わせる モニタリングアプリケーションやエージェントもシステムリソースを消費して動いている 特にエージェントはWebアプリケーション側で動いているので注意 負荷試験のために発生する負荷を考慮する 負荷試験を行う際に、Webアプリケーション・エージェントと与負荷側を分離する 与負荷側はリクエストを多く送るという性質上多くのリソースを使用するので、それによる負荷がアプリケーション側に影響しないようにする 負荷試験を行う環境は本番環境で運用する環境になるべく近づける RFC 2544 - Benchmarking Methodology for Network Interconnect Devices参照 モニタリングの間隔に注意する 高負荷な状況が続いていた時間に比べて、モニタリングの間隔が長いと、その状況が上手く補足できない→モニタリングの解像度が低い・足りていない状態 できるだけリアルタイムに解像度が高くなるようにしておく ただし、モニタリングの性質を考慮する必要がある ベンチマークなどでは間隔を短く、運用監視など長期的に取得する場合は長くするなど Observability(可観測性) アプリケーション内部でどのようにリソースが利用されているかを調査するツールとしてプロファイラがある 特にソースコードのどの行にどの程度時間がかかっているかを可視化するツールをラインプロファイラと呼ぶ WebアプリケーションにおけるCPU利用率が増えている場合は、ラインプロファイラを用いてどの関数が特にCPU時間を利用しているかを確かめる手法を採る ラインプロファイラによって集められた、それぞれの関数におけるCPU時間を階層上に積み上げたグラフとして可視化するツールをFlame Graphと呼ぶ Microservicesアーキテクチャでは複数のWebアプリケーションでサービスを提供するため、1リクエストに複数のWebアプリケーションが介することとなり、かかったリソースを捉えることができない この問題を解決するために分散トレーシングと呼ばれる手法が提案されている 分散トレーシングを含めたTracing、Logging、Metricsの3つを主要素としたモニタリングの考え方をObservability(可観測性)と呼ぶ ログによるモニタリング Webアプリケーションが出力するログを用いてモニタリングを行うことができる 例えば一定時間ごとのアクセスログを集計することで、どの時間帯にどの程度のアクセスがあるかをモニタリングできる エラーログをモニタリングし、アラートを出すといったことも考えられる ERRORログは発生次第すぐ対応、WARNは1分に10件以上発生していれば対応などといった施策ができる Chapter 3 基礎的な負荷試験 nginx access_log /path/to/conf [compression]; [compression]忘れると該当のログフォーマットが適用されない ab (Apache Bench) apache2-utilsにあるベンチマーカー 単一のエンドポイントに対して指定の条件分リクエストを送る UXを考慮すると、10ms程度の違いは分からない top topコマンドのid を見ることで、CPU全体のうち何割を使用しているかわかる 30 id なら100 - 30 = 70で全体の7割を使用していることになる 逆に各プロセスの使用率を表すテーブルでは、1コアをすべて使った場合を100とした使用率を表示している 2コアをすべて使った場合は200%になる topコマンド実行中に「1」キーを押すと個別のCPU使用率が見れる mysql スロークエリログの出力設定 /etc/mysql/mysql....