inductor's blog

nothing but self note :)

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

このエントリーについて

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

blog.inductor.me

今回は前回の続きでセクション5からです!前回のエントリーはこちらです。

blog.inductor.me

5. 稼働率(Utilization)

Borgの主な目標の1つは、Googleのマシン群を効率的に使用することで、大きな財政投資でもあります(使用率を数パーセント上げると、数百万ドルの節約になります)。 本セクションでは、Borgがそのために使用するポリシーと手法の一部について説明し、評価します。

f:id:inductor:20200202153906p:plain
(a)各セルの左側の列は、元のサイズと結合されたワークロードを示し、右側の列は分離されたケースを示しています

f:id:inductor:20200202154410p:plain
(b)15個の代表的なセルのワークロードを分離した場合に必要となる追加マシンの累積分布関数

図5:プロダクションと非プロダクションのワークロードを異なるセルに分離するには、より多くのマシンが必要です。上記2つのグラフは、プロダクションワークロードと非プロダクションワークロードが別々のセルに送信された場合に必要な追加のマシンの数を示し、単一のセルでワークロードを実行するために必要なマシンの最小数の割合として表されます。後者の累積分布関数では、各セルに表示される値は、実験試行で生成されたさまざまなセルサイズの90パーセンタイルから得られます。エラーバーには、試行から得られた値の完全な範囲を示します。

5.1 評価方法(Evaluation methodology)

稼働させるジョブには配置の制約があり、まれに発生するワークロードのスパイクを処理する必要もあります。マシンは異種混合であり、サービスジョブから回収されたリソースでバッチジョブを実行します。したがって、ポリシーの選択を評価するには、「平均使用率」よりも高度なメトリックが必要でした。ワークロードが与えられると、そのワークロードが収まらなくなるまでマシンを取り外し、ワークロードをゼロから繰り返し再パックして、不運な構成でハングアップしないようにすることで、セルがどれだけ小さくなるのかを見つけました。これにより、クリーンな終了条件が提供され、合成ワークロードの生成とモデリングの落とし穴なしに自動比較が容易になりました[31]。評価手法の定量的比較は[78]にあります。詳細は驚くほど微細です。

実稼働しているプロダクション用のセルで実験を行うことはできませんでしたが、Fauxmasterを使用して、すべての制約、実際の制限、予約、および使用データを含む、実際の生産セルとワークロードからのデータを使用して、忠実度の高いシミュレーション結果を取得しました。このデータは、2014年10月1日水曜日14:00 PDTに取得されたBorgのチェックポイントからのものです(他のチェックポイントでも同様の結果が得られました)。特別な目的、テスト、および小さい(<5000マシン)セルを最初に排除することにより、評価対象の15個のBorgセルを選択し、次に残りの母集団をサンプリングして、サイズの範囲全体でほぼ均等に広げました。

圧縮されたセルでマシンの不均一性を維持するために、削除するマシンをランダムに選択しました。ワークロードの不均一性を維持するために、特定のマシン(Borgletsなど)に関連付けられたサーバーおよびストレージタスクを除き、すべてを維持しました。元のセルサイズの半分よりも大きいジョブのハード制約をソフト制約に変更し、非常に「気難しい(条件が多い)」ために少数のマシンにしか配置できない場合、最大0.2%のタスクが保留になることを許可しました。広範な実験により、これにより低い分散で再現可能な結果が得られることが示されました。元のセルよりも大きなセルが必要な場合は、圧縮する前に元のセルを数回複製しました。さらにセルが必要な場合は、元のクローンを作成しました。

f:id:inductor:20200202160936p:plain
図6: ユーザーを分離するには、より多くのマシンが必要です。表示されているしきい値よりも大きいユーザーに5つの異なるセルのプライベートセルが与えられた場合に必要なセルの総数と追加のマシンの相関図

各実験は、異なる乱数シードを持つ各セルに対して11回繰り返されました。グラフでは、エラーバーを使用して必要なマシン数の最小値と最大値を表示し、90パーセンタイルの値を「結果」として選出しています。平均値または中央値は、ワークロードが適切であることを合理的に確認したい場合にシステム管理者が行うことを反映していません。セルの圧縮は、スケジューリングポリシーを比較するための公平で一貫した方法を提供し、コストメリットの結果に直接つながると考えています。ポリシーが優れていると、同じワークロードを実行するマシンが少なくて済みます。

私たちの実験は、長期稼働するワークロードトレースを再生するのではなく、ある時点からのワークロードのスケジューリング(パッキング)に焦点を合わせました。 これは、オープンおよびクローズのキューイングモデル[71、79]に対処することの難しさを回避するためでもあり、従来の完了までのメトリックが長期稼働するサービスの環境に適用されず、結果が大幅に異なるとは考えられないため、比較することもできるから、という理由もありますし、あるいは実用上の問題もあります。具体的には、ある時点で実験のために200000のCPUコアをBorgで消費していることがわかりました。Googleの規模であってもこれは重要な投資です。

本番環境では、ワークロードの増加、時折発生する「ブラックスワン」イベント(※訳注: ブラックスワン理論による、予測できない金融危機や自然災害などのこと)、負荷スパイク、マシン障害、ハードウェアのアップグレード、および大規模な部分的な障害(電源バスダクトなど)のために意図的に大きな余裕を残しています。図4は、セル圧縮を適用した場合に実際のセルがどれだけ小さくなるかを示しています。以下のグラフのベースラインは、これらの圧縮されたサイズを使用しています。

f:id:inductor:20200202180121p:plain
(a)5つの異なる元のセルの小さいセルの数の関数として必要になる追加のマシン

f:id:inductor:20200202180204p:plain
(b)15の異なるセルをそれぞれ2、5、または10のセルに分割するために必要な追加マシンの累積分布関数

図7: セルを小さなセルに分割するには、より多くのマシンが必要です。 これらの特定のセルをさまざまな数の小さなセルに分割した場合に必要となる追加のマシン(単一セルの場合の割合として)

5.2 セルの共有(Cell sharing)

ほぼすべてのマシンがプロダクション用と非プロダクション用の両方のタスクを同時に実行します。これは実に共有Borgセルのマシン全体の98%、Borgが管理するマシンのセット全体では83%に該当します(特別な用途のための専用のセルがいくつかあります)。

他の多くの組織では、ユーザー向けのジョブとバッチジョブを別々のクラスターで実行しているため、同じことを行うとどうなるかを調べました。図5は、製品と非製品の作業を分離するには、ワークロードを実行するために中央セルに20〜30%多いマシンが必要であることを示しています。これは、プロダクション用ジョブは通常、まれなワークロードスパイクを処理するためにリソースを予約しますが、ほとんどの場合これらのリソースを使用しないためです。Borgは未使用のリソース(§5.5)を回収して、プロダクション用以外のジョブの大部分を実行するため、全体的に必要なマシンは少なくなります。

ほとんどのBorgセルは、何千人ものユーザーによって共有されています。図6はその理由を示しています。このテストでは、ユーザーが少なくとも10TiB(または100TiB)のメモリを消費した場合、ユーザーのワークロードを新しいセルに分割します。既存のポリシーは適切に見えます。しきい値が高くても、2〜16倍のセルと20〜150%の追加マシンが必要になります。繰り返しになりますが、リソースをプールすると、コストが大幅に削減されます。

ただし、無関係のユーザーとジョブタイプを同じマシンにパックすると、CPUの干渉が発生する可能性があります。それを補うためにさらにマシンが必要になるでしょうか?これを評価するために、同じクロック速度で同じマシンタイプで実行されている異なる環境のタスクについて、CPI(命令あたりのサイクル)がどのように変化するかを調べました。これらの条件下では、CPIの値は同等であり、CPIを2倍にするとCPUにバインドされたプログラムの実行時間が2倍になるため、パフォーマンス干渉のプロキシとして使用できます。データは、1週間に12000個のランダムに選択されたprodタスクから収集され、[83]で説明されているハードウェアプロファイリングインフラストラクチャを使用して5分間隔でサイクルと命令をカウントし、CPU時間の1秒ごとに等しくカウントされるようにサンプルを重み付けしました。結果は明確ではありませんでした。

(1)CPIは、同じ時間間隔での2つの測定値(マシンのCPU使用率全体とマシンのタスク数)と正の相関関係があることがわかりました。マシンにタスクを追加すると、他のタスクのCPIが0.3%増加します(データに適合した線形モデルを使用)。マシンのCPU使用率が10%増加すると、CPIは2%未満増加します。しかし、相関関係は統計的に有意ですが、CPI測定で見た分散の5%分しか説明していません。アプリケーションの固有の違いや特定の干渉パターンなど、他の要因が支配的です[24、83]。

(2)共有セルからサンプリングしたCPIを、アプリケーションの多様性が少ない少数の専用セルのCPIと比較すると、共有セルの平均CPIは1.58(σ= 0.35)、専用セルの平均CPIは1.53(σ= 0.32)でした。つまり、共有セルのCPUパフォーマンスは約3%低下します。

(3)さまざまなセルのアプリケーションがさまざまなワークロードを持っているか、選択バイアス(さらに干渉に敏感なプログラムが専用のセルに移動された可能性があります)に悩まされるという懸念に対処するために、両方のタイプのセルのすべてのマシンで実行されるBorgletのCPIを調べました。専用セルのCPIは1.20(σ= 0.29)、共有セルのCPIは1.43(σ= 0.45)であり、専用セルと共有セルの1.19倍の速さで動作することを示唆しています。これにより、負荷の軽いマシンの効果が過大になりますが、結果にわずかに偏り、専用のセルが優先されます。

これらの実験は、ウェアハウススケールでのパフォーマンス比較が難しいことを確認しており、[51]の観察結果を補強するとともに、共有によってプログラムの実行コストが大幅に増加しないことを示唆しています。

しかし、私たちの結果の中で最も好ましくないものを想定しても、共有セルのアプローチにまだ分があります。CPUの速度低下は、いくつかの異なるパーティションスキームで必要なマシンの減少よりも重要であり、共有の利点はCPUだけでなくメモリやディスクを含むすべてのリソースに適用されます。

f:id:inductor:20200202203730p:plain
図11: リソースの推定により、未使用のリソースを特定できます。点線は、15セルにわたるタスクの要求(制限)に対するCPUおよびメモリの使用率のCDFを示しています。ほとんどのタスクは制限よりもはるかに少ないですが、いくつかは要求よりも多くのCPUを使用します。実線は、CPU予約とメモリ予約の制限に対する比率のCDFを示しています。これらは100%に近い値です。 直線は、リソース推定プロセスの成果物です。
数。目立った値はありませんが、いくつかの整数CPUコアサイズがやや幾分か際立っています。">f:id:inductor:20200202203200p:plain
図8: ほとんどのタスクに適したバケットサイズがないことを示す、サンプルセル全体の要求されたCPUおよびメモリ要求の累積分布関数。目立った値はありませんが、いくつかの整数CPUコアサイズがやや幾分か際立っています。

f:id:inductor:20200202203519p:plain
図9: 「バケット化」されたリソース要件には、より多くのマシンが必要です。CPUおよびメモリリクエストを15セルにわたって次に近い2のべき乗に切り上げることによって生じる追加オーバーヘッドの累積分布関数。下限と上限は実際の値にまたがっています(本文を参照)。

f:id:inductor:20200202203647p:plain
図10: リソースの再利用が非常に効果的であることを示す、15個の代表セルに対して無効にした場合に必要となる追加マシンの累積分布関数

f:id:inductor:20200202203730p:plain
図11: リソースの推定により、未使用のリソースを特定できます。点線は、15セルにわたるタスクの要求(制限)に対するCPUおよびメモリの使用率のCDFを示しています。ほとんどのタスクは制限よりもはるかに少ないですが、いくつかは要求よりも多くのCPUを使用します。実線は、CPU予約とメモリ予約の制限に対する比率のCDFを示しています。これらは100%に近い値です。 直線は、リソース推定プロセスの成果物です。

5.3 巨大なセル(Large cells)

Googleは大きなセルを作成し、大規模な計算を実行できるようにし、リソースの断片化を減らします。セルのワークロードを複数の小さなセルに分割して、最初にジョブをランダムに並べ替えてからラウンドロビン方式でパーティション間で割り当て、後者の効果をテストしました。図7は、小さなセルを使用すると、さらに多くのマシンが必要になることを示しています。

5.4 きめ細かいリソース要求(Fine-grained resource requests)

Borgのユーザーは、CPUをミリコア単位で、メモリとディスク領域をバイト単位で要求します(コアは、マシンタイプ全体のパフォーマンスを標準化したプロセッサハイパースレッドです)。図8は、この粒度を活用していることを示しています。要求されたメモリまたはCPUコアの量に明らかな「スイートスポット」はほとんどなく、これらのリソース間の明らかな相関はほとんどありません。これらの分布は、90パーセンタイル以上でわずかに大きなメモリ要求が見られることを除いて、[68]で提示されたものと非常に似ています。

一連の固定サイズのコンテナーまたは仮想マシンを提供することは、IaaS(インフラストラクチャーとしてのサービス)プロバイダー[7、33]の間では一般的ですが、私たちのニーズにはうまく合いません。 これを示すために、プロダクション用ジョブとallocs(§2.4)のCPUコアとメモリリソースの制限を、0.5コアと1GiBを始点とし、各リソースの次元において次に近い2のべき乗に切り上げて「バケット化」しました。 図9は、これを行うと中央値ケースで30〜50%多いリソースが必要になることを示しています。 この上限は、圧縮を開始する前に元のセルを4倍にした後に適合しなかった大きなタスクに対して、マシン全体を割り当てることに由来します。下限は、こうした大きなタスクの保留を許容するところに由来しています。(これは、4つを超えるバケットをサポートし、CPUおよびRAMの容量を個別に拡張できるため、[37]で報告された約100%のオーバーヘッドよりも小さくなります。)

f:id:inductor:20200202203937p:plain
図12:

5.5 リソースの開拓(Resource reclamation)

ジョブでは、リソース制限(各タスクに付与する必要があるリソースの上限)を指定できます。この制限は、ユーザーがジョブを許可するのに十分なクォータを持っているかどうかを判断し、特定のマシンにタスクをスケジュールするための十分な空きリソースがあるかどうかを判断するために使用されます。必要以上にクラウドでクォータを購入するユーザーがいるように、タスクが使用するよりも多くのリソースを要求するユーザーがいます。Borgは通常、要求よりも多くのRAMまたはディスクスペースを使用しようとしたり、CPUを実際の要件よりも多く要求するタスクを殺します。さらに、一部のタスクでは、すべてのリソースを使用する必要がある場合があります(たとえば、ピーク時またはサービス拒否攻撃に対処している間)が、ほとんどの場合はそうではありません。

現在消費されていない割り当て済みリソースを無駄にするのではなく、タスクが使用するリソースの数を見積もり、バッチジョブなどの低品質のリソースを許容できる作業のために残りを回収します。このプロセス全体をリソースの開拓(Resource reclamation)と呼びます。見積もり処理はタスクの予約と呼ばれ、Borgletによってキャプチャされたきめ細かい使用量(リソース消費)情報を使用して、Borgmasterによって数秒ごとに計算されます。最初の予約は、リソース要求(制限)と等しく設定されます300秒後、起動時の過渡現象を考慮して、実際の使用量と安全マージンに向かってゆっくりと減衰します。使用量が予約量を超えると、予約は急速に増加します。

Borgスケジューラーは制限を使用してプロダクション用タスクの実行可能性(§3.2)を計算するため、開拓されたリソースに依存せず、リソースの過剰確保にも見舞われません。非プロダクション用タスクの場合、既存のタスクの予約分を使用するため、新しいタスクを再利用されたリソースにスケジュールできます。

予約量(予測)が間違っている場合、すべてのタスクが制限よりも少ない場合でも、マシンは実行時にリソースを使い果たす可能性があります。これが発生した場合、非プロダクション用タスクを殺すか、抑制します。

図10は、リソースを再利用しないと、さらに多くのマシンが必要になることを示しています。ワークロードの約20%(§6.2)は、中央のセルの開拓リソースで実行されます。

図11にて詳細を確認できます。図11は、予約と使用量の制限に対する比率を示しています。メモリの制限を超えるタスクは、優先度に関係なく、リソースが必要な場合に最初にプリエンプトされるため、タスクがメモリの制限を超えることはまれです。一方、CPUは容易に調整できるため、短期的なスパイクにより、使用量が予約をかなり無害に押し上げる可能性があります。

図11は、リソースの再利用が不必要に保守的である可能性があることを示しています。予約ラインと使用ラインの間に重要な領域があります。これをテストするために、稼働中のプロダクション用セルを選択し、リソース利用状況を推定するアルゴリズムのパラメーターにおける安全マージンを1週間減らしてぎりぎりの設定に調整し、次に、基準値とぎりぎりな値の中間設定に調整しました、そしてベースライン値に戻しました。

図12では何が起こったかを示しています。予約量は2週目が明らかに使用量に近く、3週目にはやや少なくなり、基準の週(1週目と4週目)に最大のギャップが表示されます。予想どおり、メモリ不足(OOM)イベントの割合は2週目と3.5週目にわずかに増加しました。これらの結果を確認した後、純利益がマイナス面を上回っていると判断し、中程度のリソース開拓パラメーターを他のセルに展開しました。

f:id:inductor:20200202204045p:plain
図13

6. アイソレーション(Isolation)

マシンの50%は9つ以上のタスクを実行します。90パーセンタイルのマシンには約25のタスクがあり、約4500のスレッドが動いています[83]。アプリケーション間でマシンを共有すると使用率が向上しますが、タスクが相互に干渉しないようにするための優れたメカニズムも必要です。これは、セキュリティとパフォーマンスの両方に適用されます。

6.1 セキュリティの分離(Security isolation)

我々の基盤では、を、同じマシン上の複数のタスク間の主要なセキュリティ分離メカニズムとしてLinux chroot jailを使用しています。リモートデバッグを可能にするために、sshキーを自動的に配布(および取り消し)して、ユーザーがタスクを実行している間にのみユーザーがマシンにアクセスできるようにしました。ほとんどの場合、これはborgsshコマンドに置き換えられました。このコマンドは、Borgletと連携して、タスクと同じchrootおよびcgroupで実行されるシェルへのssh接続を構築し、アクセスをより厳密にロックダウンします。

VMとセキュリティサンドボックスの技術は、GoogleのAppEngine(GAE)[38]とGoogle Compute Engine(GCE)で外部ソフトウェアを実行する際に使用されます。Borgタスクとして実行されるKVMプロセス[54]でホストされた各VMを実行します。

6.2 パフォーマンスの分離(Performance isolation)

初期のバージョンのBorgletには、メモリ、ディスク容量、CPUサイクルの事後使用チェックと、メモリまたはディスクを過剰に使用したタスクの終了と、CPUを過剰に使用したタスクを抑制するLinuxのCPU優先度の積極的な適用との組み合わせた、比較的原始的なリソース分離の強制がありました。しかし、不正なタスクがマシン上の他のタスクのパフォーマンスに影響を与えることは依然として容易すぎるため、一部のユーザーはリソース要求を増大させて、Borgがタスクと共同スケジュールできるタスクの数を減らし、使用率を低下させました。リソース回収は、安全マージンが関係しているため、すべてではなく、一部の余剰を回収する可能性があります。最も極端なケースでは、専用のマシンまたはセルを使用するような要求を入れていました。

現在、すべてのBorgタスクはLinux cgroupベースのリソースコンテナ[17、58、62]内で実行され、Borgletはコンテナ設定を操作し、OSカーネルがループ内にあるため、制御が大幅に改善されます。 それでも、[60、83]のように、時折低レベルのリソース干渉(メモリ帯域幅やL3キャッシュの汚染など)が発生します。

オーバーロードとオーバーコミットを支援するために、Borgタスクにはアプリケーションクラス(appclass)があります。最も重要な違いは、遅延に敏感な(LS: Latency-Sensitive)appclassと、この論文ではバッチと呼ばれる残りのappclassです。LSタスクは、要求に迅速に応答する必要があるユーザー向けアプリケーションおよび共有インフラサービスに使用されます。優先度の高いLSタスクは最適な処理を受け、一度に数秒間、バッチタスクを一時的に枯渇させることができます。

2番目の分割は、CPUサイクル、ディスクI / O帯域幅などなどのレートベースで強制終了せずにサービスの品質を低下させることでタスクから回収できる圧縮可能なリソースと、メモリ、ディスクスペースなどのタスクを強制終了せずに再利用できない非圧縮リソースの間で行われます。マシンの非圧縮リソースが不足すると、Borgletは残りの予約量が満たされるまで優先度の低いものから高いものへとタスクを即座に終了します。マシンが圧縮可能なリソースを使い果たすと、Borgletは使用量を抑制し(LSタスクを優先)、タスクを強制終了せずに短い負荷スパイクを処理できるようにします。状況が改善されない場合、Borgmasterはマシンから1つ以上のタスクを削除します。

Borgletのユーザー空間におけるコントロールループは、予測される将来のリソース使用量(プロダクション用タスクの場合)またはメモリ負荷(プロダクション用以外の場合)に基づいてメモリをコンテナに割り当てます。カーネルからのメモリ不足(OOM)イベントを処理します。また、メモリ制限を超えて割り当てようとした場合、またはオーバーコミットされたマシンが実際にメモリを使い果たした場合タスクを強制終了します。Linuxのファイルキャッシュは欲しがりで、正確なメモリアカウンティングが必要なために実装を大幅に複雑にします。

パフォーマンスの分離を改善するために、LSタスクは物理CPUコア全体を予約できます。これにより、他のLSタスクがそれらを使用できなくなります。バッチタスクは任意のコアで実行できますが、LSタスクに関連する小さな共有スケジューラが与えられます。Borgletは、貪欲なLSタスクのリソースキャップを動的に調整してバッチタスクが数分間飢えないようにし、必要に応じてCFS(Completely Fair Scheduler )の帯域幅制御を選択的に適用します[75]。複数の優先度レベルがあるため、共有は不十分です。

Leverich [56]のように、標準のLinux CPUスケジューラ(CFS)では、低レイテンシと高使用率の両方をサポートするために大幅な調整が必要であることがわかりました。スケジューリングの遅延を減らすために、CFSのバージョンは拡張cgroupごとのロード履歴[16]を使用し、LSタスクによるバッチタスクのプリエンプションを許可し、複数のLSタスクがCPUで実行可能な場合のスケジューリング量を減らします。幸いなことに、私たちのアプリケーションの多くは、要求ごとのスレッドモデルを使用しており、永続的な負荷の不均衡の影響を軽減しています。特にレイテンシ要件が厳しいアプリケーションにはCPUコアを割り当てるために、cpusetsを控えめに使用します。これらの取り組みの結果の一部を図13に示します。この領域で作業が継続され、NUMA、ハイパースレッディング、および電力を認識するスレッドの配置とCPU管理([81]など)が追加され、Borgletの制御忠実度が向上します。

タスクは限界までリソースを消費できます。それらのほとんどは、CPUなどの圧縮可能なリソースの範囲を超えて、未使用(スラック)リソースを活用することができます。おそらく予測可能性を高めるために、LSタスクの5%のみがこれを無効にします(バッチタスクの1%未満しかこれを行っていません)。スラックメモリの使用は、タスクが強制終了される可能性が高くなるため、デフォルトでは無効になっていますが、LSタスクの10%がこれをオーバーライドし、バッチタスクの79%がMapReduceフレームワークのデフォルト設定であるためこれを無効にします。これは、開拓されたリソース(§5.5)の結果を補完します。バッチタスクは、未使用のメモリと回収されたメモリを日和見的に利用します。ほとんどの場合、これは機能しますが、LSタスクが急いでリソースを必要とする場合、バッチタスクは犠牲になります。


5章では主にリソースの効率的な利用を促進するためのさまざまな試みが行われたことが示されています。

Kubernetesでも同じことが言えると思いますが、複数のマシンをクラスタリングしてアプリケーションの実行環境にするためには、アプリケーションが持つさまざまな特性を受け入れ、それにあったリソース管理が必要です。

ノイジーネイバーと呼ばれるような日々の運用においてユーザーが受け入れたくない外的要因による障害を未然に防ぐためにも、このように様々な試行でもって環境の効率化と改善を継続的に行っているのはシンプルに素晴らしいと思います。

6章では、マルチテナントな設計を持つBorgが、どのようにしてセキュリティ的に、そしてリソース管理的に隔離された環境をユーザーに提供できているのかという部分にも言及しています。

メモリー管理やCPUのクオータ管理におけるオーバーコミットとの付き合い方など、学ぶべきことも多いなと思いました。

さてさて、いよいよ論文も残り2セクションです。次は7. Related workからですが、やりきれそうだったら8のFuture workまでやりきりたいですね。