HDFSオブザーバーネームノードからの整合性のある読み取り

目的

このガイドでは、HDFSオブザーバーネームノード機能の概要と、一般的なHA対応クラスタでの構成/インストール方法について説明します。詳細な技術設計の概要については、HDFS-12943に添付されているドキュメントを参照してください。

背景

HA対応HDFSクラスタ(詳細については、QJMを使用したHDFS高可用性を参照)では、単一のアクティブネームノードと1つ以上のスタンバイネームノードが存在します。アクティブネームノードはすべてのクライアントリクエストを処理する役割を担い、スタンバイネームノードはJournalNodeからの編集ログの追跡と、すべてのデータノードからのブロックレポートの受信によって、名前空間とブロック位置情報に関する最新情報を保持します。このアーキテクチャの欠点の1つは、アクティブネームノードが単一ボトルネックとなり、特にビジー状態のクラスタではクライアントリクエストで過負荷になる可能性があることです。

HDFSオブザーバーネームノードからの整合性のある読み取り機能は、**オブザーバーネームノード**と呼ばれる新しいタイプのネームノードを導入することで、上記の問題に対処します。スタンバイネームノードと同様に、オブザーバーネームノードは名前空間とブロック位置情報に関する最新情報を保持します。さらに、アクティブネームノードと同様に、整合性のある読み取りを提供する機能も備えています。読み取りリクエストは一般的な環境では大部分を占めるため、これによりネームノードトラフィックの負荷分散と全体的なスループットの向上が期待できます。

アーキテクチャ

新しいアーキテクチャでは、HAクラスタはアクティブ、スタンバイ、オブザーバーの3つの異なる状態のネームノードで構成されます。状態遷移はアクティブとスタンバイ、スタンバイとオブザーバーの間で発生する可能性がありますが、アクティブとオブザーバーの間では直接発生しません。

単一クライアント内での書き込み後読み取りの整合性を確保するため、ネームノード内のトランザクションIDを使用して実装される状態IDがRPCヘッダーに導入されます。クライアントがアクティブネームノードを介して書き込みを実行すると、ネームノードからの最新のトランザクションIDを使用して状態IDが更新されます。後続の読み取りを実行する際、クライアントはこの状態IDをオブザーバーネームノードに渡します。オブザーバーネームノードは自身のトランザクションIDと照合し、読み取りリクエストを提供する前に、自身のトランザクションIDがリクエストの状態IDに追いついたことを確認します。これにより、単一クライアントからの「自分の書き込みを読み取る」セマンティクスが保証されます。帯域外通信が発生した場合の複数クライアント間の整合性の維持については、以下の「クライアントの整合性の維持」セクションで説明します。

編集ログの追跡は、アクティブネームノードでトランザクションが適用されてからオブザーバーネームノードで適用されるまでの遅延に直接影響するため、オブザーバーネームノードにとって非常に重要です。「編集追跡ファストパス」と呼ばれる新しい編集ログ追跡メカニズムが導入され、この遅延が大幅に短縮されました。これは、既存の進行中の編集ログ追跡機能に基づいて構築されており、HTTPではなくRPCベースの追跡、JournalNodeのメモリ内キャッシュなど、さらに改良されています。詳細については、HDFS-13150に添付されている設計ドキュメントを参照してください。

新しいクライアント側プロキシプロバイダーも導入されています。既存のConfiguredFailoverProxyProviderを継承したObserverReadProxyProviderは、後者を置き換えてオブザーバーネームノードからの読み取りを有効にするために使用されます。クライアント読み取りリクエストを送信すると、プロキシプロバイダーは最初にクラスタ内で使用可能な各オブザーバーネームノードを試行し、すべて失敗した場合にのみアクティブネームノードにフォールバックします。同様に、ObserverReadProxyProviderWithIPFailoverは、IPフェイルオーバー設定でIPFailoverProxyProviderを置き換えるために導入されています。

クライアントの整合性の維持

上記のように、クライアント「foo」は、すべての書き込み操作を含むアクティブネームノードへのすべてのリクエストで状態IDを更新します。オブザーバーネームノードへのリクエストは、オブザーバーがこのトランザクションIDを確認するまで待機し、クライアントが自分の書き込みをすべて読み取ることができるようにします。ただし、「foo」が帯域外(つまり、HDFS以外)のメッセージをクライアント「bar」に送信して書き込みが実行されたことを通知した場合、「bar」による後続の読み取りでは「foo」による最近の書き込みが表示されない場合があります。この不整合な動作を防ぐため、新しいmsync()、または「メタデータ同期」コマンドが追加されました。クライアントでmsync()が呼び出されると、アクティブネームノードに対して状態IDが更新されます(非常に軽量な操作です)。これにより、後続の読み取りでmsync()の時点までの整合性が保証されます。したがって、「bar」が読み取りを実行する前にmsync()を呼び出す限り、「foo」によって行われた書き込みが表示されることが保証されます。

msync()を使用するために、アプリケーションが必ずしもコードを変更する必要はありません。起動時に、クライアントはオブザーバーに対して読み取りを実行する前に自動的にmsync()を呼び出し、クライアントの初期化前に行われた書き込みが表示されるようにします。さらに、ObserverReadProxyProviderでサポートされている設定可能な「自動msync」モードがあり、設定可能な間隔で自動的にmsync()を実行して、クライアントが時間制限よりも古いデータを表示しないようにします。これには、各更新でアクティブネームノードへのRPCが必要になるため、ある程度のオーバーヘッドが発生するため、デフォルトでは無効になっています。

デプロイメント

構成

オブザーバーネームノードからの整合性のある読み取りを有効にするには、**hdfs-site.xml**にいくつかの構成を追加する必要があります。

  • **dfs.namenode.state.context.enabled** - ネームノードがサーバーの状態とIDを維持および更新できるようにします。

これにより、ネームノードは現在のサーバー状態IDを追跡する配置コンテキストインスタンスを作成します。サーバー状態IDはクライアントに返されます。オブザーバー読み取りケースのパフォーマンスを最適化するために、デフォルトでは無効になっています。ただし、オブザーバーネームノード機能では**これを有効にする必要があります**。

    <property>
       <name>dfs.namenode.state.context.enabled</name>
       <value>true</value>
    </property>
  • **dfs.ha.tail-edits.in-progress** - 進行中の編集ログの高速追跡を有効にします。

これにより、進行中の編集ログと、RPCベースの編集ログフェッチ、JournalNodeのメモリ内キャッシュなどの他のメカニズムを通じて、高速編集ログ追跡が可能になります。デフォルトでは無効になっていますが、オブザーバーネームノード機能では**有効にする必要があります**。

    <property>
      <name>dfs.ha.tail-edits.in-progress</name>
      <value>true</value>
    </property>
  • **dfs.ha.tail-edits.period** - スタンバイ/オブザーバーネームノードがJournalNodeから編集をフェッチする頻度。

これは、Observer NameNode の Active に対する staleness(最新状態とのずれ)を決定します。値が大きすぎると、クライアントのリクエストが Observer が編集ログを tailing し、Active の最新状態に追いつくまで RPC キューで待機するため、RPC 時間が増加します。デフォルト値は 1 分です。この値をはるかに低い値に設定することを**強くお勧めします**。低い値を使用する場合は、バックオフを有効にすることもお勧めします。以下を参照してください。

    <property>
      <name>dfs.ha.tail-edits.period</name>
      <value>0ms</value>
    </property>
  • dfs.ha.tail-edits.period.backoff-max - スタンバイ/Observer NameNode が編集ログの tailing を行う際にバックオフを実行するかどうかを指定します。

これは、スタンバイ/Observer NameNode が JournalNode から編集ログを tailing しようとして、利用可能な編集ログが見つからない場合の動作を決定します。これは、編集ログの tailing 期間が非常に短いものの、クラスタの負荷が高くない場合によく発生する状況です。この設定がない場合、このような状況では、スタンバイ/Observer は利用可能な編集ログがない場合でも常に編集ログの読み取りを試みるため、スタンバイ/Observer の使用率が高くなります。この設定を有効にすると、編集ログの tailing の試行で 0 件の編集ログが返された場合に、指数関数的なバックオフが実行されます。この設定は、編集ログの tailing の試行間の最大待機時間を指定します。

    <property>
      <name>dfs.ha.tail-edits.period.backoff-max</name>
      <value>10s</value>
    </property>
  • dfs.journalnode.edit-cache-size.bytes - JournalNode のメモリ内キャッシュサイズ(バイト単位)です。

これは、JournalNode 側で編集ログを格納するためのメモリ内キャッシュのサイズ(バイト単位)です。キャッシュは、RPC ベースの tailing を介して編集ログを提供するために使用されます。これは、dfs.ha.tail-edits.in-progress が有効になっている場合にのみ有効です。

    <property>
      <name>dfs.journalnode.edit-cache-size.bytes</name>
      <value>1048576</value>
    </property>
  • dfs.namenode.accesstime.precision – HDFS ファイルのアクセス時間を有効にするかどうかを指定します。

この設定を無効にすることを**強くお勧めします**。有効にすると、開かれたファイルの時間を更新するために書き込みロックを保持する必要があるため、getBlockLocations 呼び出しが書き込み呼び出しに変換されます。そのため、リクエストはすべての Observer NameNode で失敗し、最終的にアクティブにフォールバックします。結果として、RPC パフォーマンスが低下します。

    <property>
      <name>dfs.namenode.accesstime.precision</name>
      <value>0</value>
    </property>

新しい管理コマンド

スタンバイ NameNode を Observer 状態に移行するための新しい HA 管理コマンドが導入されました。

haadmin -transitionToObserver

これはスタンバイ NameNode でのみ実行できます。Active NameNode でこれを呼び出すと、例外がスローされます。

同様に、既存の transitionToStandby は Observer NameNode でも実行でき、スタンバイ状態に移行します。

注記:Observer NameNode がフェイルオーバーに参加する機能はまだ実装されていません。したがって、次のセクションで説明するように、transitionToObserver を使用して Observer を起動する必要があります。ZKFC は Observer NameNode で有効にすることができますが、NameNode が Observer 状態のときは何も実行しません。ZKFC は、NameNode がスタンバイ状態に移行した後、Active の選出に参加します。

デプロイメントの詳細

Observer サポートを有効にするには、まず 2 つ以上の NameNode を持つ HA 対応の HDFS クラスタが必要です。次に、スタンバイ NameNode を Observer 状態に移行する必要があります。最小限のセットアップでは、クラスタ内で 3 つの NameNode(1 つのアクティブ、1 つのスタンバイ、1 つの Observer)を実行します。大規模な HDFS クラスタの場合、読み取りリクエストの強度と HA 要件に応じて、2 つ以上の Observer を実行することをお勧めします。

現在、Observer NameNode は自動フェイルオーバーが有効になっている場合に完全に統合されないことに注意してください。dfs.ha.automatic-failover.enabled が有効になっている場合、Observer NameNode で ZKFC を実行することの唯一の利点は、NameNode をスタンバイに移行した後、自動的に Active の選出に参加することです。これが望ましくない場合は、Observer NameNode で ZKFC を無効にすることができます。それに加えて、transitionToObserver コマンドに forcemanual フラグを追加する必要もあります。

haadmin -transitionToObserver -forcemanual

将来的には、この制限は解除される予定です。

クライアント設定

読み取りアクセスに Observer NameNode を使用したいクライアントは、クライアント側の hdfs-site.xml 設定ファイルで、プロキシプロバイダーの実装に ObserverReadProxyProvider クラスを指定できます。

<property>
    <name>dfs.client.failover.proxy.provider.<nameservice></name>
    <value>org.apache.hadoop.hdfs.server.namenode.ha.ObserverReadProxyProvider</value>
</property>

Observer NameNode を使用したくないクライアントは、引き続き既存の ConfiguredFailoverProxyProvider を使用でき、動作の変更は発生しません。

「自動 msyc」機能を利用したいクライアントは、以下の設定を調整する必要があります。これは、クライアントの状態 ID が Active NameNode から更新されていない場合に、msync() が自動的に実行されるまでの期間を指定します。これが 0 として指定された場合、msync() は*すべて*の読み取り操作の前に実行されます。これが正の時間間隔の場合、読み取り操作が要求され、Active との接続がその期間よりも長く行われていない場合に、msync() が実行されます。これが負の場合(デフォルト)、自動 msync() は実行されません。

<property>
    <name>dfs.client.failover.observer.auto-msync-period.<nameservice></name>
    <value>500ms</value>
</property>