Netflix のスケール - オートメーション編

遅くなったけど続編。前回 TODO にしてたスケールアウトのオートメーションあたり。AWS のオートスケールについては、本家ドキュメントに一通り書いてあります。その機能を大まかにまとめると、
  • スケールアウト、バランシングが自動化できる
    • 複数のゾーン (Availability Zone) にまたがっていても一元管理できる (AZRebalance
    • ELB を指定してインスタンスを追加できる
  • 不健全なインスタンスを自動的に入れ替えれる

>>> オートスケールの設定

大きく分けると 3 つのステップ
  1. CPU やメモリ等のリソースを識別する
  2. CloudWatch に各リソースをモニタリングするためのメトリクスを作成する
    デフォルトでモニタリング可能なリソースが用意されている (デフォルトで用意されているリソース
    • EC2 の CPU 負荷、ディスク I/O、ネットワーク I/O
    • ELB のホスト数 (健全/不健全)、リクエスト数、レイテンシ
    Netflix 推奨は ELB の rps (req/sec
    クラスメソッドさんがカスタムメトリクスの追加について超わかりやすく解説されています。
  3. リソースの変化に基づくアラームやオートスケール等のポリシーを定義する
    • ポリシーベース: 負荷が高くなったら EC2 インスタンスを増やし、負荷が減ったら EC2 インスタンスを減らす
    • スケジュールベース: 負荷の予測が可能な場合は、インスタンスの増減をスケジュールする (バッチ処理にも素敵

>>> Netflix がオートスケールから学んだこと

Netflix さんが実際にオートスケールを運用して、その結果をフィードバックされています。以下抄訳とかメモとか
  • 早めにスケールアップ
    例えば負荷テストで 25rps (rec/sec) を超えると待ちが発生するとする。その場合は待ちが発生しないように、20rps を超えた際にキャパシティーを増やすよう設定する。この rps の上限設定は以下の目的がある。
    1. オートスケールイベントのトリガーでは間に合わない程の、予想をはるかに超える rps もありうる
    2. このバッファーが "キャパシティスパイラル" を回避するセーフティーネットとなり、徐々にキャパシティを増やせるようになる
    3. インスタンスの起動失敗によるキャパシティ不足を予防する (追記
    Netflix の推奨設定
    • 目標値 (25rps 等) の 75% (20rps 等) が目安
    • イベントのトリガーは 5 分 ~ 10 分 (EC2 やアプリケーション等、インスタンスの起動時間を考慮
    メモ): rps による効果的なオートスケールには、まずちゃんと負荷テストするのが大事
  • ゆっくりスケールダウン
    スケールダウンを開始する時間は、スケールアップを開始する時間より長くする。上記の例に引き続き、例えば 5 分間 20rps を超えた場合にスケールアップするとする。この場合、20 分間 10rps 未満の場合にスケールダウンする。 時間差は 4 倍にする 。理由は、ご検出によるスケールダウンを防ぐため。例えば、中間層のサービスの場合、周辺サービスが完全、部分的な機能停止した場合にもスケールダウンしてしまう可能性があります。周辺サービスがオンラインに戻った時に、中間層のサービスのキャパシティが足りない可能性があります
  • Availability Zone のキャパシティ
    複数の AZ にまたがってオートスケールグループを作成する際、パーセンテージベースのスケールポリシーを使うのは注意が必要。小規模の場合は特に。負荷が偏り、キャパシティ不足のゾーンが出来てしまう。
    ※ パーセンテージベースのスケールポリシーを使う場合の注意点をそのうち公開してくれるっぽい
  • 細かいアップ/ダウンアラームを避ける
    細かいアラームを設定するとキャパシティのスラッシングが発生する。例えばロードアベレージを小さな値でアラームを設定してしまうと、予期しないスケールが発生する。発生時間が短ければさらなる問題が。例えば、ログローテーションやCPU スパイクによりスケールしてしまう。
  • シンメトリックな Percent
    キャパシティのスラッシングを防ぐために、シンメトリックにオートスケールポリシーを作成する。例えば 10% 拡大し、10% 縮小する。2 つが異なっている場合は、極端なキャパシティーの増加が発生し、その後すぐ削除される (ほんとは 10 インスタンスの追加のはずが、100 インスタンス追加して、90 インスタンス削除するとか) ことによるキャパシティのスラッシングが発生します。
  • シンメトリックな Period (1分 ~ 2週間)
    CloudWatch は 300, 600 等集計期間が異なると、総計の結果が異なります。細かい値の場合は特に。これにより意図しないスケールが発生する可能性がある。

>>> Eureka (LB / フェイルオーバーツール

ふたたび V 先生ドリブン。Netflix が先日 Eureka というツールを OSS として公開しています。簡単に言うと、Web トラフィックを対象にしたロードバランシング / フェールオーバーツールが ELB, CloudWatch なのに対し、中間層のサービス (Netflix の場合は Cassandra, memcached 等) を対象にしたロードバランシング / フェールオーバーツール。P2P 型ロードバランサ?以下、抄訳
なぜ Eureka が必要なのか 一般的なロードバランサは IP やホスト名をベースに動作しますが、AWS ではロードバランサへのサーバの追加と削除が必要。また、AWS は中間層のサービス用のロードバランサを提供していないため。
ELB と何が違うか ELB は Web トラフィックをロードバランスするためのソリューション。 Eureka は中間層のサービス用。理論的には ELB 配下に中間層のサービスを配備できるけど、それだとインターネット上に公開することになってしまう。
ELB を含むプロクシベースのロードバランサはセッション維持 (sticky session) 機能を提供しますが、Eureka はデフォルトではセッション維持を提供しない。Netflix の中間層のサービスはほぼステートレスであり、AWS 上でのオートスケールではセッション維持のないロードバランスの方が良い。
ELB が代表的なプロクシーベースのロードバランサなのに対し、Eureka は インスタンス/サーバ/ホスト レベルでロードバランスする。クライアントインスタンスが通信するサーバのすべての情報を知っている
もうひとつのプロクシベースのロードバランサと Eureka の大きな違いは、利用可能なサーバの情報をクライアントがキャッシュしているため、ロードバランサーが停止してもすぐに復帰できること。少量のメモリを必要とするけど、復帰が早い。プロクシーを介さず、サーバーとダイレクトに通信するため、若干レイテンシも下がる (素敵)。
Route 53 と何が違うか Route 53 はネーミングサービスなのに対し、Eureka は中間層のサーバにネームを提供する。Route 53 も AWS 意外のデータセンタ用に DNS レコードをホスト出来る。また、AWS 内でレイテンシベースのルーティングも出来る。Eureka は内部 DNS と似ており、インターネット上の DNS とはまったく関係がないし、どの AWS リージョンのサーバかも知らない。Eureka の主な目的は単一リージョン内の負荷分散。
Route 53 へ中間層のサーバ群を登録し、AWS セキュリティグループでアクセス制限をすることもできるけど、サーバ名はインターネット上からアクセス可能。また、Route 53 は、大抵の DNS サーバベースのロードバランシングと同様にサービスが落ちている、もしくはアクセスできない (インスタンスが落ちた) サーバへのルートへもバランスしてしまう欠点があります。
どこで使うの?
  • ELB に登録したくない、もしくは外部からのトラフィックにさらされたくない中間層のサービスを持っている
  • シンプルなラウンドロビンロードバランサがほしい
  • セッション維持の必要がない
  • クライアントベースのロードバランサがほしい
asgard と統合して、より素敵な中間層のロードバランシングソリューションを開発中だそうです。
うむ。Web アプリケーションについては結構簡単にオートスケール設定できるのですが、中間層のオートスケールは必要になってから勉強ということでw

>>> 追記) AWS な先生方より


話はめっちゃ飛びますが、なんと先日 "みんなのPython 第三版" のレビューに参加させて頂きました (柴田先生のブログ)!おかげさまで改めて楽しく Python 3 系の勉強をすることができました。この場を借りてお礼申し上げます。柴田さん第三版おめでとうございます!!!買いましょう

コメント

このブログの人気の投稿

Python から Win32 API 経由で印刷する

Disqus のスケール - Django 編

Django と Python 3 - #python_adv