負荷をさらに耐えられるようにする方法
おかえし
サービスが負荷に耐え切れなくなったとき、オペレーターは選択の余地があります。既存のマシンをより大きくすること(より多くのCPU、より多くのRAM、より高速なディスク)か、同じ仕事をするより多くのマシンを追加することです。
最初のパスは縦スケーリング(スケールアップ)と呼ばれています。第二のパスは水平スケーリング(スケールアウト)と呼ばれています。
このレッスンでは、ほとんどの現代のウェブアーキテクチャが水平を選ぶ理由と、どのようなプロパティがその選択が実行可能であるかを教えます。その選択が実行可能である理由は、ワークロードの1つの特性に隠れています。それはステートです。
終わったら理解できること:
- 縦スケーリングと水平スケーリングのコスト曲線、およびそれぞれが適切な状況でどのように適用するか
- 'ステートフル'と'stateless'の実践的な意味、そしてどちらが安く増えるのか
- 予想される負荷と急増負荷の下でレプリカ船団のサイズを計算する数学
- 層がキューイングの膝から崩れないようにするヘッドルームのルール
- ステートはどこにいる必要があります(消滅することはありません)そして、スケーリングが必要な層から押し出す方法
水平スケーリングの勝利のしきい値
縦スケーリング:より大きなボックス
メリット:シンプル。コード変更なし。調整なし。同じプロセスがより多くのCPUを持っています。
デメリット:天井。最大の商用VMは有限のRAMとコアです。その上で、より多くのヘッドルームを得る金銭が存在しません。超線形にコストが増加します。1つのマシンの失敗により、全サービスがダウンします。
水平スケーリング:より小さなボックスが多い
メリット:天井なし(あなたの支払い意欲とマシンの調整までに)。容量がレプリカカウントとともに線形に増加し、予測可能です。1つのレプリカの失敗により、容量の1/Nがなくなるのではなく、100%がなくなる。
デメリット:ワークロードがそれをサポートしている必要があります。1つの大きなデータベース(状態のあるゲームサーバー、ライブセッションを保持している)が水平スケーリングに抵抗する場合があります。コーディネーションと負荷分配は運用上の懸念事項です。
クロスオーバー: 製品サービスが単一のマシン障害を乗り越すために、少なくとも2台のマシンで実行する必要がある場合、それは水平スケーリングを選択することを意味します。そこから、質問は 'どうやって追加のレプリカを安く追加できるか' ではなく 'すべきか' ではなく 'どうやって追加のレプリカを安く追加できるか' になる
鍵となる要素: マシン自身に保持するリクエストごとの状態を持っていないワークロード。すると、どのレプリカでもリクエストに答えられるようになり、レプリカを追加することで容量が増加するだけで、協調が必要なくなる
状態のあるものと状態のないもの:実践
状態は消滅しない。ただ場所が変わる
状態のあるコンポーネント: 失われると挙動が変わる情報を保持する。ユーザーアカウントを保持するデータベース。セッショントークンを保持するキャッシュ。長期間継続するストリーミング接続を特定のユーザーに固定するワーカー。
状態のないコンポーネント: 失われると問題ない情報を保持しない。リクエストを受け取ってデータベースをクエリし、レスポンスを書き込むWeb層。リクエスト間で情報を保持していない。
鍵の見解: 状態はシステムから消滅しない。状態を保持するレイヤーに移動する(データベース、Redisクラスター、オブジェクトストア)。TRAFFICに直面するレイヤーは状態のないものになり、状態のないレイヤーは水平スケーリングが可能になる。どのレプリカでもリクエストに答えられる。
実践的なテスト: この層にランダムにプロセスをキルし、再起動した場合、ユーザーが間違った答えや失われたセッションを経験するかどうかを確認してください。もしYesの場合、状態を持っていることを示しています。もしNoの場合、状態を持っていないことを示しています。
例
- Python Webプロセスがリクエストを読み込み、Postgresをクエリし、JSONを返す: 状態のないもの。状態はPostgresに存在。
- Python Webプロセスがローカルメモリにユーザーショッピングカートを保持する: 状態のあるもの。プロセスをキルするとカートが失われる。
- WebSocketサーバーがチャットユーザーとのオープン接続を保持する: 接続の観点では状態のあるもの。プロセスをキルすると接続が切断される。クライアントが再接続する必要がある。通常、これらは慎重に水平スケーリングすることができる(粘性セッション、一貫性のあるハッシュ)。
- RedisキャッシュがPostgresを前面に置く: キャッシュ内容は状態を持つが、キャッシュミスが許容可能であれば問題ない。リプリカの失敗はキャッシュミスであり、データロスではない。
水平スケーリングを設計することは、スケーラブルなレイヤーから状態を外すことである。
疑われる階層を検査する
チームは、リバースプロキシの背後にある6つのバックエンドVM上でリコメンドAPIを実行しています。アプリケーションは、リクエストからユーザーIDを読み取り、Postgresからユーザーの最近のアクティビティを取得し、スコアリングアルゴリズムを実行し、推奨アイテムのリストを返します。2つの非標準な行動です。
- アプリケーションは、プロセスメモリにユーザーの'recent user activity'キャッシュを保持し、最初のリクエストでユーザーのデータを取得し、以降のリクエストで再利用します。
- アプリケーションは、ユーザーがVM #3に到達すると、その後のすべてのリクエストがVM #3に送られます(プロキシはクッキーのルーティングにsticky routingが設定されている)。
リプリカの公式
最もシンプルな容量の公式
階層が状態を持たなくなったら、そのサイズを算術で決めることができます。常時負荷がリプリカに到着し、離脱する速度が同じになるように、スラッグファクターに十分なリプリカが必要です。
公式:
リプリカ = ⌈ (ピーク負荷 × スラッグファクター) / 1リプリカあたりの容量 ⌉ + ヘッドルーム
ただし:
- ピーク負荷: 通常の運用で期待する最大持続的なリクエスト/秒
- スラッグファクター: 可預なトラフィックの場合1.5xから2x、ウイルス的で予測不能な場合3x以上の短期的なブーストの上限
- 1リプリカあたりの容量: 可容れるレイテンシーと利用率で1リプリカが処理できるリクエスト/秒(通常は70%のCPUで測定され、飽和点ではありません)
- ヘッドルーム: リプリカの失敗が階層を崩すことを防ぐために追加のリプリカ(小規模な艦隊の場合1-2リプリカ、大規模な場合10-20%)
ワーク例: 1つのリプリカが70%のCPU使用率で100req/sを処理できます。ピーク負荷は600req/sです。たまに2倍のスパイクがあります。3つのリプリカが失われることを想定し、生存者に80%の使用率を超えないようにします。
リプリカ数 = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14リプリカ
80%のルール
リプリカあたりの容量は飽和点ではありません。100%ではなく、70-80%のCPU使用率で容量を測定してください。
80%の利用率を超えると、キュー処理の曲線が急上昇します:60%の利用率で10msで処理されていたキューは、90%の利用率で80msで処理されます。レイテンシーが初めて破れるのは、スループットではなくです。(『stateless_horizontal_scalingの幾何学』という相互教材でこの曲線を数学的に導出しています。)
オートスケーリング vs スタティックプロビジョニング
スタティック:ピーク×スパイクの余裕を持つリソースを確保し、ピーク外の時間帯は低い利用率でコストを引き受けます。
オートスケーリング:コントローラーが観測された利用率、目標レイテンシー、またはキューの深さに基づいてリプリカを追加・削除します。
オートスケーリングの注意事項: 新しいリプリカが2分かかる場合、オートスケーリングは30秒のスパイクに対応できません。成熟したオートスケーリングは、スケールアップしきれずにリプリカを準備して保管しています。
新サービス用に艦隊を設定する
Your team plans to launch a video metadata API. Benchmarks show a single replica handles 250 req/s at 70% CPU & 50 ms p99 latency. Marketing forecasts peak load at 4,000 req/s during prime-time hours. A planned promotional event could surge to 3x peak briefly. You want the service to survive 3 simultaneous replica failures without exceeding 80% utilization on the survivors.
リアルな艦隊にはリアルのエッジがあります
実際の艦隊には実際のエッジがあります
公式では、リプリカが瞬時に現れ、トラフィックを受け入れるか、トラフィックを放出するかのいずれも瞬時にできるという仮定がありますが、これらは実際の生産現場では成立しません。
Cold start: 新レプリカがOSを起動しプロセスを開始し、構成を読み込み、キャッシュを温めるだけでなく、ヘルスチェックを通過する必要があります。コンテナ再起動の場合は5秒、全VMブートおよびイメージプルまで5分かかります。オートスケーリングは、この遅延時間よりも短いバーストに対応できません。
Slow drain: プールからレプリカを削除する際には、インフライトリクエストを終了するのに時間がかかり、終了する前に終了する前にユーザーが短縮された応答を受けます。リバースプロキシは、ドレイン(新しいリクエストを停止し、活動的なものを完了する)をサポートしますが、それが数秒から分かかります。
Warm pool: 生産環境の艦隊は、信号に従ってトラフィックを受け取る準備ができているがアイドル状態の事前割り当て済みのレプリカのプールを維持します。定常的な小さなコストを交換して高速なスルージャンプ応答を得ます。
Connection draining vs immediate kill: すばやいシャットダウンは重要です。SIGTERMがドレインを開始し、SIGKILLよりも長くかかるが、ユーザー要求を破壊しないです。
Health check window: レプリカが始まった直後は、データベース接続プールが温かくなる前に最初のヘルスチェックを通過するかもしれません。プロキシは実際のパスをテストするためにヘルスチェックを調整し、プロセスの生存性だけでなく、実際のパスをテストします。
Stickiness creep: すでに状態レスポンス層が時々刻々と粘性を獲得します(CDNキャッシュ、DNS解決済みのキャッシュ、接続プール)。状態レスポンス層が異なる動作をする「同一のレプリカ」に対して警戒してください。
ウォームプールかリアクティブオートスケーリング?
あなたのビデオメタデータAPI(前回の質問で同じもの、定常ピークおよびスルージャンプのサイズで51のレプリカ)では、新しいビデオがアップロードされるたびに30秒のスルーを経験し、通常の負荷の5倍になります。オートスケーリングは、90秒かけて新しいレプリカを冷たい状態から追加します(イメージプルおよびウォームアップ)。90秒の間隔で、レイテンシーが急上昇し、いくつかのリクエストがタイムアウトします。
制約下での状態レスポンス層の設計
シンセシス
あなたは、水平スケーリングが小さなしきい値を超えるとどのように勝利するか、実践的な状態の意味、予想されるおよびサージロードの下で艦隊をサイズにする方法、および水平スケーリングがエッジで破綻するかを学びました。
すべての4つを適用します。
feed.example.com、ソーシャルフィードAPIのバックエンド階層を設計します。制約: 従量 200 req/s、70% CPU; 期待されるピークロード 1500 req/s; サージ要素 2.5x(たまにトレンドするストーリー); 同時にレプリカ障害が2つ発生し、耐えられる; 冷スタータータイム 60秒; バーストは45秒間続く; 費用は、一時的な確保が2.5xではないが、一定の無駄容量を許可しています。
このコースが次にどこに行くか
このコースが次にどこに行くか
あなたは、状態レス階層の機能的なメンタルモデルを持っています:それがスケーリングする理由、サイズ方法、エッジで破綻する理由、および状態が、成長する必要があるレイヤーから押し出されたときにどこに移動する必要があるか。
このコースの次のレッスン(cs_distsys_ingress_egress_separation)は、入力および出力トラフィックが同じネットワークパスを共有する場合に、完全にサイズ付けられた状態レス階層が驚くべき方法で失敗する問題を扱います。クラシックの例は、プロキシが自分自身に接続しようとする場合です;解決策は、1つの階層を2つの異なる責任を持つものに分割することです。
連携レッスン:geometry_of_stateless_horizontal_scalingは、レプリカ艦隊のキューイング曲線、リトル法の適用、および80%利用率の膝の幾何学的意味を導出します。
素晴らしい仕事です。進んでください。