inductor's blog

nothing but self note :)

Googleが作った分散アプリケーション基盤、Borgの論文を読み解く -その1-

このエントリーについて

このエントリーを書き始めた経緯は下記にあります。

inductor.hatenablog.com

上記の理由の通り、目的は論文を翻訳することだけではなく、最終的にこれを踏まえて自分の見解をつらつらと書いていくところにもあります。

おそらく一番時間がかかるのはそれなので、一旦は翻訳を一通り終えた上で更に頑張っていきます。ゆっくりお待ちいただければと思います><

1. Introduction(まえがき)

Borgが内部的に呼び出すクラスター管理システムは、Googleが実行するすべてのアプリケーションを許可、スケジュール、起動、再起動、および監視します。この論文ではその方法を説明します。

Borgには3つの主な利点があります。

  1. リソース管理と障害処理の詳細を隠すため、ユーザーは代わりにアプリケーション開発に集中できます。
  2. 非常に高い信頼性と可用性で動作し、同じことを行うアプリケーションをサポートします。
  3. 数万台のマシンで効果的にワークロードを実行できます。

Borgは、これらの問題に対処する最初のシステムではありませんが、上記のような復元力と完全性を備えた、この規模で運用されている数少ないシステムの1つです。
この論文は、これらのトピックを中心に構成されており、10年以上にわたって本番環境でBorgを運用してきた一連の定性的観察で締めくくっています。

f:id:inductor:20191029235753p:plain
図1. Borgの高レベルのアーキテクチャ図。数千のワーカーノードのごく一部のみを示しています。

2. The user perspective(ユーザーの視点)

Borgのユーザーは、Googleのアプリケーションとサービスを実行するGoogleの開発者とシステム管理者(サイトリライアビリティエンジニア、またはSRE)です。

ユーザーは自分の作業を”ジョブ”という形でBorgに送信します。各ジョブは、すべて同じプログラム(バイナリ)を実行する1つ以上のタスクで構成されます。

各ジョブは、1つのBorgセル(マシンをセットとしてまとめた1つの単位)内で実行されます。

このセクションの残りの部分では、Borgのユーザー視点で公開される主な機能について説明します。

2.1 ワークロード(The workload)

Borgセルは、2つの主要部分で異種のワークロードを実行します。

1つ目は、「決してダウンすることがない」長期間実行されるサービスであり、短期間でレイテンシセンシティブ(数µsから数百ms)なリクエストを処理します。このようなサービスは、GmailGoogleドキュメント、ウェブ検索などのエンドユーザー向け製品、および内部インフラストラクチャサービス(BigTableなど)に使用されます。

2つ目は、完了するまでに数秒から数日かかるバッチジョブです。これらは、短期的なパフォーマンスの変動にあまり影響されません。こうしたワークロードの混在状況はセルごとに異なり、主要なテナントに応じてアプリケーションの様々な組み合わせで実行します(例えば、一部のセルはバッチに特化したものになっています)し、時間の経過とともに変化します。バッチジョブは出入りが激しく、エンド・ユーザー向けのサービスジョブは日中での利用を多く示します。 Borgには、これらのケースを全て同様に上手く処理することが求められます。

Borgの代表的なワークロードは、広範囲に分析されてきた[80]、2011年(例:[68]および[1、26、27、57])の公開されている一ヶ月間のトレースで見つけることができます。

ここ数年、社内のMapReduceシステム[23]、FlumeJava[18]、Millwheel [3]、Pregel[59]など、多くのアプリケーションフレームワークがBorgの上に構築されてきました。
これらのほとんどには、マスタージョブと1つ以上のワーカージョブを送信するコントローラーがあります。最初の2つは、YARNのアプリケーションマネージャーと同様の役割を果たします[76]。
GFS [34]やその後継のCFSBigtable [19]、Megastore [8]などの分散ストレージシステムはすべてBorg上で動作します。

この論文では、優先順位の高いBorgジョブを「プロダクション(prod)」、残りを「非プロダクション(non-prod)」として分類します。ほとんどの長時間実行サーバージョブはprodです。ほとんどのバッチジョブはnon-prodです。
代表的なセルでは、prodジョブは合計CPUリソースの約70%に割り当てられ、合計CPU使用率の約60%を表します。合計メモリの約55%が割り当てられ、合計メモリ使用量の約85%に相当します。

割り当てと使用量の不一致は、§5.5で重要になります。

2.2 クラスターとセル(Clusters and cells)

セル内のマシンは単一のクラスタに属しており、これらのマシンを接続する高性能なデータセンター規模のネットワークファブリックによって定義されます。 クラスターは単一のデータセンターの建物内に存在し、建物の集合がサイトを構成します。
通常、クラスターは1つの大きなセルを収容し、少数の小規模なテストセルまたは特定用途のセルを持つ場合があります。
我々は単一障害点を徹底的に回避しています。

セルの規模の中央値は、テストセルを除くと約1万マシンです。もっと大きいものもあります。
セル内のマシンは、サイズ(CPU、RAM、ディスク、ネットワーク)、プロセッサーのタイプ、パフォーマンス、外部IPアドレスやフラッシュ・ストレージなどの機能など、さまざまな面で異なります。Borgは、タスクを実行するセル内の場所を決定し、リソースを割り当て、プログラムやその他の依存関係をインストールし、それらの状態を監視し、失敗した場合には再起動することによって、これらの差分からユーザーを隔離します。

2.3 ジョブとタスク(Jobs and tasks)

Borgジョブのプロパティには、名前、所有者、および所有しているタスクの数が含まれます。
ジョブには、プロセッサアーキテクチャ、OSバージョン、外部IPアドレスなどの特定の属性を持つマシンでタスクを強制的に実行するための制約があります。
制約はハードにもソフトにもすることができます。後者は要件ではなく設定のように機能します。 ジョブの開始は、前のジョブが終了するまで延期できます。ジョブは1つのセルで実行されます。 各タスクは、マシンのコンテナで実行されているLinuxプロセスのセットにマップされます[62]。 Borgワークロードの大部分は仮想マシンVM)内で実行されません。これは、仮想化のコストを支払う必要がないためです。 また、システムは、ハードウェアで仮想化をサポートしていないプロセッサにかなりの投資をしたときに設計されました。

タスクには、リソース要件やジョブ内のタスクのインデックスなどのプロパティもあります。ほとんどのタスクプロパティはジョブ内のすべてのタスクで同じですが、たとえばタスク固有のコマンドラインフラグを提供するためにオーバーライドできます。 各リソースディメンション(CPUコア、RAM、ディスクスペース、ディスクアクセスレート、TCPポート2など)は、個別に細かく指定されます。固定サイズのバケットやスロットを課していません(§5.4)。
Borgプログラムは、ランタイム環境への依存を減らすために静的にリンクされており、Borgによってインストールが調整されるバイナリとデータファイルのパッケージとして構造化されています。
ユーザーは、最も一般的にはコマンドラインツール、他のBorgジョブ、または監視システム(§2.6)からBorgにリモートプロシージャコール(RPC)を発行することにより、ジョブを操作します。ほとんどのジョブの概要は、宣言的な構成言語BCLで記述されています。これはGCL[12]の変形で、いくつかのBorg特有のキーワードで拡張されたprotobufファイル[67]を生成します。
GCLはラムダ関数を提供して計算を可能にし、アプリケーションが環境に合わせて設定を調整するために使用します。数万のBCLファイルは1000行を超えており、数千万行のBCLを蓄積しています。Borgのジョブ構成は、Aurora構成ファイルと類似しています[6]。 図2は、ジョブとタスクが存続期間中に通過する状態を示しています。

f:id:inductor:20191030004905p:plain
図2:ジョブとタスクの状態ダイアグラム。ユーザーは送信、強制終了および更新の遷移をトリガーできます。

ユーザーは、新しいジョブ構成をBorgにプッシュし、タスクを新しい仕様に更新するようBorgに指示することにより、実行中のジョブの一部またはすべてのタスクのプロパティを変更できます。これは、ジョブが閉じられる(コミットされる)まで簡単に元に戻すことができる、軽量の非アトミックトランザクションとして機能します。更新は通常、ローリング方式で行われ、更新が引き起こすタスクの中断(再スケジュールまたはプリエンプション)の数に制限を課すことができます。より多くの中断を引き起こす変更はスキップされます。

一部のタスクの更新(たとえば、新しいバイナリのプッシュ)では、常にタスクを再起動する必要があります。一部(リソース要件の増加や制約の変更など)により、タスクがマシンに適合しなくなり、タスクが停止および再スケジュールされる場合があります。また、一部のタスク(優先度の変更など)は、タスクを再起動または移動せずにいつでも実行できます。

タスクは、SIGKILLによってプリエンプトされる前にUnix SIGTERMシグナルを介して通知されるように要求できるため、クリーンアップ、状態の保存、現在実行中の要求の終了、および新しい要求の拒否を行う時間ができます。 プリエンプターが遅延限界を設定すると、実際の通知は少なくなる場合があります。実際には、通知は約80%の時間で配信されます。

2.4 配置(Allocs)

Borg alloc(割り当ての略)は、ひとつ以上のタスクを実行可能なマシン上で予約されたリソースの集合です。リソースは、使用されているかどうかにかかわらず割り当てられたままになります。
Allocを使用すると、将来のタスクのためにリソースを確保したり、タスクを停止してから再開するまでの間にリソースを保持したり、異なるジョブから同じマシンにタスクを収集したりできます。たとえば、Webサーバーインスタンスや、サーバのURLログをローカルディスクから分散ファイルシステムにコピーする関連するログ保存タスクなどです。allocのリソースは、マシンのリソースと同様に扱われます。1つの内部で実行される複数のタスクは、そのリソースを共有します。allocを別のマシンに再配置する必要がある場合、そのタスクのスケジュールが変更されます。

allocセットはジョブのようなもので、複数のマシンでリソースを予約するallocのグループです。作成したallocセットでは、1つ以上のジョブをサブミットして実行できます。
簡単のため、「タスク」はallocまたはトップレベルのタスク(allocの外部にある)と定義し、「ジョブ」はジョブまたはallocのセットと定義します。

2.5 優先度、クオータ及びアドミッション制御(Priority, quota, and admission control)

収容できる以上のワークロードが現れた場合どうなるでしょうか?これに対するソリューションは、優先度とクオータです。

すべてのジョブには、優先順位という小さな正の整数があります。優先度の高いタスクは、優先度の低いタスクを犠牲にしてリソースを取得できます。
Borgは、監視、プロダクション、バッチ、およびベストエフォート(テストまたはフリーとも呼ばれます)など、さまざまな用途の重複しない優先順位の幅を(優先順位の低い順に)定義します。
この論文では、prodジョブとは、監視およびプロダクションのジョブを指します。

中断されたタスクはセル内の別の場所で再スケジュールされることがよくありますが、優先度の高いタスクが優先度がやや低いタスクにぶつかると、優先度のカスケードが発生する可能性があります。 この大部分を排除するために、プロダクションのタスクが互いに先取りすることを禁止します。 細粒度の優先度は他の状況でも役立ちます。たとえば、MapReduceマスタータスクは、信頼性を向上させるために、制御するワーカーよりもわずかに高い優先度で実行されます。

優先度は、セルで実行中または実行を待機しているジョブの相対的な重要度を表します。クオータを使用して、スケジューリングのために許可するジョブを決定します。クォータは、一定期間(通常は数か月間)の特定の優先度におけるリソース量(CPU、RAM、ディスクなど)のベクトルとして表されます。
この量は、ユーザーのジョブリクエストが一度に要求できるリソースの最大量を指定します(たとえば、「セルxxにおいて今から7月末までのプロダクションの優先度で20 TiBのRAM」)。クォータチェックは、スケジューリングではなくアドミッションコントロールの一部です。クォータが不十分なジョブは、送信時にすぐに拒否されます。

優先度の高いクォータは、優先度の低いクォータよりもコストがかかります。プロダクション優先度の割り当ては、セルで使用可能な実際のリソースに制限されているため、割り当てに適合する生産優先度のジョブを送信するユーザーは、フラグメント化と制約を剰余演算してジョブを実行できるようになります。
ユーザーが必要以上にクォータを確保することは推奨していませんが、多くのユーザーは、アプリケーションのユーザーベースが拡大した場合に将来の不足を防ぐため、過剰確保します。これに対応するには、優先度の低いレベルでクォータを過剰に配布します。すべてのユーザーは、優先度ゼロの無限のクォータを持っていますが、リソースが過剰にサブスクライブされるため、これを実行するのは困難です。
優先度の低いジョブは受け入れられますが、リソースが不足しているため、保留中(予定外)のままになる場合があります。

クォータの割り当てはBorg以外で処理され、物理的なキャパシティプランと密接に関連しています。物理的なキャパシティプランの結果は、異なるデータセンターにおけるクォータの価格(コスト)と可用性に反映されます。 ユーザー・ジョブは、必要な優先度で十分なクォータがある場合にのみ許可されます。 クォータを使用すると、Dominant Resource Fairness(DRF)[29、35、36、66]などのポリシーの必要性が減ります。

Borgには、一部のユーザーに特別な特権を与える機能システムがあります。
たとえば、管理者がセル内の任意のジョブを削除または変更できるようにしたり、ユーザーが制限されたカーネル機能やジョブのリソース推定を無効にする(§5.5)などのBorgの動作にアクセスできるようにします。

2.6 命名と監視(Naming and monitoring)

タスクを作成して配置するだけでは不十分です。サービスのクライアントやその他のシステムは、新しいマシンに移動した後でも、それらを見つけることができる必要があります。これを可能にするために、Borgはセル名、ジョブ名、タスク番号を含む各タスクの安定した「Borgネームサービス」(BNS)名を作成します。
Borgは、タスクのホスト名とポートを、この名前でChubby[14]の一貫した高可用性ファイルに書き込みます。これは、RPCシステムがタスクエンドポイントを見つけるために使用します。BNS名はタスクのDNS名の基礎にもなるため、セルccのユーザーubarが所有するジョブjfooの50番目のタスクには、50.jfoo.ubar.cc.borg.google.comからアクセスできます。また、Borgはジョブサイズとタスクの正常性の情報を変更するたびにChubbyに書き込むので、ロードバランサーはリクエストのルーティング先を確認できます。

Borgで実行されるほぼすべてのタスクには、タスクの健全性に関する情報と数千のパフォーマンスメトリック(RPCレイテンシなど)を公開する組み込みHTTPサーバーが含まれています。
BorgはヘルスチェックURLを監視し、すぐに応答しないかHTTPエラーコードを返さないタスクを再起動します。その他のデータは、ダッシュボード用の監視ツールとサービスレベル目標(SLO)違反のアラートによって追跡されます。

Sigmaと呼ばれるサービスは、ユーザーがすべてのジョブ、特定のセルの状態を調べたり、個々のジョブとタスクにドリルダウンしてリソースの動作、詳細なログ、実行履歴、さらに、そして実行結果が調査可能なWebベースのユーザーインターフェイス(UI)を提供します。
私たちのアプリケーションは膨大なログを生成します。これらはディスク領域の不足を避けるために自動的にローテートされ、デバッグを支援するためにタスクの終了後しばらく保持されます。ジョブが実行されていない場合、Borgは「なぜ保留中なのか」という注釈と、セルに合わせてジョブのリソース要求を変更する方法に関するガイダンスを提供します。私達は簡単にスケジュールできる可能性が高い「適合」リソース形状のガイドラインを公開しています。

Borgは、すべてのジョブ送信とタスクイベント、およびDremel[61]を介した対話型SQLのようなインターフェイスを備えたスケーラブルな読み取り専用データストアであるInfrastoreの詳細なタスクごとのリソース使用情報を記録します。
このデータは、使用量ベースの課金、ジョブとシステムの障害のデバッグ、および長期的な容量計画に使用され、Googleクラスターワークロードトレースのデータも提供しました[80]。

これらの機能はすべて、ユーザーがBorgとそのジョブの動作を理解およびデバッグし、SREが1人あたり数万台のマシンを管理するのに役立ちます。


つかれた!!!!

つぎは「3. Borg architecture」からです。

inductor.hatenablog.com