YARNサービスレジストリ

はじめに と 概念

このドキュメントでは、以下の2つの問題を解決するために構築されたHadoopサービスレジストリについて説明します。

  1. クライアントは、YARNによってデプロイされたサービスと、そのようなサービスを構成するコンポーネントとどのように通信できるか?
  2. Hadoopコアサービスを登録および発見できるようにすることで、構成パラメーターを削減し、コアサービスをより簡単に移動できるようにする。

サービスの登録と検出は、ゼロックスのGrapevine Serviceにまで遡る、分散コンピューティングにおける長年の問題です。この提案は、YARNによってデプロイされた分散アプリケーションを特定し、これらのアプリケーションとの通信に必要なバインディング情報を決定するためのレジストリに関するものです。

定義

サービス: Hadoop YARNクラスターにデプロイされた、またはそこから到達可能な可能性のある分散アプリケーション。例:Apache HBase、Apache hcatalog、Apache Storm。サービスは短命であることも長命であることもあります。

サービスクラス: サービスの種類を表す名前で、レジストリ内のパスとして使用され、DNS互換のパス命名スキームに一致します。例:org-apache-hbaseorg-apache-hcatalog

コンポーネント: サービス内の分散要素。例:HBaseマスターノード、HBaseリージョンサーバー、HBase RESTサーバー。

サービスインスタンス: アプリケーションの単一のインスタンス。例:HBaseクラスターdemo1。サービスインスタンスは、サービスを構成するコンポーネントのインスタンスが実行中の場合に実行されています。これは、分散コンピューティングの意味での「ライブ」を意味するものではなく、単にプロセスが実行されていることを意味します。

コンポーネントインスタンス: サービスインスタンス内のコンポーネントの単一のインスタンス。例:ホストrack1server6上のHBaseマスターノード、またはホストrack3server40上のリージョンサーバー。

エンドポイント: サービスインスタンスまたはコンポーネントインスタンスとのバインドの1つの手段。例:HBaseのApache Zookeeperバインディング、リージョンサーバーのJava JMXポート、HBaseマスターのWeb UI、HBase RESTコンポーネントインスタンスのREST API。エンドポイントは、サービスインスタンス内で使用するための内部、またはサービスインスタンスのクライアントが使用するための外部である場合があります。

サービスレコード: エンドポイントのリストを含む、サービスインスタンスまたはコンポーネントインスタンスを記述するレジストリ内のレコード。

YARNリソースマネージャー、 “RM”: クライアントアプリケーションがYARNクラスターに作業(サービスインスタンスをデプロイする要求を含む)を送信できるようにするYARNコンポーネント。RMは、実行中のすべてのアプリケーションの状態を保持します。

YARNアプリケーション: YARNを介してデプロイされたアプリケーション。すべてのアプリケーションインスタンスには、一意のアプリケーションIDがあります。

YARNアプリケーションマスター、“AM”: RMによってスケジュールおよびデプロイされるアプリケーション固有のコンポーネント。このアプリケーションのすべての他のコンポーネントインスタンスを要求および管理することを含め、アプリケーションの内部状態を維持する役割を担います。YARN RMは、AMの障害を検出し、再スケジュールすることで対応します。

YARNコンテナ: コンポーネントインスタンスのリソース割り当て(CPUやRAMを含む)。AMは、コンポーネントに必要なコンテナを要求し、割り当てられたコンテナにコンポーネントインスタンスをインスタンス化するコマンドを作成する責任があります。割り当てられたすべてのコンテナには、一意のコンテナIDがあります。

バインディングの問題

Hadoop YARNを使用すると、アプリケーションをHadoopクラスター上で実行できます。これらの一部は、YARNの既存のAPIを使用してアプリケーションIDを使用して管理できるバッチジョブまたはクエリです。さらに、YARNは、Apache Tomcat WebサーバーのプールやApache HBaseクラスターなどの長寿命サービスインスタンスをデプロイできます。YARNは、個々のコンポーネントの要件とサーバーの可用性に応じて、クラスター全体にデプロイします。これらのサービスインスタンスはクライアントによって検出される必要があります。従来、それらのIPがDNSまたは一部の構成ファイルに登録されます。ただし、ホスト名もネットワークポートも事前にわからないYARNでデプロイされたアプリケーションでは、それは実現可能ではありません。

その結果、クライアントが動的にデプロイされたアプリケーションと対話する簡単な方法はありません。

YARNは、YARN Application MasterがWeb URLとIPCアドレスを登録できる基本的なレジストリをサポートしていますが、他のエンドポイント(REST URL、zookeeperパス、Application Masterが実行するタスクのエンドポイントなど)を登録できないため、私たちの目的には十分ではありません。さらに、登録できる情報は、YARNアプリケーションインスタンス(YARNアプリケーションが開始されるたびに変更される一意のインスタンスID)にマッピングされます。これにより、名前付きサービスへの静的参照を介してバインディング情報を解決することや、現在アクティブではないサービスインスタンスの存在をプローブすることさえ不可能になります。

ユースケース

サービス名例

Hadoopコアサービス。

これらは、/servicesパスに書き込む権限を持つアカウントを介して静的に、動的に、またはHadoopクラスター内からアクセスできるリモートサービスの登録としてデプロイできます。

    /services/hdfs
    /services/yarn
    /services/oozie

個々のユーザーに属するYARNデプロイサービス。

    /users/joe/org-apache-hbase/demo1
    /users/joe/org-apache-hbase/demo1/components/regionserver1

登録のユースケース

  1. YARNで実行されていないHadoopコアサービス(例:HDFS)を検出のために登録できます。これは、サービスまたは管理ツールによって実行できます。

  2. YARNによってデプロイされた長寿命のアプリケーションは、クライアントによる検出のために自身を登録します。登録データは、アプリケーションマスター、おそらくサービスインスタンスの単一デプロイメントの寿命よりも長く存続することを目的としています。

  3. サービスのコンポーネントインスタンスは、JMXポートなどの内部バインディング情報を公開して自身を登録します。

  4. YARNでデプロイされたアプリケーションは、静的および動的な両方の依存サービスインスタンスにバインドできます。例:動的なHBaseサービスインスタンス「/users/joe/services/hbase/demo1」にバインドするTomcat Webプール。

  5. コンポーネントインスタンスは、レジストリを使用して、定期的にハートビートするアプリケーションマスターの内部エンドポイントにバインドします。

サポートされていない登録のユースケース

  1. 短命のYARNアプリケーションは、すべてのコンテナを含めてレジストリに自動的に登録され、ジョブが終了すると登録解除されます。多数のコンテナを持つ短命のアプリケーションは、レジストリに過度の負荷をかけます。すべてのYARNアプリケーションには登録のオプションが与えられますが、自動ではありません。アプリケーション作成者は短命のコンテナを登録しないようにアドバイスする必要があります。

ルックアップのユースケース

  1. クライアントアプリケーションは、ユーザー、サービスクラス、インスタンス名がわかっている動的にデプロイされたサービスインスタンス(例:/users/joe/services/hbase/demo1)をルックアップし、サービスに接続するために必要な情報を取得します。

  2. クライアントアプリケーションは、静的にデプロイされたHadoopサービス(例:/services/hdfs)をルックアップします。

  3. Application Masterは、登録されているすべてのコンポーネントインスタンスを列挙し、リストされているJMXポートを検出し、独自のWeb UIを初期化し、これらのエンドポイントへのリンクを提供します。

  4. ユーザーは、/users/joe/services/hbase/demo1にあるプライベートHBaseサービスインスタンスに接続します。

  5. ユーザーは、/services/hbaseにあるクラスターのHBaseサービスに接続します。

  6. ユーザーは、/net/cluster4/services/hdfsにあるリモートHadoopクラスターのファイルシステムへのバインディング情報をルックアップします。登録情報には、リモートファイルシステムのwebhdfs:// URLが含まれています。

  7. ユーザーは、HBaseサービスインスタンスを一覧表示します。

    ls /users/joe/services/hbase
    
  8. ユーザーは、クラスター内のすべてのHbaseサービスを見つけます。

    find -endpointField.api=org.apache.hbase
    
  9. 将来的に可能性があるもの: DNSを介してサービスをルックアップする。

このレジストリ提案は、アプリケーションがサービスエンドポイントを登録する手段を提供し、クライアントがそれらを見つけられるようにすることで、これらのユースケースをサポートすることを目的としています。

サービスレジストリの主な要件

サービスインスタンスの動的な登録を許可します。

  • YARNでデプロイされたサービスインスタンスは、バインディングを登録し、クライアントによって検出できるようにする必要があります。

  • Hadoopコアサービスインスタンスは、サービスエンドポイントを登録できる必要があります。

  • サービスが移動した場合、またはHAフェイルオーバーが発生した場合、バインディングはアップグレード可能である必要があります。

  • サービスインスタンスは、サービス用にさまざまなエンドポイント(Web UI、RPC、REST、Zookeeperなど)を公開できる必要があります。さらに、証明書やその他の公開セキュリティ情報をバインディングの一部として登録できる必要があります。

レジストリサービスのプロパティ

  • レジストリは可用性が高くなければなりません。

  • 規模: 大規模なクラスター内の多数のサービスと多数のクライアント。これにより、サービスが公開できるデータ量が制限されます。

  • 普遍性: 物理、仮想、クラウドのいずれであっても、すべてのYARNクラスターでこれが必要です。

  • 階層的な名前空間と名前をサポートする必要があります。名前の規則はDNSの規則に一致させる必要があります。これにより、プロジェクトの後の段階でDNSプロトコル経由で名前空間にアクセスするオプションが得られます。

  • レジストリAPIの言語/プロトコル

  • クロス言語: 任意の言語に依存しない。クライアントの言語 != サービス

  • レジストリデータを読み取るためのREST API

アクセス制御

  • すべてのユーザーに読み取りアクセスを許可

  • 書き込みは、不正占拠やなりすましを防ぐために制限されています。

リモートアクセス性: Apache Knox経由でのみアクセス可能なクラスターや、クラウド環境でホストされているクラスターでもリモートアクセスをサポートします。

非要件

  • レジストリは、アプリケーション自体のライブネス検出、リーダー選出、またはその他の「共有合意状態」のアクションを実行することを目的としたものではありません。ただし、コンポーネントインスタンス間でバインディング情報を共有する可能性はあります。

  • レジストリは、任意のアプリケーション状態のストア、またはサービスとそのコンポーネントが提供するエンドポイントへのバインディング情報以外の構成データを公開するためのものではありません。このような使用はレジストリに過負荷をかけ、Zookeeperが許可する制限にすぐに達します。

アーキテクチャ

サービスとコンポーネントインスタンスを記述するレコードに文字列名をバインドする基本レジストリサービスを提案します。Zookeeperが多くのプロパティをサポートしているため、基本ネームサービスとしてZKを使用する予定です。サービスレジストリのルートとしてZK名前空間の一部を選択します(デフォルト:yarnRegistry)。

この基本実装の上に、レジストリサービスAPIと、YARNがサービスに使用する命名規則を構築します。レジストリには、ZK経由で直接ではなく、レジストリAPIによってアクセスされます。ZKは単なる実装の選択です(将来変更される可能性は低いですが)。

  1. サービスは、パスサービスレコードと呼ばれる値にバインドすることによって登録されます。パスは階層的で、ルートと区切り文字として/を使用します。

  2. サービスレコードは永続的なznodeとして登録されます。これにより、クライアントコードが一時的な停止に対して回復力があるという前提で、計画された、および計画外のサービスの停止中もレコードが存在し続けることが保証されます。

  3. 各サービスインスタンスのサービスレコードには、そのサービスインスタンスによってエクスポートされたさまざまなプロトコルのエンドポイントが一覧表示されます。

  4. プロトコルエンドポイントごとに、以下を含める必要があります。

    1. Web、REST、IPC、zookeeperなどを含むプロトコル名。(タイプ:文字列)

    2. そのアドレス:このエンドポイントを特定するために使用される具体的な詳細

    3. そのaddressType。これは、バインド文字列の形式です。(URL、ZKパス、hostname:portのペア)。事前定義されたプロトコルについては、バインド文字列がMUSTである形式を定義します。例:protocol==RESTはバインドタイプがURLであることを意味し、protocol==IPCバインドはアドレスタイプhost/portを使用します。

    4. api。これはエンドポイントによって提供されるAPIであり、アプリケーション固有です。例:org.apache.hadoop.namenodeorg.apache.hadoop.webhdfs

  5. エンドポイントはexternal(サービス自体以外のプログラムで使用するため)とinternal(サービスインスタンス内のコンポーネントを接続するため)があります。それらを区別するために、サービスレコードの異なるセクションにリストされます。

  6. コアサービスは、次の規則を使用して登録されます:/services/{servicename} 例:/services/hdfs

  7. YARNサービスは、次の規則を使用して登録する必要があります

    /users/{username}/{serviceclass}/{instancename}
    
  8. コンポーネントインスタンスは、以下に登録する必要があります

    /users/{username}/{serviceclass}/{instancename}/components/{componentname}
    
  9. この規則に従う各ユーザーのサービスには、一意のサービスクラス名が必要です。

  10. 各コンポーネントインスタンスには、そのサービスインスタンスで一意である名前が必要です。YARNでデプロイされたアプリケーションの場合、これはコンテナIDから簡単に導き出すことができます。

一意の名前の要件により、サービスインスタンスまたはコンポーネントインスタンスへのパスが一意であることが保証され、特定のサービスクラスのすべてのインスタンスをサービスクラスパスの子をすべてリストすることで列挙できます。

レジストリモデル

サービスエントリは永続的である必要があります。サービスエントリを削除する時期を決定するのは、YARNおよびその他のツールの責任です。

パス要素

すべてのパス要素は、RFC1123で定義されているホスト名パスの小文字のエントリと一致する必要があります。正規表現は次のとおりです。

([a-z0-9]|([a-z0-9][a-z0-9\-]*[a-z0-9]))

このポリシーにより、レジストリ階層がDNSサービスによってエクスポートされた場合、すべてのサービスクラスと名前が有効になることが保証されます。

ユーザー名では、プラットフォームがスペース、上位Unicode、その他の文字を含むユーザー名を許可する可能性があるため、複雑になります。このようなパスは、国際化されたDNSに使用されるpunycode規則を使用して、有効なDNSホスト名エントリに変換する必要があります。

サービスレコード

サービスレコードには、いくつかの基本情報と、場合によっては空の内部および外部エンドポイントのリストがあります。

サービスレコード

サービスレコードには、いくつかの基本情報と、2つのエンドポイントのリストが含まれています。1つはサービスのユーザー用、もう1つはアプリケーション内での内部使用用です。

名前 説明
型:文字列 常: "JSONServiceRecord"
説明:文字列 人間が読める説明。
外部:List [Endpoint] 外部呼び出し元のサービスエンドポイントのリスト。
内部:List [Endpoint] サービスインスタンス内での内部使用のためのサービスエンドポイントのリスト。

typeフィールドは"JSONServiceRecord"である必要があります。この文字列を義務付けることで、将来のレコードタイプを可能にし、JSONパーサーでデータを解析しようとする前に、この文字列がないバイト配列を迅速に拒否できます。

YARNの永続化ポリシー

属性yarn:idyarn:persistenceは、関連付けられたYARNコンポーネントが完了したときに削除される可能性があるレコードおよび子エントリを指定します。

yarn:idフィールドは、一致するアプリケーション、試行、またはコンテナIDを定義します。yarn:persistence属性は、レコードのクリーンアップのトリガーを定義し、暗黙的にyarn:idフィールドの内容のタイプを定義します。

これらの属性は、ポリシーを実装するためにHadoopクラスターのYARNレイヤーに依存することを示すために、プレフィックス "yarn:"を使用します。レジストリがスタンドアロンで実行される場合(これは完全に可能)、すべてのレコードは暗黙的に永続的になります。

名前 説明 yarn:idフィールドの内容
永久 レコードは手動で削除されるまで存続します。 (未使用)
アプリケーション idフィールドで定義されたYARNアプリケーションが終了すると削除します。 アプリケーションID
アプリケーション試行 現在のYARNアプリケーション試行が終了すると削除します。 アプリケーション試行ID
コンテナ IDフィールドのYARNコンテナが終了すると削除します コンテナID

アプリケーション、アプリケーション試行、またはコンテナが終了したときにクリーンアップするポリシーでは、yarn:idフィールドがアプリケーション、試行、またはコンテナのものと一致する必要があります。誤ったIDが設定されている場合、クリーンアップは実行されません。別のアプリケーションまたはコンテナに設定されている場合、そのアプリケーションのライフサイクルに従ってクリーンアップされます。

エンドポイント

名前 説明
api:文字列としてのURI バインディングの最後に実装されたAPI
プロトコル:文字列 プロトコル。例:`http`、`https`、`hadoop-rpc`、`zookeeper`、`web`、`REST`、`SOAP`、...
アドレスタイプ:文字列 バインディングの形式
アドレス:List [Map [String、String]] アドレスマップのリスト

すべての文字列フィールドには、サイズに制限があり、サービスがテキストの説明に複雑なJSON構造を隠すのを防ぎます。

フィールドaddressType:アドレスタイプ

addressTypeフィールドは、エントリの文字列形式を定義します。

別々のタイプを持つことで、(Webビューアーなどの)ツールはプロトコルを認識しなくてもバインディング文字列を処理できます。

形式 バインディング形式
uri uri:エンドポイントのURI
ホスト名 ホスト名:サービスホスト
inetaddress ホスト名:サービスホスト、ポート:サービスポート
パス パス:汎用UNIXファイルシステムパス
zookeeper ホスト名:サービスホスト、ポート:サービスポート、パス:ZKパス

zookeeperバインディングでは、すべてのエントリがクォーラム内の単一のノードを表し、hostnameフィールドとportフィールドはZKインスタンスのホスト名とそれがリッスンしているポートを定義します。pathフィールドには、アプリケーションが使用するzookeeperパスがリストされます。たとえば、HBaseの場合、これはHBaseクラスターに関する情報を含むznodeを参照します。

パスは、addressesリストのすべてのアドレス要素間で同一である必要があります。これにより、単一のアドレスにクォーラムに接続して関連するznodeに接続するのに十分な情報が含まれることが保証されます。

新しいアドレスタイプを定義できます。標準ではない場合は、文字シーケンス "x-"をプレフィックスとして付けてください。

フィールドapi:API識別子

APIフィールドには、エンドポイントの特定のAPIを識別するURIが含まれている必要があります。混乱を避けるために、これらはAPIごとに一意である必要があります。

APIの一意のURIを提供するために、次の戦略が推奨されます

  1. サービスを定義するWSDLへのURLを使用するSOAP / WS- *規則
  2. REST APIを定義するsvn / gitホストドキュメントへのURL
  3. アプリケーション内のクラスまたはパッケージへのパスが続くclasspathスキーマ。
  4. 生成されたUUIDを使用したuuidスキーマ。

標準のAPI URIが一般的なAPIに対して定義されることを期待しています。このドキュメントでは、2つのそのような非規範的なAPIが使用されています

  • http://:人間向けのWebサイト
  • classpath:javax.management.jmx:JMX管理プロトコル(RMIベース)をサポートするエンドポイント

サービスエントリの例

これは、YARNでデプロイされたtomcatアプリケーションのサービスエントリの例です。

アプリケーションの作成と登録後、レジストリは次のようになります

/users
  /devteam
   /org-apache-tomcat
     /test1
       /components
         /container-1408631738011-0001-01-000002
         /container-1408631738011-0001-01-000001

/users/devteam/org-apache-tomcat/tomcat-testサービスレコードは、アプリケーション全体を記述します。ロードバランサーへのURLをエクスポートします。

{
  "description" : "tomcat-based web application",
  "external" : [ {
    "api" : "http://internal.example.org/restapis/scheduler/20141026v1",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [
     { "uri" : "http://loadbalancer/" },
     { "uri" : "http://loadbalancer2/" }
      ]
  } ],
  "internal" : [ ]
}

サービスインスタンスは、コンテナIDがDNS互換のホスト名に変換された2つのコンポーネントインスタンスから構築されます。エントリは一時的なものとしてマークされます。コンテナ内でエントリが設定された場合、そのコンテナが解放されるか、コンポーネントが失敗すると、エントリは自動的に削除されます。したがって、その永続化ポリシーは「3」、コンテナであると宣言されています。yarn:idフィールドは、このエントリの削除をトリガーするコンテナの完了を識別します

/users/devteam/org-apache-tomcat/test1/components/container-1408631738011-0001-01-000001

{
  "yarn:id" : "container_1408631738011_0001_01_000001",
  "yarn:persistence" : "container",
  "description" : "",
  "external" : [ {
    "api" : "http://internal.example.org/restapis/scheduler/20141026v1",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [{ "uri" : "rack4server3:43572" }  ]
  } ],
  "internal" : [ {
    "api" : "classpath:javax.management.jmx",
    "addressType" : "host/port",
    "protocol" : "rmi",
    "addresses" : [ {
      "host" : "rack4server3",
      "port" : "48551"
    } ]
  } ]
}

コンポーネントインスタンスは、エンドポイントを一覧表示します。外部エンドポイントとしてのパブリックREST API、内部エンドポイントとしてのJMXアドレスです。

/users/devteam/org-apache-tomcat/test1/components/container-1408631738011-0001-01-000002

{
  "registrationTime" : 1408638082445,
  "yarn:id" : "container_1408631738011_0001_01_000002",
  "yarn:persistence" : "container",
  "description" : null,
  "external" : [ {
    "api" : "http://internal.example.org/restapis/scheduler/20141026v1",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ [ "http://rack1server28:35881" ] ]
  } ],
  "internal" : [ {
    "api" : "classpath:javax.management.jmx",
    "addressType" : "host/port",
    "protocol" : "rmi",
    "addresses" : [ {
      "host" : "rack1server28",
      "port" : "48551"
    } ]
  } ]
}

この情報は、(仮想の)ロードバランサーがコンポーネントを列挙し、リクエストをディスパッチするコンポーネントインスタンスのリストを構築するために使用できます。同様に、管理アプリケーションは、利用可能なすべてのコンポーネントインスタンスとそのJMXポートを列挙し、それぞれに接続してパフォーマンスメトリクスを収集できます。

レジストリAPI

これは、Javaアプリケーションから見たレジストリAPIです。APIは、ZK操作の上の薄いレイヤーであり、基本的にはパスを構築し、エントリを読み取り、書き込み、更新し、子を列挙します。REST APIはサーバー内で実装されており、この同じAPIを使用してREST APIを実装します。

リストされている例外は、可能な例外のサブセットにすぎません。インターフェイスは、特別な意味を持つ例外のみをリストします。

すべての書き込み操作は、Zookeeperクライアントの一貫性のあるビューを持つレジストリサービスと通信していることを前提とする必要があります。読み取り専用クライアントは、ビューが多少古くなっている可能性があることを前提とする必要があります。

すべてのクライアントは、レジストリが共有リソースであり、一連のアクション中に変更される可能性があることを認識する必要があります。

レジストリ操作

public interface RegistryOperations extends Service {

  /**
   * Create a path.
   *
   * It is not an error if the path exists already, be it empty or not.
   *
   * The createParents flag also requests creating the parents.
   * As entries in the registry can hold data while still having
   * child entries, it is not an error if any of the parent path
   * elements have service records.
   *
   * @param path path to create
   * @param createParents also create the parents.
   * @throws PathNotFoundException parent path is not in the registry.
   * @throws InvalidPathnameException path name is invalid.
   * @throws IOException Any other IO Exception.
   * @return true if the path was created, false if it existed.
   */
  boolean mknode(String path, boolean createParents)
      throws PathNotFoundException,
      InvalidPathnameException,
      IOException;

  /**
   * Set a service record to an entry
   * @param path path to service record
   * @param record service record service record to create/update
   * @param createFlags creation flags
   * @throws PathNotFoundException the parent path does not exist
   * @throws FileAlreadyExistsException path exists but create flags
   * do not include "overwrite"
   * @throws InvalidPathnameException path name is invalid.
   * @throws IOException Any other IO Exception.
   */
  void bind(String path, ServiceRecord record, int createFlags)
      throws PathNotFoundException,
      FileAlreadyExistsException,
      InvalidPathnameException,
      IOException;

  /**
   * Resolve the record at a path
   * @param path path to service record
   * @return the record
   * @throws PathNotFoundException path is not in the registry.
   * @throws InvalidPathnameException the path is invalid.
   * @throws IOException Any other IO Exception
   */

  ServiceRecord resolve(String path) throws PathNotFoundException,
      InvalidPathnameException,
      IOException;

  /**
   * Get the status of a path
   * @param path path to query
   * @return the status of the path
   * @throws PathNotFoundException path is not in the registry.
   * @throws InvalidPathnameException the path is invalid.
   * @throws IOException Any other IO Exception
   */
  RegistryPathStatus stat(String path)
      throws PathNotFoundException,
      InvalidPathnameException,
      IOException;

  /**
   * Probe for a path existing.
   * This is equivalent to {@link #stat(String)} with
   * any failure downgraded to a
   * @param path path to query
   * @return true if the path was found
   * @throws IOException
   */
  boolean exists(String path) throws IOException;

 /**
   * List all entries under a registry path
   * @param path path to query
   * @return a possibly empty list of the full path names of
   * child entries
   * @throws PathNotFoundException
   * @throws InvalidPathnameException
   * @throws IOException
   */
   List<String> list(String path) throws
      PathNotFoundException,
      InvalidPathnameException,
      IOException;

  /**
   * Delete a path.
   *
   * If the operation returns without an error then the entry has been
   * deleted.
   * @param path path delete recursively
   * @param recursive recursive flag
   * @throws PathNotFoundException path is not in the registry.
   * @throws InvalidPathnameException the path is invalid.
   * @throws PathIsNotEmptyDirectoryException path has child entries, but
   * recursive is false.
   * @throws IOException Any other IO Exception
   *
   */
  void delete(String path, boolean recursive)
      throws PathNotFoundException,
      PathIsNotEmptyDirectoryException,
      InvalidPathnameException,
      IOException;

  /**
   * Add a new write access entry to be added to node permissions in all
   * future write operations of a session connected to a secure registry.
   *
   * This does not grant the session any more rights: if it lacked any write
   * access, it will still be unable to manipulate the registry.
   *
   * In an insecure cluster, this operation has no effect.
   * @param id ID to use
   * @param pass password
   * @return true if the accessor was added: that is, the registry connection
   * uses permissions to manage access
   * @throws IOException on any failure to build the digest
   */
  boolean addWriteAccessor(String id, String pass) throws IOException;

  /**
   * Clear all write accessors.
   *
   * At this point all standard permissions/ACLs are retained,
   * including any set on behalf of the user
   * Only  accessors added via {@link #addWriteAccessor(String, String)}
   * are removed.
   */
  public void clearWriteAccessors();
}

RegistryPathStatus

RegistryPathStatusクラスは、レジストリ内のノードの内容を要約します。

public class RegistryPathStatus {

  /**
   * Short path in the registry to this entry
   */
  public String path;

  /**
   * Timestamp
   */
  public long time;

  /**
   * Entry size in bytes, as returned by the storage infrastructure.
   * In zookeeper, even "empty" nodes have a non-zero size.
   */
  public long size;

  /**
   * Number of child nodes
   */
  public int children;
}

セキュリティ

レジストリでは、サービスインスタンスは、パーミッションを持つパスの下にのみ登録できます。YARNは、YARNによってデプロイされたサービスがユーザーによって登録できる、ユーザーに適したパーミッションを持つディレクトリを作成します。これは、サービスインスタンスのユーザーアカウントのものです。管理者は、適切なパーミッションを持つディレクトリ(/servicesなど)も作成します(コアHadoopサービスが自身を登録できる場所)。

レジストリ情報への読み取りアクセスを制限する試みは行われません。サービスは、認証と認可を要求することにより、クライアントによる不適切なアクセスを保護します。サービスレコードにはscopeフィールドがありますが、これは「内部APIのみ」を示すマーカーであり、直接的なセキュリティ制限ではありません(そのため、「public」と「private」ではなく、「internal」と「external」が提案されています)。

根拠:登録されるエンドポイントは、いずれにせよポートスキャンによって発見可能になるでしょう。すべてをワールドリーダブルにすることで、REST APIがよりシンプルなアクセスモデルを持つことができ、DNSと一致します。

セキュアなクラスターでは、長期間実行されるサービスの場合、ZKトークンの更新が問題になる可能性があります。トークンが期限切れになると、セッションが期限切れになる可能性があります。このようなトークンの更新は、APIの実装の一部ではありません。レジストリ操作クラスのインスタンスのトークンを更新する手段を追加する必要があるかもしれません。

セキュリティポリシーの概要

Kerberosを使用しないZookeeperクラスターでは、セキュリティポリシーは実装されていません。

レジストリは、Kerberos管理クラスターでセキュリティ保護されるように設計されています。

  • レジストリルートは、「システムアカウント」:mapredhdfsyarnに対してフル権限:"rwcda"を付与します。その他すべてのアカウントと匿名アクセスは、読み取り専用です。

  • パーミッションは、/usersおよび/services/でも同様に制限されています。

  • インストールでは、これらのシステムアカウントを拡張または変更できます。

  • ユーザーに属するアプリケーションがスケジュールされると、YARNはそのユーザーのエントリ/users/${username}を作成する必要があります。

  • このノードはシステムへのフルアクセス権を持ち、ユーザーはアクセス権"crd"を持ちます。つまり、子ノードを作成または削除できますが、ホームノードに書き込んだり、そのパーミッションを変更したりすることはできません。

  • レジストリに書き込みたいアプリケーションは、SASL接続を使用してZookeeperを介して認証する必要があります。

  • ユーザーパスにノードを作成するアプリケーションは、サイト指定のシステムアカウントをACLリストにフルアクセスで含める必要があります。

  • ユーザーパスにノードを作成するアプリケーションは、ACLを含める必要があります。これにより、

  • ユーザーパスにノードを作成するアプリケーションは、自身のユーザーIDをsasl:user@REALMエントリとして宣言する必要があります。

  • ユーザーパスにノードを作成するアプリケーションは、digest: ACLトークンを追加して、Kerberos認証情報を必要とせずに、サービスがレジストリの一部を操作できるようにできます。

ダイジェスト駆動の認証は、長期間実行されるアプリケーションでの認証情報更新の問題を回避します。YARNアプリケーションは、起動時にZKサービスに接続するためのトークンを渡される場合があります。その後、ノードのパーミッションに秘密のダイジェストACLを含めて、エントリを作成または更新できます。その結果、認証情報が期限切れになった後でも、一部のアクセス権を保持します。

これが成功するためには、クライアントがセッションをフォールバックさせて、SASLを使用せず、代わりに認証ID:パスワードの認証情報を使用する必要があることに注意してください。

クラスター外およびクロススクラスターアクセス

  1. クライアントは、別のクラスターのサービスにアクセスするために、そのクラスターのレジストリにアクセスできる必要があります。この必要性の詳細は、さらに具体化する必要があります。

  2. Apache Knoxなどのファイアウォールサービスは、公開されたサービスの内部分析を行い、エンドポイントのサブセットを公開できます。それらは、将来のREST APIを実装できます。

制限

エントリサイズ

Zookeeperには、デフォルトで1MB/ノードの制限があります。サービスまたはコンポーネントのすべてのエンドポイントがそのノードに添付されたJSONに格納されている場合、すべてのエンドポイント登録データの合計制限は1MBです。

これが問題になるのを防ぐために、クライアントAPIはフィールドの最大長に厳密な制限を実装する必要があります。addressType、protocol、apiフィールドには低い制限、descriptionおよびaddresses要素には少し長い制限、さらにaddressesフィールドの要素数には制限を設ける必要があります。

名前のサイズ

将来のDNSをサポートするために、すべてのパス要素に63バイトの制限が必要です。非ASCIIユーザー名の場合、この制限は、より短いパスが制限になる可能性があることを意味します。

更新率

エントリの変更の高速なレートは、ZKクラスターでは反社会的であると見なされます。実装では、更新操作を調整する場合があります。

ポーリングレート

レジストリをポーリングするクライアントは、調整される場合があります。

サービスレコードの完全な例

以下は、(規範的ではない)YARNアプリケーションから取得したサービスレコードの例です。

{
  "type" : "JSONServiceRecord",
  "description" : "Slider Application Master",
  "yarn:persistence" : "application",
  "yarn:id" : "application_1414052463672_0028",
  "external" : [ {
    "api" : "classpath:org.apache.slider.appmaster",
    "addressType" : "host/port",
    "protocol" : "hadoop/IPC",
    "addresses" : [ {
      "port" : "48551",
      "host" : "nn.example.com"
    } ]
  }, {
    "api" : "http://",
    "addressType" : "uri",
    "protocol" : "web",
    "addresses" : [ {
      "uri" : "http://nn.example.com:40743"
    } ]
  }, {
    "api" : "classpath:org.apache.slider.management",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "http://nn.example.com:40743/ws/v1/slider/mgmt"
    } ]
  }, {
    "api" : "classpath:org.apache.slider.publisher",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "http://nn.example.com:40743/ws/v1/slider/publisher"
    } ]
  }, {
    "api" : "classpath:org.apache.slider.registry",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "http://nn.example.com:40743/ws/v1/slider/registry"
    } ]
  }, {
    "api" : "classpath:org.apache.slider.publisher.configurations",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "http://nn.example.com:40743/ws/v1/slider/publisher/slider"
    } ]
  }, {
    "api" : "classpath:org.apache.slider.publisher.exports",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "http://nn.example.com:40743/ws/v1/slider/publisher/exports"
    } ]
  } ],
  "internal" : [ {
    "api" : "classpath:org.apache.slider.agents.secure",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "https://nn.example.com:52705/ws/v1/slider/agents"
    } ]
  }, {
    "api" : "classpath:org.apache.slider.agents.oneway",
    "addressType" : "uri",
    "protocol" : "REST",
    "addresses" : [ {
      "uri" : "https://nn.example.com:33425/ws/v1/slider/agents"
    } ]
  } ]
}

これは、内部と外部の両方の多数のエンドポイントを公開しています。

外部

  1. クライアント-AM通信用のIPCホスト名とポート
  2. AMのWeb UIへのURL
  3. 特定のアプリケーションサービスのためのWeb UI下のREST URLのシリーズ。詳細は無関係です。一意性を保証するためにアプリケーション固有のAPI値を使用していることに注意してください。

内部

  1. アプリケーション自体によってデプロイされたコンテナ用にAMが提供するREST APIへの2つのURL。

コンテナで実行されているPythonエージェントは、AMと通信するための内部エンドポイントURLを取得します。レコードはコンテナの起動時に解決され、通信の問題が発生するまでキャッシュされます。その時点で、現在のレコードについてレジストリがクエリされ、AMに再接続が試行されます。

ここで「接続性」の問題とは、「低レベルのソケット/IOエラー」と「HTTPS認証の失敗」の両方を意味します。エージェントは双方向HTTPS認証を使用します。AMが失敗し、別のアプリケーションが同じポートでリッスンを開始した場合、認証の失敗がトリガーされ、サービスレコードが再読み取りされます。