TT Lab
はじめる
学ぶ 学習パス コース

Apache Hadoop — 1つのPodにHDFSとYARNを立てて運用する

NameNode は記憶を fsimage と edits の二か所に分けて書く

TT Labで続きを見る

一言でいうと

NameNodeは、名前空間全体をメモリに持ち、ディスクにはある時点の写真(fsimage)とそのあとの変更ログ(edits)を別々に書き込みます。両者をまとめる処理がチェックポイントで、ブロックがどのDataNodeにあるかは、どこにも書かないので、起動するたびにセーフモードでレポートを待ちます。

なぜファイル1つでは済まないのか

ファイルを1つ作るたびに、名前空間全体をディスクに書き直すとどうなるでしょうか。ファイルが1,000万個あるクラスターでは、mkdir1回が数ギガバイトの書き込みになります。逆に、変更だけを書き足して、全体の姿を一度も保存しなければ、再起動するときに、数か月分のログを最初からやり直す必要があります。

HDFSは、2つの方式を組み合わせました。HDFS設計ドキュメントは、NameNodeがメタデータのすべての変更をEditLogというトランザクションログに書き、ブロックとファイルの対応やファイル属性を含む名前空間全体は、FsImageというファイルに入れると説明しています。同じドキュメントは、理由も短く書いています。FsImageを読むのは効率的ですが、FsImageに変更を1つずつ反映するのは効率的ではないのです。そのため、変更はログの末尾に追記するだけにして(順次書き込みなので速いです)、写真はときどき新しく撮ります。

この構造を知らないと、2種類の事故に遭います。1つは、再起動に1時間かかる事故です。ユーザーガイドが警告するように、editsが大きくなるほど、次の再起動が長くなります。もう1つは、NameNodeのディレクトリを失う事故です。このディレクトリがHDFSのすべてです。DataNodeにブロックファイルが無事に残っていても、それがどのファイルの何番目の断片かを知っているのは、ここだけです。

どう動くのか

NameNodeの保存ディレクトリのcurrent/を開くと、おおよそこのような形です。

current/
  VERSION
  seen_txid
  fsimage_0000000000000000000
  fsimage_0000000000000000000.md5
  edits_0000000000000000001-0000000000000000042
  edits_inprogress_0000000000000000043

ファイル名の数字はトランザクション番号(txid)です。fsimage_Nは、トランザクションNまでが反映された写真で、edits_A-BはAからBまでの閉じたログの断片、edits_inprogress_Cは、今書いている断片です。起動するとき、NameNodeは最新のfsimageをメモリに載せ、その番号の後のeditsを順に再適用します。すると、止まる直前の名前空間がメモリに復元されます。

NameNodeのディスクにあるfsimageとeditsの断片が、トランザクション番号でつながる図です。起動するとfsimageをメモリに載せ、後ろのeditsを順に適用して名前空間を復元します。チェックポイントは両者をまとめて新しいfsimageを書き、ブロックの位置はディスクにないので、DataNodeのブロックレポートで埋めます

チェックポイントは、このまとめ処理を事前に済ませておくことです。設計ドキュメントによると、チェックポイントは、決められた時間間隔(dfs.namenode.checkpoint.period)か、溜まったトランザクション数(dfs.namenode.checkpoint.txns)のうち、先に届くほうで起こります。hdfs-default.xmlのデフォルトは、3600秒と1,000,000トランザクションです。このまとめ処理は、NameNode自身ではなく、通常はSecondary NameNode(HA構成ならStandby NameNode)が行います。名前のせいで予備のNameNodeだと誤解されますが、Secondaryは、障害が起きても代わりにサービスしません。写真を新しく撮って返してくれる助手にすぎません。

残る写真の数も決まっています。dfs.namenode.num.checkpoints.retainedのデフォルトは2で、最も古い写真から現在までを復元するのに必要なeditsも一緒に残します。写真1つが壊れても、1つ前に戻る道を残しておくのです。

ここで必ず押さえておくことがあります。fsimageには、ブロックがどのDataNodeにあるかがありません。ファイルがどのブロックIDで構成されているかは書かれていますが、そのブロックのレプリカがどのサーバーのどのディスクにあるかは、DataNodeが起動時に送るブロックレポート(Blockreport)で、毎回新しく埋めます。位置は、ディスクが壊れたりサーバーが変わったりして、絶えず変わるので、書いておくと間違った情報になりやすいのです。

セーフモードは何を待つのか

そのため、起動したばかりのNameNodeは、名前はすべて知っていますが、ブロックがどこにあるかは知りません。この状態で、レプリカが足りないと判断してレプリケーションを始めると、まだレポートを送っていないDataNodeが無事に持っているブロックまで、不要にレプリケーションしてしまいます。設計ドキュメントが言うSafemodeは、この誤判断を防ぐ待機状態です。コマンドリファレンスは、セーフモードのNameNodeは、名前空間の変更を受け付けず(読み取り専用)、ブロックをレプリケーションしたり削除したりもしないと整理しています。

抜け出す条件は、設定で決まります。dfs.namenode.safemode.threshold-pctのデフォルトは0.999fです。ブロックの99.9%が、最小レプリカ数の分だけ報告されると条件が満たされ、さらにdfs.namenode.safemode.extensionのデフォルト30000ms(30秒)を待ってから抜けます(ノードが1つだけのラボのイメージは、この余裕を0にしてあります)。運用者がhdfs dfsadmin -safemode enterで、わざと入ることもできます。

わざと入る代表的な理由が、-saveNamespaceです。コマンドリファレンスによると、このコマンドは現在の名前空間を保存ディレクトリに新しいfsimageとして書き、editsを新しく開始しますが、セーフモードが必要です。写真を撮っている間に名前空間が変わると、写真がどのtxidの姿なのかがぼやけるからです。

ディスクを開かずに読む方法

fsimageとeditsはバイナリなので、catでは読めません。代わりに、2つのオフラインツールがあります。オフラインイメージビューアー(oiv)は、fsimageを人間が読める形式に変換し、クラスターが起動していなくてもかまいません。デフォルトのプロセッサーは、読み取り専用のWebHDFSを起動するWebで、-p XMLはすべての情報を含む最も大きな出力を、-p Delimitedは、パス・レプリカ数・ブロックサイズ・ブロック数・ファイルサイズ・クォータ・権限を、1行に1つ、区切り文字で並べた出力を出します。スクリプトで数えたり絞り込んだりするには、Delimitedが便利です。

オフラインエディットビューアー(oev)は、editsの断片を読みます。デフォルトのプロセッサーがxmlなので、RECORDごとにOPCODE(OP_MKDIR、OP_ADD、OP_CLOSE、OP_DELETEなど)とTXIDが見え、-p statsはオペコードごとの件数だけを数えてくれます。誰がいつ何を削除したかを監査するときや、名前空間がなぜ急に大きくなったかを見るときに使うツールです。

現場での姿

1つ目は、デフォルトの保存場所が/tmpの下であることです。dfs.namenode.name.dirのデフォルトはfile://${hadoop.tmp.dir}/dfs/nameで、hadoop.tmp.dirは/tmp/hadoop-${user.name}です。設定なしで起動したNameNodeは、サーバーを再起動して/tmpが空になった瞬間、名前空間を丸ごと失います。本番では、この値を必ず変更し、カンマで複数のディレクトリを指定すれば、同じ内容がすべてに複製されます。

2つ目は、チェックポイントが止まっても、しばらくは何の症状もないことです。Secondaryが死んでいると、editsだけが延々と長くなり、数週間後に再起動する日に、NameNodeが何時間もログを読み直します。最後のチェックポイントの時刻を、監視項目に入れる必要がある理由です。

3つ目は、セーフモードから出なければ、ブロックレポートが足りないということです。DataNodeがまだ起動しきっていないか、ディスクを1つ失って、99.9%に届かない場合です。-safemode leaveで無理に抜ける前に、何が欠けているかをまず見ます。

4つ目は、人間の手でeditsを直す日が来ることです。壊れたログの末尾のせいでNameNodeが起動しないとき、oevでXMLに展開して見て、そのツールが再びバイナリに戻す道も提供しています。することがないことを願いますが、ツールは知っておきます。

実務で本当に大切なこと

次のラボですること

1つのPodで起動したNameNodeが、今セーフモードかをまず確認し、セーフモードに自分で入ってディレクトリの作成が拒否されるのを見ます。その状態でsaveNamespaceで新しいfsimageを作ってファイル名のtxidを読み、セーフモードを解除してディレクトリを1つ作ります。続いて、oivでそのfsimageをXMLと区切り文字形式に展開して、名前空間を読みます。最後に、rollEditsで今書いているedits区間を閉じ、今したディレクトリの作成が入っている閉じた区間をoevで展開して、OP_MKDIRの記録がいくつあるかを数えます。