CDH3から始めてCDH4.1までアップグレードして利用し続けていますが、この過程でNameNodeの構成は変更せずに運用してきました。
当然、CDH4からの公式HA構成に関心はあったのですが、複数の更新を同時に行うと危ないとか、英語マニュアル読むのめんどくせー感からミドルレンジに距離を置いていたところに、もりす先生がトライしてくれて、乗るしかないビッグウェーブ到来。待つべきものは他人の検証とはよく言ったものですなぁ。
タイトルはNameNode構成の切り替え、となっていますが、これから新しくQJM HAで組む人にも役に立つ内容となっていますゆえ、私が血反吐を吐いてまとめた情報を是非ご覧くださいませ。
リンク
QuorumJournalManagerによるHA構成の選択理由
CDHに触れ始めて1年が経過しますが、振り返ると大きな更新はそう多く行っていません。CDH3で始めて、勢いでCDH4デビューし、ImpalaのためにCDH4.1にしました。
よって、いままでクラスタを停止したのは2回だけになります。
この間は全て、NameNodeはDRBD+VIPによるエセ冗長化で構成していました。とはいえ、特にHDFSに障害が発生したこともなく平和に運用してきたわけですが、CDH4における最大の利点である(NFSじゃない)公式HA構成は捨てがたく、3度目のクラスタ停止を決意したわけです。
DRBD+VIPによるエセHAで問題が発生したことはないため、当然、QJMを導入することに意味はあるのか考えるわけですが、検証後の私の結論としてはこのような感じです。
今からCDH4.1以降を導入する人はQJM HAで必ず組んだほうが良いと思いますし、イニシエのDRBD構成や、NFS HAを既に組んでいる場合は機を見て早めに済ませた方が長い目で見てよろしいかと思います。
QJM HAの仕組みの要点
細かい仕組みは公式を見ていただくとして、私なりの要点はnon-HAからQJM HAへの移行手順
DRBD+VIPで動いている状態からQJM HAへ変更する手順を記します。VIP周りの扱いは人それぞれだと思うので適当に濁して書きます。今回はマニュアルで言う手動フェイルオーバー(=独自スクリプトによる自動化)を採用しているため、Zookeeper関係は不要となっています。パッケージのインストール
もともと存在していた NN×2 と SNN×1 にJournalNodeを入れます。hadoop-hdfs-zkfc, zookeeper, zookeeper-server は自動フェイルオーバーにのみ必要なので入れません。
1 |
apt-get install hadoop-hdfs-journalnode |
HDFSクラスタの停止
クライアントからNNへのアクセスはVIPのみだったので、ここでVIPを破棄することでHDFSの利用を完全停止。HDFS利用の停止後はできればSNNの最後の同期を確認してからNN, SNNを停止したいところ。とはいえ、NNは停止したらfsimageが最新に更新されるので、無理にSNNを待つ必要はありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# SNNが正常に動作していることを確認する # - dfs.namenode.checkpoint.period を短くして同期させてしまう less /data/hadoop/log/hadoop-hdfs-secondarynamenode-host-of-snn.log # NNのメタデータディレクトリを確認する # - fsimageが更新されているか # - editsログが異常に溜まっていないか ls -l /data/hadoop/cache/hdfs/dfs/name/current/ | less # ここでNN×2でVIPを破棄する # NameNode, SecondaryNameNode, DataNode, # ResourceManager, HistoryServer, NodeManger を全部止める # ジョブ関係のノードもHDFSを利用するため、再起動が必要 /etc/init.d/hadoop-hdfs-namenode stop /etc/init.d/hadoop-hdfs-secondarynamenode stop /etc/init.d/hadoop-yarn-resourcemanager stop; /etc/init.d/hadoop-mapreduce-historyserver stop /etc/init.d/hadoop-hdfs-datanode stop; /etc/init.d/hadoop-yarn-nodemanager stop # 不要になるSNNの削除 apt-get purge hadoop-hdfs-secondarynamenode |
バックアップ
DRBD丸ごとで十分なのですが、損失したら終わるので少し過剰にとっておきます。NNのメタデータ
1 2 |
cd /var/tmp tar -C /data/hadoop/cache/hdfs/dfs/name/ -czf namenode-metadata_20130109.tar.gz current/ |
SNNのメタデータ
1 2 |
cd /var/tmp tar -C /data/hadoop/cache/hdfs/dfs/namesecondary/ -czf bigdata-secondarynamenode-metadata_20130109.tar.gz current/ |
DRBD丸ごと
1 2 |
cd /var/tmp tar -C /drbd -czf bigdata-namenode-drbd_20130109.tar.gz ./ |
DRBDをXFSにフォーマット
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 必要があればumountするためにいったんJNを停止します lsof /drbd /etc/init.d/hadoop-hdfs-journalnode stop umount /drbd drbdadm secondary cluster # DRBDを削除 /etc/init.d/drbd stop ls -l /dev/drbd* apt-get purge drbd8-utils # XFSフォーマットして /data をシンボリックリンクから実ディレクトリに切り替え mkfs -t xfs -f /dev/xvdb1 rm /data rm -R /drbd mkdir /data # /data の行を有効にしてマウント vim /etc/fstab mount -a |
データの復旧
ここの解凍と下記のログの削除はANN,SBN両方で行います。メタデータ部分は後でANNからSBNへscpコピーすることでも可能です。hdfs namenode -bootstrapStandby というSBN準備用コマンドがありますが、現状では丸ごとデータコピーじゃないと動かないようです
1 2 3 |
cd /data tar xzf /var/tmp/bigdata-namenode-drbd_20130109.tar.gz ls -l /data/ |
不要なeditsログの削除
editsログなどが残っているとこんなエラーが出て正常に稼働しないので
1 2 |
FATAL org.apache.hadoop.hdfs.server.namenode.ha.EditLogTailer: Unknown error encountered while tailing edits. Shutting down standby NN. java.io.IOException: There appears to be a gap in the edit log. We expected txid 2668747, but got txid 2668748. |
新構成開始時にあってはならないファイルを削除しておきます。
1 2 3 |
rm /data/hadoop/cache/hdfs/dfs/name/current/edits_* rm /data/hadoop/cache/hdfs/dfs/name/current/seen_txid rm /data/hadoop/cache/hdfs/dfs/name/in_use.lock |
/etc/hostsの編集
今までは fs.defaultFS にVIPを指定していればVIPでLISTENしてくれたのですが、HA設定では基本的には静的アドレスでLISTENされるようになりました。Hadoop Confを利用するクライアントは自動的にANNを使ってくれるものの、FluentdやFUSEは基本無理なので、VIPを使う必要がありどうしたものかと。調べても真の仕様ははっきりしなかったのですが、こうすることで 0.0.0.0:8020 でLISTENすることに成功しました。
core-site.xml
1項目だけ変更します。以降の設定はクライアントも含めて全台に更新してください。
1 2 3 4 5 6 7 8 |
<property> <name>fs.defaultFS</name> <value>hdfs://hacluster</value> <description> NameNodeのホストとポートを指定する。全ノード・クライアントで指定する。 HA構成の場合は dfs.nameservices の値を指定する。 </description> </property> |
hdfs-site.xml
全て追記となります。ホストはDNS管理にしています。元はNNとSNNですが、NNとJNは別名で登録してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<!-- HA NameNode --> <property> <name>dfs.nameservices</name> <value>hacluster</value> <description>HAクラスタ名を名付ける。</description> </property> <property> <name>dfs.ha.namenodes.hacluster</name> <value>master01,master02</value> <description>HA構成のNameNodeのSERVICEIDリスト</description> </property> <!-- HA構成におけるNameNodeのRPCとHTTPのリスト --> <!-- dfs.namenode.rpc-address.CLUSTER.SERVICEID --> <property> <name>dfs.namenode.rpc-address.hacluster.master01</name> <value>host-of-ann:8020</value> </property> <!-- dfs.namenode.http-address.CLUSTER.SERVICEID --> <property> <name>dfs.namenode.http-address.hacluster.master01</name> <value>host-of-ann:50070</value> </property> <property> <name>dfs.namenode.rpc-address.hacluster.master02</name> <value>host-of-sbn:8020</value> </property> <property> <name>dfs.namenode.http-address.hacluster.master02</name> <value>host-of-sbn:50070</value> </property> <!-- HA JournalNode --> <property> <name>ha.zookeeper.quorum</name> <value>host-of-jn-01:2181,host-of-jn-02:2181,host-of-jn-03:2181</value> </property> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://host-of-jn-01:8485;host-of-jn-02:8485;host-of-jn-03:8485/hacluster</value> <description>JounalNodeのリストとクラスタ名</description> </property> <property> <name>dfs.journalnode.edits.dir</name> <value>/data/hadoop/cache/journal</value> <description>JounalNodeがデータを保存するローカルディレクトリ</description> </property> <property> <name>dfs.client.failover.proxy.provider.hacluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> <description>Client Failover Configuration</description> </property> <property> <name>dfs.ha.fencing.methods</name> <value>shell(/bin/true)</value> <describe> If you choose to use no actual fencing methods, you still must configure something for this setting, for example shell(/bin/true). </describe> </property> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>false</value> <description>自動フェイルオーバーのON/OFF</description> </property> <!-- HA Edits Log --> <property> <name>dfs.ha.log-roll.period</name> <value>120</value> <description>editsログのローテーション間隔秒</description> </property> <property> <name>dfs.namenode.num.extra.edits.retained</name> <value>1000</value> <description> editsログの削除時に最低限残しておくトランザクション数。 デフォルトは 1000000 なので本番/テストに応じて要変更。 </description> </property> |
ログ設定
QJM構成でデバッグを有効にするとネームノードが起動しない件 のため threshold を all ではなくinfo にしないとSBNがeditsログローテート時にダウンするため変更しておきます。log4j.xml の場合は
1 |
<log4j:configuration threshold="info" xmlns:log4j="http://jakarta.apache.org/log4j/"> |
そしてフェイルオーバー具合によってはクライアントからStandBy側へアクセスが一度行ってからActiveへ再アクセスするために、StandByに大量のログレベル:ERRORが残ることになります。
1 |
ERROR org.apache.hadoop.security.UserGroupInformation: PriviledgedActionException as:hdfs (auth:SIMPLE) cause:org.apache.hadoop.ipc.StandbyException: Operation category WRITE is not supported in state standby |
おそらく容量的に厳しい量が保存されるので、ログにフィルターを追加しておきます。
1 2 3 4 |
<filter class="org.apache.log4j.varia.StringMatchFilter" > <param name="StringToMatch" value="StandbyException" /> <param name="AcceptOnMatch" value="false" /> </filter> |
クラスタ起動
JournalNode×3
止めたりしたので全部起動します。
1 |
/etc/init.d/hadoop-hdfs-journalnode restart |
ANN
JNのログを初期化します。ログ関係が変になってお掃除するときもこのコマンドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 初期化/お掃除 # - hdfsユーザでやらないと ${user.name} がrootになって hadoop.tmp.dir が # おかしくなりエラーが hadoop.log に残る # - /data/hadoop/cache/hdfs/dfs/name/in_use.lock が残っていてもエラーになる(上記で削除済み) su - hdfs hdfs namenode -initializeSharedEdits exit # 起動 /etc/init.d/hadoop-hdfs-namenode start # StandBy状態なので、SafeMode off になったらActive化 hdfs haadmin -transitionToActive master01 hdfs haadmin -getServiceState master01 |
SBN
起動するだけで、そのままStandByとなります。
1 2 |
/etc/init.d/hadoop-hdfs-namenode start hdfs haadmin -getServiceState master02 |
DataNode
全台で起動します。
1 |
/etc/init.d/hadoop-hdfs-datanode start |
ResourceManager, HistoryServer
ジョブマネージャを起動します。
1 2 |
/etc/init.d/hadoop-yarn-resourcemanager stop /etc/init.d/hadoop-mapreduce-historyserver stop |
NodeManager
全台で起動します。
1 |
/etc/init.d/hadoop-yarn-nodemanager start |
確認
QJM HAクラスタの動作確認事項は最初はINFOログを残して確認するとよいです。
editsログの保管条件
だいぶ検証しましたが、情報の少なさもあり、まだ間違っているかもしれませんので、ご容赦を。トランザクションID
HDFSへの書込番号みたいなものですが、一応おさらい保管条件(=削除条件)
時間単位とトランザクション単位の2つの条件を合わせた結果が保管条件となります。時間単位
まずeditsログのローテートは dfs.ha.log-roll.period 秒間隔で行われます。default:120 なので、ANNとJNには2分で1ファイル作られていくことになります。
次に dfs.namenode.checkpoint.period 秒毎にSBNから同期処理が走るので、default:3600 だと fsimage 2世代保存(dfs.namenode.num.checkpoints.retained : 2)の場合、最長で60分前と120分前まで適用されたfsimageができることになります。
そしてこの古い方のfsimageの”120分前”までのeditsログが確実に保管されることになります。
つまり、1ファイル2分なので、60ファイルです。
トランザクション単位
そこからさらに追加で、dfs.namenode.num.extra.edits.retained(default:1000000)トランザクション数を含むeditsログが確実に保管されます。例えば、1editsログ当り約10,000トランザクションとすると、100ファイル保管されることになります。これを時間単位と合わせると、60 + 100 = 160 で160editsログが残り、それより前のファイルは削除されていくことになります。
だいぶわかりづらいですが、dfs.namenode.num.extra.edits.retained は最も古いfsimageに適用されたトランザクションIDより前のトランザクションをいくつ残すかの設定、ということです(ファイル数ではないです)。
この例だと普通の残数になりますが、テスト環境などもっと書き込みが少ない場合を考えてみると、例えば 1editsログ(2分当り) に 10トランザクションしかない場合、時間単位の 60ファイル残 は同じですが、トランザクション単位で残るファイル数が 100,000 となり、NN, JNのデータディレクトリが大変なことになります。
少なくともテスト環境では dfs.namenode.num.extra.edits.retained を少なくしておくべきですが、本番環境でも果たしてfsimageに適用済みのeditsログを多く残すことに意味があるかというと・・・あまりない気がします。ANNが落ちたらフェイルオーバーするし、SBNが落ちたらfsimage同期&editsログ掃除処理が走らないので、どうも公式設定マニュアルの説明はしっくりきていません。
なので私としては 1,000 ~ 10,000 くらいにしておいて、気に食わなければ後でフェイルオーバーさせながらNN再起動して反映すればいいや、くらいに考えています。
自動フェイルオーバー
もともとVIPを利用していて、監視&独自フェイルオーバーをしていたので、手動コマンドである hdfs haadmin -failover をスクリプトに組み込むことで自動フェイルオーバーを実現しました。監視系統からのスクリプト実行などを組んでいない場合は、公式の自動フェイルオーバーの方が簡潔になるかもなので、マニュアルのZookeeper関連を入れる手順を続けて試し、ANNを落としたりして検証してみてください。
メタデータのバックアップ
これまでメタデータのアーカイブ化&転送はSNNで行っていました。が、SNNはなくなったので、バックアップはANN, SBN両方で走らせて、StandBy状態だったらバックアップ処理に入るようにしています。QJM HAの不満点
NameNodeのメモリ増設が必要になった時にHDFSクラスタの停止なしに運用できる、といった大きなメリットがあるため導入必須とは思いますが、予想していたよりも正しく構成するまで手間取ったのもあり、おいそれと導入したらいいよ!とは言い難いものがあります。(2つ目のテスト環境で検証し、1つ目のテスト環境で手順を確立し、本番でさらに手こずりました)
まぁより強固になったのは事実だし、新年一発目の激しいウォーミングアップになったので、今年もより良質なブログにしていけるよう精進して参りたいと思います。よろしくお願いします。
この投稿をHadoop Conference Japan 2013 Winterに捧げます
外道父