より多くの負荷を支える2つの方法
歓迎
サービスが負荷に耐えきれなくなると、オペレーターは選択に迫られます。既存のマシンを大きくする(CPU、RAM、より高速なディスク)。または、同じ作業を行うマシンを追加する。
最初の経路は垂直スケーリング(スケールアップ)と呼ばれます。2番目の経路は水平スケーリング(スケールアウト)と呼ばれます。
このレッスンでは、なぜ最新のWebアーキテクチャのほとんどが水平スケーリングを選択するのか、そしてその選択を可能にするワークロードの特性が何かを学びます。答えは1つの単語に隠れています:状態。
学習終了時には、以下の内容を理解できるようになります:
- 縦方向スケーリングと横方向スケーリングのコスト曲線、およびそれぞれが有効な場面
- 「ステートフル」と「ステートレス」の実践的な意味、およびなぜ片方が安価に複製できるのか
- 想定負荷とスパイク負荷下でレプリカ群のサイズを決定する計算式
- 階層がキューイングの膝点を超えて崩壊しないように保つためのヘッドルームのルール
- ステートが存在すべき場所(決して消えることはない)と、スケーリングが必要なレイヤーからステートを外部へ移す方法
なぜ閾値を超えると横方向スケーリングが有利なのか
縦方向スケーリング:より大きな1台のボックス
利点: 単純である。コードの変更は不要。調整も不要。同じプロセスがより多くのCPUを保有するようになるだけである。
欠点: 上限が存在する。市販の最大規模のVMには、有限のRAMとコア数しか存在しない。それを超えると、いくら金を出しても余剰性能(headroom)は買えない。ベンダーの提供範囲の最適点を過ぎると、コストは超線形的に高騰する。その1台のマシンが障害を起こすと、サービス全体がダウンする。
水平スケーリング: 多くの小さなボックス
利点: 上限がない(マシンを購入し調整する意思と予算の範囲内では)。容量はレプリカ数に比例して予測可能に増加する。単一のレプリカの障害は、容量の100%ではなく1/N分しか失われない。
欠点: ワークロードがそれをサポートしている必要がある。一部のワークロード(単一の巨大なデータベース、ライブセッションを保持するステートフルなゲームサーバーなど)は水平スケーリングに抵抗する。調整と負荷分散が運用上の懸念事項となる。
転換点: 単一マシンの障害に耐える必要がある本番環境のサービスは、少なくとも2台のマシンで実行されなければならない。2台を許容した時点で、すでに水平スケーリングを選んでいることになる。そこから先、問いは「すべきか?」ではなく「次のレプリカをどれだけ安価に追加できるか?」である。
重要な有効化要因: マシン自体にリクエストごとの状態を保持しないワークロードである。そうすれば、どのレプリカもどのリクエストにも応答でき、レプリカの追加は調整なしで容量を追加する。
実践におけるステートフルとステートレス
ステートは消滅せず、移動するだけ
ステートフルコンポーネント: 情報が失われた場合、動作が変わるような情報を持つコンポーネント。ユーザーアカウントを保持するデータベース。セッショントークンを保持するキャッシュ。長時間実行されるストリーミング接続を特定のユーザーに固定するワーカー。
ステートレスコンポーネント: 失われても問題にならない情報を持たないコンポーネント。リクエストを読み取り、データベースにクエリを実行し、レスポンスを書き込むウェブティア。各リクエストは独立しており、ティアはリクエスト間で何も記憶しない。
重要な洞察: ステートはシステムから消滅することはない。それは、それを保持するために設計されたレイヤー(データベース、Redisクラスタ、オブジェクトストアなど)に移動する。これにより、トラフィックに直面するレイヤーはステートレスになり、ステートレスなレイヤーはどのレプリカもどのリクエストにも応答できるため、水平スケーリングが可能になる。
実用的なテスト: このティアのプロセスを1つランダムに停止して再起動した場合、ユーザーが誤った回答を受け取ったり、セッションを失ったりするだろうか?そうなら、それはステートを持っている。そうでなければ、持っていない。
例
- リクエストを読み取り、Postgresにクエリを実行し、JSONを返すPythonウェブプロセス: ステートレス。ステートはPostgresに存在する。
- ユーザーのショッピングカートローカルメモリに保持するPythonウェブプロセス: ステートフル。プロセスを停止するとカートが失われる。
- チャットユーザーとの接続を維持するWebSocketサーバー: 接続の意味でステートフル。プロセスを強制終了すると接続が切断され、クライアントは再接続する必要があります。ただし、これらのサーバーは多くの場合、注意深く設計(スティッキーセッション、一貫性ハッシュなど)することで、依然として水平スケーリングが可能です。
- Postgresの前に配置されたRedisキャッシュ: キャッシュの内容に関してはステートフルですが、キャッシュミスが許容される範囲であれば問題ありません。レプリカが障害を起こしても、それはデータ損失ではなく単なるキャッシュミスに過ぎません。
水平スケーリングのための設計 = スケールが必要なレイヤーからステートを外部へ移すこと。
疑わしいティアの監査
あるチームは、リバースプロキシの背後に6台のバックエンドVMで推奨APIを運用しています。このアプリケーションは、リクエストからユーザーIDを読み取り、Postgresからそのユーザーの最近のアクティビティを取得し、スコアリングアルゴリズムを実行して、推奨アイテムのリストを返します。2つの非標準的な動作があります:
- アプリケーションはプロセスメモリ内に「最近のユーザーアクティビティ」のキャッシュを保持しており、ユーザーの初回リクエスト時に設定され、その後のリクエストで再利用されます。
- アプリケーションはスティッキーセッションを使用しています。一度ユーザーがVM #3にアクセスすると、その後のすべてのリクエストがVM #3にルーティングされます(プロキシはクッキーベースのスティッキールーティングに設定されています)。
レプリカの計算式
最もシンプルなキャパシティ計算式
ティアがステートレスになると、サイズ調整は算術問題になります。定常状態の負荷が同じ速度で到着し、離れるように十分なレプリカ数が必要であり、サージ(急増)のための余裕(ヘッドルーム)も確保する必要があります。
その数式:
replicas = ⌈ (peak_load × surge_factor) / per_replica_capacity ⌉ + headroom
ここで:
- peak_load: 通常の運用で想定される最大持続リクエスト/秒
- surge_factor: ピークを上回る一時的なバーストをカバーするための倍率(予測可能なトラフィックでは一般的に1.5倍から2倍、バイラル/予測不可能なものでは3倍以上)
- per_replica_capacity: 許容されるレイテンシと使用率で1つのレプリカが処理できるリクエスト/秒(一般的には飽和状態ではなく、CPU 70%で測定されます)
- headroom: 一部のレプリカが故障してもティアが崩壊しないようにするための追加レプリカ(小規模なフロートでは一般的に1〜2個、大規模なものでは10〜20%)
実例: バックエンドが1レプリカあたりCPU使用率70%で100 req/sを処理している。ピーク負荷は600 req/sである。一時的な2倍のスパイクが発生すると予想される。2つのレプリカの障害に耐えられるようにしたい。
replicas = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14レプリカ
80%ルール
1レプリカあたりの処理能力は飽和点ではない。処理能力は100%ではなく、CPU使用率70〜80%で測定すること。
使用率が約80%を超えると、キューイング曲線は急激に上昇する。使用率60%で10 msだったキューが、使用率90%では80 msになる。最初に壊れるのはスループットではなくレイテンシである。(姉妹レッスン geometry_of_stateless_horizontal_scaling では、この曲線を数学的に導出している。)
オートスケーリングと静的プロビジョニング
静的プロビジョニング: ピーク × スパイクの余裕分をプロビジョニングし、オフピーク時に低使用率で稼働するコストを受け入れる。
オートスケーリング: コントローラーが観測された使用率、目標レイテンシ、またはキューの深さに基づいてレプリカを追加・削除する。
オートスケーリングの注意点: コールドスタート時間が重要です。新しいレプリカの起動に2分かかる場合、30秒間のトラフィック急増にはオートスケーリングは対応できません。成熟したオートスケーリングは、スケールアップ閾値の直下で、事前プロビジョニングされたレプリカのウォームプールを維持します。
新サービス向けにフリートをサイズ設定する
チームは動画メタデータAPIのリリースを計画しています。ベンチマークによると、単一のレプリカはCPU使用率70%、p99レイテンシ50msで250 req/sを処理できます。マーケティング部門は、プライムタイム時間帯のピーク負荷を4,000 req/sと予測しています。計画されているプロモーションイベントでは、一時的にピークの3倍まで急増する可能性があります。サービスは、3つのレプリカが同時に故障しても、生存レプリカの利用率が80%を超えないようにする必要があります。
コールドスタート、遅いドレイン、その他の現実的なエッジケース
現実のフリートには現実的なエッジケースがある
この数式は、レプリカが即座に出現し、即座にトラフィックを受け入れ、即座にトラフィックを捨てることを前提としています。本番環境では、これらはいずれも成立しません。
コールドスタート: 新しいレプリカは、OSの起動、プロセスの開始、設定の読み込み、キャッシュのウォームアップ、ヘルスチェックの通過が必要です。コンテナの再起動で5秒から、完全なVMのブートとイメージのプルで5分まで、幅があります。オートスケーリングは、この遅延より短いバーストには応答できません。
遅いドレイン: プールから削除されるレプリカは、終了前に進行中のリクエストを完了させる時間が必要です。そうしないと、ユーザーは切り詰められたレスポンスを見ることになります。リバースプロキシはドレイン(新しいリクエストの受け入れを停止し、アクティブなリクエストを完了させる)をサポートしていますが、数秒から数分かかります。
ウォームプール: 本番環境のフロートは、シグナルを受信した時点でトラフィックを受け取れるよう、事前プロビジョニング済みだがアイドル状態のレプリカのプールを維持します。わずかな定常コストを対価に、急激なトラフィック増加への迅速な対応を実現します。
コネクションドレインと即時キルの違い: グレースフルシャットダウンは重要です。ドレインをトリガーするSIGTERMはSIGKILLよりも時間がかかりますが、ユーザーのリクエストを破綻させることはありません。
ヘルスチェックのウィンドウ: 起動したばかりのレプリカは、データベース接続プールがウォームアップされる前に最初のヘルスチェックに合格する可能性があります。その場合、プロキシが実際のトラフィックを送り出し、最初の数十リクエストが遅延します。ヘルスチェックはプロセスの生存確認だけでなく、実際の処理パスをテストするように調整してください。
スティッキーさの浸食: 名目上ステートレスなティアであっても、時間の経過とともにスティッキーさ(CDNキャッシュ、DNSリゾルバキャッシュ、接続プールなど)を獲得します。一見同一であるはずのレプリカが異なる挙動を示す場合は、その原因を疑うべきです。
ウォームプールか、リアクティブなオートスケーリングか?
あなたの動画メタデータAPI(前問と同じもの。定常ピークとサージに対応するため51レプリカでサイズ設定済み)は、バイラル動画がアップロードされるたびに、通常の5倍の負荷への30秒間のサージを経験します。現在のオートスケーリングでは、コールドスタート(イメージプル+ウォームアップ)から新しいレプリカを追加するのに90秒かかります。この90秒のギャップの間、レイテンシは急上昇し、一部のリクエストがタイムアウトします。
制約条件のもとでステートレスティアを設計する
統合
小規模な閾値を超えた時点でなぜ水平スケーリングが有利になるのか、実務において「状態(state)」が何を意味するのか、期待負荷とスパイク負荷の下でフロートをどのようにサイズ調整するか、そして水平スケーリングが境界で破綻する箇所はどこかを学習しました。
4つの要素をすべて適用してください。
ソーシャルフィードAPIである feed.example.com のバックエンドティアを設計してください。制約条件:レプリカあたりの処理能力はCPU使用率70%で200 req/s、予想されるピーク負荷は1500 req/s、サージ係数は2.5倍(トレンド記事による突発的な増加)、2つのレプリカが同時に障害を起こしても生存可能であること、コールドスタート時間は60秒、バーストは最大45秒間持続する可能性がある、予算はアイドル容量の確保は可能だが、2.5倍の恒常的なプロビジョニングは不可。
このコースの次のステップ
このコースの次のステップ
これにより、ステートレスティアの動作に関する機能するメンタルモデルを身につけました。なぜスケーリングが可能なのか、どのようにサイズを決定すべきか、境界で何が破綻するのか、そして成長が必要なレイヤーから外に拡張した際にステートをどこへ移動させるべきか、これらについて理解できるようになりました。
このコースの次のレッスン(cs_distsys_ingress_egress_separation)では、より微細な問題に取り組みます。完全にサイズ調整されたステートレスティアであっても、受信トラフィックと送信トラフィックが同じネットワークパスを共有している場合、予期せぬ形で失敗することがあります。古典的な例としては、プロキシが自分自身に接続しようとするケースがあり、その解決策には、1つのティアを異なる責任を持つ2つに分割することが含まれます。
関連レッスン: geometry_of_stateless_horizontal_scaling では、キューイング曲線の導出、レプリカ群へのリトルの法則の適用、および80%利用率の膝点(knee)の幾何学的意味を扱います。
よくできました。次へ進みましょう。