インサイド MapR (1) (Hadoop アドベントカレンダー 2011 16日目)

この記事は Hadoop アドベントカレンダー 2011 の16日目の記事です。

今年の5月にMapR Technologies社から発表された、独自実装のHadoopディストリビューション(以降、単純にMapRと呼びます)ですが、そのユニークな特徴やオープンソース実装のはるか先をいく先進性から、いろいろなところで注目を集めています。ただ、非常に多彩な機能を持ちながら、これどうやって実現しているの、という風に思っている方も多いと思います。私はお仕事柄MapRの実装を若干詳しく知る立場におりますので、MapRの中身を少し掘り下げてみたいと思います。

MapRはHadoopのどこを改良しているのか

MapR TechnologiesはもともとGoogleでGFS、BigTableMapReduceなどの検索基盤技術を担当していたエンジニア M.C. Srivas 氏などが中心となって3年ほど前に創業された会社ですが、今年製品を発表する前まではひっそりと、いわゆる「ステルスモード」で最適化Hadoop実装の研究開発を進めていました。3年前はすでにHadoopの可能性について世間の注目は集まりはじめていましたが、当時から実装の非効率性や単一障害点の存在などの課題は指摘されていました。ただ、オープンソースコミュニティのプロジェクトなので大きなアーキテクチャ上の変更を伴う提案は合意を得るのはなかなか難しく、開発の速度も遅いため、それであれば企業が本当に必要とする高速・高信頼の実装を自分たちで速攻で作って世に出してしまえばよいではないか、ということで投資家から資金を得て開発を進めたというのが経緯です。

ではMapRが改良を加えた部分は何か、というところですが、主にファイルシステムHDFS)の部分になります。HDFSAPIは100%Apache互換を保ちながら、内部実装をMapR-FSという完全に再設計した実装で置き換えています。これにより、ファイルシステムに対するランダムRead/Write、NFSアクセス、ボリューム管理、スナップショット、ミラーリング、ビルトイン圧縮といった多彩な機能を実現しています。

本エントリでは、特にボリューム管理と、ファイルシステムの基本となるコンテナの実装について書いてみたいと思います。

ボリュームという概念

MapRにはオープンソース実装にはない「ボリューム」という概念があります。オープンソース実装では、HDFSファイルシステムはディレクトリ構造がひとつあるだけで、その中を分けて管理するという発想はありませんが、MapRではボリューム単位で様々な管理ポリシーを設定して細かい制御を行うとともに、ボリュームをベースにしたスナップショットやミラーリングなどのデータ管理機能が用意されています。

ボリュームごとに設定できる項目には次のようなものがあります。

マウントポイント
ファイルシステムのディレクトリ構造の中で、ボリュームをどこにマウントするかを指定します。デフォルトで存在するルートボリュームのマウントポイントは「/」ですが、作成したボリュームを任意のディレクトリ(例えば/vol/vol1)にマウントできます。
容量上限
ボリュームの容量上限を設定します。上限を超えてファイルを作成しようとするとエラーになります。実際の上限値であるハードリミットと、それより小さい値でアラームを発生させるアドバイザリリミットを設定できます。
ボリュームの所有ユーザー
所有権をもつユーザーを指定します。
各ユーザーの管理権限
各ユーザーの管理権限(削除できるか、ダンプできるか、スナップショットを作成できるか等)を設定します。ボリューム内のファイルに対するアクセス権限とは違います。アクセス権限はファイルパーミッションにより設定します。
レプリケーション
MapRではレプリケーション数はボリューム単位で指定します。実際のレプリケーション数と、下回るとアラームを発生させる最低レプリケーション数を設定できます。
スナップショットのスケジュール
あらかじめスケジュールポリシーを設定しておくことにより、例えば毎日深夜0時に有効期間7日間のスナップショット(ボリュームの静止イメージ)を作る、といったことができます。スナップショットのイメージはファイルシステム内に作られ、その後の更新分はCopy-on-Write方式で差分のみ保管されるため、スナップショットの作成は一瞬で完了し、効率的なストレージ利用になります。
ミラーリング
あるボリュームに対し、ミラー先ボリュームを設定することで、ボリューム間のデータ同期を図ることができます。ミラー先ボリュームは読み込み専用ではありますが、クラスタ内で複数のミラーボリュームを作成することでアクセスの負荷分散をしたり、アプリケーション間での効率的なデータ移行を行うことができます。また、遠隔地のクラスタにあるボリュームとミラー構成を組めば、災害対策(DR)としても活用可能です。
トポロジーに対するマッピング
トポロジーとはクラスタの物理的なノード配置をツリー構造で記述しておく設定です。一般的にはひとつのラックに格納されたサーバを一つのトポロジーグループに配置させるといったような構成をとりますが、ボリュームをトポロジにマッピングすることにより、例えば特定ボリュームを特定のラック内のサーバに張り付け、その中だけでデータを分散配置させる、という設定ができます。

コンテナ管理によるスケーラビリティの向上と分散NameNode

オープンソース実装では各ファイルのメタ情報は独立したサーバで稼働するNameNodeで管理されます。この方法には2つの大きな課題があります。1つ目はファイル数が増えるに従って扱うメタ情報も大きくなり、性能が劣化するという点です。各ファイルは一定のブロックサイズで分割され、NameNodeはすべてのデータブロックの配置を管理しているために、アクセスが1点に集中するためです。2つ目はメタ情報の管理が1カ所で行われるため、NameNodeのサーバに障害が発生すると、クラスタのデータアクセスがすべて失われる点です。オープンソース実装でもメタ情報のレプリケーションは行いますし、HAミドルウェアを使って自動フェールオーバーを行うように構成することも可能ですが、追加の構築やテストの工数が増えて大変です。

MapRではこの2つの課題に対応するために、データブロックの管理にコンテナという概念を追加し、非常に巧妙なかたちでメタ情報管理の分散と耐障害性の向上を図っています。

まずコンテナですが、コンテナにはデータコンテナ、ネームコンテナの2種類があります。データコンテナはデータブロックを複数まとめて格納する管理単位です。デフォルトでは16GBの大きさで、この中に入る分だけのデータブロックを格納します。上で説明したボリュームは複数のデータコンテナから構成されます。一方、ネームコンテナはそれぞれのボリュームに1つだけ存在し、ボリューム内のファイルのメタ情報を格納します。オープンソース実装のNameNodeの機能は、このネームコンテナが代替します。ネームコンテナはデータコンテナ同様クラスタ内のノードに分散され、このためNameNodeを独立して稼働させる必要がなく、これが「分散NameNode」といわれるゆえんです。

ところで、各コンテナの配置は誰が管理しているの?という疑問をお持ちの方がいるかもしれません。これに関してはCLDB (Container Location Database) というサービスが管理をしています。なんだ、結局そこが単一障害点になるんじゃないの、という声が聞こえてきそうですが、CLDBは最大3ノードまでのHA構成が可能で、しかもNameNodeとは違ってCLDBで管理する情報はコンテナの位置情報とバージョン情報だけですので、相当大規模な構成でも余裕でメモリに乗り切るサイズな上に、フェールオーバーも高速です。

下にコンテナによるメタ情報管理の概念図を示します。メタ情報管理が2段構成になるようなイメージですね。

次にレプリケーションについて説明します。データのレプリケーションの管理はコンテナ単位で行われます。データコンテナだけでなく、メタ情報を格納するネームコンテナもデータと同じレプリケーションのしくみを使い、耐障害性を確保しています。デフォルトでは各コンテナは3つのレプリカをノードをまたがった形で作りますが、そのうち1つがマスターコンテナになり、残りのレプリカコンテナがデータ更新のための「レプリケーションチェーン」によってマスターとつながれる形になります。

レプリケーションチェーンの構造はネームコンテナとデータコンテナでは異なり、ネームコンテナに関してはマスターを中心としたスター型、データコンテナに関してはマスターを起点とするデイジーチェーン型になっています。これはメタ情報の更新単位は小さいので同時に更新を行っても性能への影響は小さく、一方ファイルデータブロックの更新は単位が大きいので負荷の分散を図って性能への影響を抑えるためです。データの更新はまずマスターに対して行われ、さらにレプリケーションチェーンを辿って各レプリカで更新が行われた後に、今度はチェーンを逆に辿って更新の完了がマスターに通知されます。この一連の更新フローはトランザクションとして扱われ、万が一更新中にどこかのノードで障害が起きた場合にはすべての更新は取り消されるため、データの一貫性が保たれます。

ネームコンテナはボリュームごとに異なるサーバノードに作成されるため、メタ情報に対するアクセスは(少なくともノード数以上のボリュームがあれば)全ノードに対して分散されます。また、各コンテナのレプリケーションチェーンにおけるマスターコンテナも全ノードに分散されるため、データの更新についてもどこかのノードに集中しない形になります。これをまとめると下のようなイメージになります。


以上、今回書ききれなかった部分もたくさんありますので、また次回も続けて行きたいと思います。