たごもりすメモ

コードとかその他の話とか。

CDH4 NameNode HA (QJM)でクラスタ構成

CDH4と延々格闘してたが、ようやくひととおり設定が終わったのでまとめ。
特にNameNode HA QJM版はドキュメントもけっこうグチャグチャで何をどうすればいいのかの把握が困難だった。また Auto Failover は設定するとマトモに動かなかったので無効にした。そのうち調べてもいいけど、実運用上も特に要らなそうだし、まあいいかな、と。

で、手順と設定のポイントについていくつか。なおNameNode Federationは使ってないので知らん。使うならクラスタ名の指定とかに影響があるはず。

セットアップ順序

基本的にはこのドキュメントを読む。
Redirecting...

が、通常のセットアップとの関係やどういう順序で全体を進行すればいいかがいまいちちゃんと書いてなくて不明なところが多い。簡単に概要をまとめると以下のようになる。パッケージ名はyumでのものなので適当に読み替えを。

  • Journal用の3台にインストールするもの (うち2台はNameNode)
    • zookeeper, zookeeper-server
    • hadoop-hdfs-journalnode
  • NameNode用の2台にインストールするもの
  • その他ノードにインストールするもの

これらは適当にインストールしておく。そのほか hive などは必要に応じて入れておく。またOS設定は最初にやっとく。

  • limits変更
    • PAMが有効になってない場合は有効にする
      • /etc/pam.d/sshd を適当に書く
      • sshdで UsePAM yes して再起動する
      • ssh接続できるか確認しとく
    • /etc/security/limits.conf を編集
      • "* soft nofile 32768" みたいな感じで
    • ログイン後に有効になってるか ulimit -n で確認
  • kernel option変更
    • vm.swappiness とか適当に /etc/sysctl.conf を変更したり sysctl -p したりする
  • mount option変更
    • noatime, nodiratime を有効にするようfstabに書く
    • rebootがめんどうなら mount -o remount,noatime,nodiratime /path/to/mount
  • resolv.conf をチェック

設定を放り込む。内容についてはあとで。基本的に /etc/hadoop/conf とかから読めるようにしておくのと、必要な環境変数は /etc/hadoop/conf/hadoop-env.sh に書いておくのを忘れないこと。

各ノードの起動等を以下の順序でやる。journalnodeが動いてないと hdfs namenode -format が成功しない。またstandby側NameNodeの起動時注意事項についてはたぶんドキュメントのどこにも書いてない……と思う。

なお Auto Failover を結局やっていないのでZookeeper(やZKFC)要らない気はするんだけど、まあとりあえず入れとくといいと思う。心変わりするかもしれないし。

  1. Journal用ノード3台で zookeeper-server
    • service zookeeper-server init
    • service zookeeper-server start
  2. Journal用ノード3台で journalmanager
  3. active側namenodeで初期化と起動
  4. standby側namenodeの起動
    • active側 namenode の dfs.namenode.name.dir で指定したところからデータを丸ごとコピー
    • hdfs hdfs namenode -bootstrapStandby
    • やっぱactive側から丸ごとコピーしないとダメかも……。
    • service hadoop-hdfs-namenode start
  5. active namenodeの指定
    • hdfs haadmin -transitionToActive master01
    • hdfs haadmin -getServiceStatus master01 (確認、activeになってればOK)
    • hdfs haadmin -failover master01 master02 (master01から02へfailover、from to の順番で指定すること)

2台目のNameNodeでは1台目からデータをコピってきて置いてやったらうまくいった。 @choplin さんにおしえてもらった。(2/2追記: 見付けられていなかったドキュメント に正確な方法があったようだ)

(2014/5/21追記:) ひさびさにクラスタを作ってたら、この -bootstrapStandby はこのエントリの記述時点では正常に動作しなかったようだ。ええええええ。まじかよ。HDFS HA構成のガイド は -bootstrapStandby だけでうまくいくみたいに書いてあるが Known Issues にうまくいかないからrsyncしろって書いてある。えええー。
で、試してみたら CDH4.4.0 ではダメで CDH4.5.0 以降だとちゃんと動いた。やれやれ。

(追記ここまで)

ここで書いている "master01" とか "master02" はコマンドのUsageを見ると と書いてあるもの。あとで出てくる。

activeな側で port 50070 をブラウザから見るとちゃんと Active って書かれて QJM まわりのステータスなんかも出てくるので確認する。またWebHDFSは active のほうに繋がないと使えない。逆に言うと failover してactiveノードが切り替わったら、新しくactiveになった方に繋げばちゃんと使える。アクセス系はPowerDNSで切り替えてDNS based failoverやるかなあ。

QJMベースのHA設定について

zookeeper以外はほとんど hdfs-site.xml にまとまってるので、なんとかなる。頑張る。まず fs.defaultFS だけはこんな。

<!-- core-site.xml -->
 <property>
    <name>fs.defaultFS</name>
    <value>hdfs://hacluster</value>
 </property>

名前は適当につける。ここだけ変更したら、あとは hdfs-site.xml であれこれやっつける。

  <property>
    <name>dfs.nameservices</name>
    <value>hacluster</value>
  </property>
  <property>
    <name>dfs.ha.namenodes.hacluster</name>
    <value>master01,master02</value>
    <!-- list of serviceId -->
  </property>

  <!-- dfs.namenode.rpc-address.CLUSTER.SERVICEID -->
  <property>
    <name>dfs.namenode.rpc-address.hacluster.master01</name>
    <value>master01.cdh4.local:8020</value>
  </property>
  <!-- dfs.namenode.http-address.CLUSTER.SERVICEID -->
  <property>
    <name>dfs.namenode.http-address.hacluster.master01</name>
    <value>master01.cdh4.local:50070</value>
  </property>

  <property>
    <name>dfs.namenode.rpc-address.hacluster.master02</name>
    <value>master02.cdh4.local:8020</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.hacluster.master02</name>
    <value>master02.cdh4.local:50070</value>
  </property>

クラスタ名と serviceId をこういうふうにつける。dfs.ha.namenode.hacluster でリストアップしたノードごとに rpc-address と http-address を入れていく。まあ並べるだけですね。

  <property>
    <name>dfs.namenode.shared.edits.dir</name>
    <value>qjournal://master01.cdh4.local:8485;master02.cdh4.local:8485;master03.cdh4.local:8485/mycluster</value>
  </property>

  <property>
    <name>dfs.journalnode.edits.dir</name>
    <value>/disk01/dfs/jn</value>
  </property>

  <property>
    <name>dfs.client.failover.proxy.provider.hacluster</name>
    <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
  </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>
  </property>
  <property>
    <name>ha.zookeeper.quorum</name>
    <value>master01.cdh4.local:2181,master02.cdh4.local:2181,master03.cdh4.local:2181</value>
  </property>

dfs.namenode.shared.edits.dir でeditsを書き込むQJMのリストをURI形式で指定する。ホスト部をセミコロンで並べるのがだいぶ変な表記だけど。また dfs.ha.fencing.methods にはfailover時に本当に active になって良いかどうかのチェックに使う処理を記述する、が、まあ特にチェックすること無いよねって場合は常に成功するコマンドを指定しとけばいい。無指定はできないらしい。
そして dfs.ha.automatic-failover.enabled は false にしてZKFCは使わない。 ha.zookeeper.quorum という指定が混乱を招くが、これはたぶんjournalnodeまわりとは関係のない話、だと思う、たぶん。

基本的にQJM based HAの設定はこれで全部のはず。そんなに変なものは無い。

ログ設定

なんのことかというと、このエントリの問題ですが。その回避をしておく。

Yet Another HDIF? — QJM構成でデバッグを有効にするとネームノードが起動しない件

log4jの設定でDEBUG LOGを受け入れる設定になった状態だと standby namenode が起動しなくなる(すぐ落ちる)。これを回避するために log4j.properties で以下のように設定する。"all"とかになってると死亡。

log.threshold=info

自分の場合は設定として log4j.xml を使っていた。こちらのエントリの通りにして作ったやつ。
これだと以下のようになってしまっていた。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration threshold="all" xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender class="org.apache.log4j.ConsoleAppender" name="console">
        <param value="System.err" name="target"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param value="%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n" name="ConversionPattern"/>
        </layout>
    </appender>
    <!-- 省略 -->
    <appender class="org.apache.log4j.RollingFileAppender" name="RFA">
        <param value="${hadoop.log.dir}/${hadoop.log.file}" name="File"/>
        <!-- <param value="${hadoop.log.maxbackupindex}" name="MaxBackupIndex"/> -->
        <param value="5" name="MaxBackupIndex"/>
        <!-- <param value="${hadoop.log.maxfilesize}" name="MaxFileSize"/> -->
        <param value="10MB" name="MaxFileSize"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param value="%d{ISO8601} %p %c: %m%n" name="ConversionPattern"/>
        </layout>
        <filter class="org.apache.log4j.varia.StringMatchFilter" >
            <param name="StringToMatch" value="FileNotFoundException" />
            <param name="AcceptOnMatch" value="false" />
        </filter>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="levelMin" value="WARN"/>
            <param name="levelMax" value="FATAL"/>
            <param name="acceptOnMatch" value="true"/>
        </filter>
    </appender>
    <!-- 後略 -->  
    <root>
        <appender-ref ref="RFA" />
        <!-- <appender-ref ref="EventCounter"/> -->
    </root>
</log4j:configuration>

これだと RFA で LevelRangeFilter に WARN から FATAL を指定してるんでいいかと思ってた、ら、先頭のほうのこれがひっかかってた。

<log4j:configuration threshold="all" xmlns:log4j="http://jakarta.apache.org/log4j/">

こいつを "info" に変更してnamenodeふたつを再起動したら対処完了。

以上!