読者です 読者をやめる 読者になる 読者になる

たごもりすメモ

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

CDH4で YARN から MRv1 にスイッチ

Hadoop

CDH4.2 + YARN + Hive でしばらく動かしてたんだけど、なんか一部のクエリが失敗しまくる。おかしいなーと思ったらこれにひっかかってた。

https://issues.cloudera.org/browse/DISTRO-461

なんだかなー。

いい回避策がないかあれこれやってみたんだけどどうにもうまくないので、しょうがないから MRv1 に変えよう、ということにした。
ところで YARN と MRv1 は設定をスイッチして再起動すればまあいけるんじゃね? ということは聞いてたが実際のところはどうなのよ、ということをここにまとめる次第。結論から言うとできた。

おおざっぱにまとめると以下のようにした。

  • 現状の設定ファイルセットを丸ごとコピーして MRv1 用に一部のみ書き換え
    • 切り替えは alternatives で読み込むパスを変更することで行う
  • 変更するファイルは mapred-site.xml と hive-site.xml のみ (yarn-site.xml は優雅に無視する)
  • 環境変数をいじらないといけないので hadoop-env.sh と hive-env.sh も変更対象
  • HDFSまわりは断固変更しない

手順は以下の通り。(HDFS関連は起動しっぱなしで触らない)

  1. hiveserverを停止
  2. resourcemanager / nodemanager を停止
  3. yum install hadoop-0.20-mapreduce-jobtracker ならびに tasktracker でインストール
  4. alternativesで設定ファイルの参照パスを変更
  5. mapred.local.dir で指定したディレクトリの作成・パーミッション変更
  6. jobtracker / tasktracker 起動
  7. hiveserver起動

無事動いたのでこれで多分よいのでしょう。戻すときは逆にすればいい。たぶん。

設定のdiff

まず簡単なところから言うと、YARN設定時に指定してあった HADOOP_MAPRED_HOME があるとうまくmapreduceが走らなったので、これを *-env.sh からコメントアウトする。理屈は知らん。*1

hive-site.xml にJobTrackerなどの情報が書いてあるので、これを変更する。

  <property>
    <name>mapred.job.tracker</name>
    <!--YARN <value>master.local:10020</value> -->
    <value>master.local:8021</value>
  </property>

  <property>
    <name>mapred.job.tracker.http.address</name>
    <!--YARN <value>master.local:19888</value> -->
    <value>master.local:50030</value>
  </property>

あとの変更点は mapred-site.xml のみ。親切な人にもらった設定例をベースにがりがり書き換えて、こんな感じ。これで全部。YARN時の mapred-site.xml を丸ごと置き換えた。

<configuration>

  <property>
    <name>mapreduce.framework.name</name>
    <value>classic</value>
  </property>

  <property>
    <name>mapred.job.tracker</name>
    <value>master.local:8021</value>
  </property>

  <!-- ここは見易さのために改行した -->
  <property>
    <name>mapred.local.dir</name>
    <value>
      /var/hadoop/disk01/mapred/local,/var/hadoop/disk02/mapred/local,
      /var/hadoop/disk03/mapred/local,/var/hadoop/disk04/mapred/local
    </value>
  </property>

  <property>
    <name>io.seqfile.local.dir</name>
    <value>
      /var/hadoop/disk01/mapred/tmp,/var/hadoop/disk02/mapred/tmp,
      /var/hadoop/disk03/mapred/tmp,/var/hadoop/disk04/mapred/tmp
    </value>
    <description>HADOOP-5219</description>
  </property>

  <property>
    <name>mapred.system.dir</name>
    <value>/mapred/system</value>
  </property>

  <property>
    <name>mapreduce.jobtracker.staging.root.dir</name>
    <value>/mapred/staging</value>
  </property>

  <!-- LOG -->
  <property>
    <name>mapreduce.map.log.level</name>
    <value>WARN</value>
  </property>

  <property>
    <name>mapreduce.reduce.log.level</name>
    <value>WARN</value>
  </property>

  <property>
    <name>mapred.userlog.retain.hours</name>
    <value>96</value>
  </property>

  <property>
    <name>mapred.compress.map.output</name>
    <value>false</value>
  </property>

  <property>
    <name>mapred.output.compress</name>
    <value>true</value>
  </property>

  <property>
    <name>mapred.output.compression.type</name>
    <value>BLOCK</value>
  </property>

  <property>
    <name>mapred.output.compression.codec</name>
    <value>org.apache.hadoop.io.compress.GzipCodec</value>
  </property>

  <property> <!-- ADD HERE for MRv1 (and newlines for blog entry)-->
    <name>io.compression.codecs</name>
    <value>
      org.apache.hadoop.io.compress.DefaultCodec,
      org.apache.hadoop.io.compress.GzipCodec,
      org.apache.hadoop.io.compress.BZip2Codec,
      org.apache.hadoop.io.compress.DeflateCodec,
      org.apache.hadoop.io.compress.SnappyCodec
    </value>
  </property>

  <!-- JobTracker -->
  <property>
    <name>mapred.job.tracker.handler.count</name>
    <value>150</value>
  </property>

  <property>
    <name>hadoop.job.history.location</name>
    <value>/mrv1-history</value>
  </property>

  <property>
    <name>mapreduce.job.maps.speculative.execution</name>
    <value>false</value>
    <description>default: true</description>
  </property>

  <!-- for MRv1 ? -->
  <property>
    <name>mapred.map.tasks.speculative.execution</name>
    <value>false</value>
    <description>default: true</description>
  </property>
  <property>
    <name>mapred.reduce.tasks.speculative.execution</name>
    <value>false</value>
    <description>default: true</description>
  </property>

  <property>
    <name>mapreduce.job.reduces.speculative.execution</name>
    <value>false</value>
    <description>default: true</description>
  </property>

  <!-- TaskTracker -->
  <property>
    <name>mapred.tasktracker.map.tasks.maximum</name>
    <value>6</value>
  </property>

  <property>
    <name>mapreduce.map.memory.mb</name>
    <value>1024</value>
  </property>

  <property>
    <name>mapred.tasktracker.reduce.tasks.maximum</name>
    <value>2</value>
  </property>

  <property>
    <name>mapreduce.reduce.memory.mb</name>
    <value>2048</value>
  </property>

  <property>
    <name>mapred.child.java.opts</name>
    <value>-Xmx2048m</value>
  </property>


  <property>
    <name>tasktracker.http.threads</name>
    <value>120</value>
  </property>

  <property>
    <name>mapred.job.reuse.jvm.num.tasks</name>
    <value>1</value>
  </property>

注意点は以下の通り。

  • mapred.system.dir と mapreduce.jobtracker.staging.root.dir , hadoop.job.history.location
    • わざとこれまでに使っていなかった hdfs dir を指定
    • yarn使用の場合とかぶせるとpermissionまわりのエラーが怖いため
  • mapred.tasktracker.map.tasks.maximum と mapred.tasktracker.reduce.tasks.maximum
    • tasktracker毎の指定であることを忘れててクラスタ全体のつもりで指定したらjvm上がりすぎて死にかけた
  • mapred.child.java.opts
    • 指定しないと -Xmx200m でtaskが起動してheap不足で死にまくった、ので指定した
  • mapred.map.tasks.speculative.execution と mapred.reduce.tasks.speculative.execution
    • タスクの投機的実行は抑制したかったので mapreduce.job.*.speculative.execution を指定してたんだけど、これ効いてないっぽい?
    • ので追加で指定した(象本英語3版 p216)
    • けどまだなんか効いてないような……なぜだ

これでYARNのときと変わらない感じでHiveクエリが走るようになった。やれやれ。

いっぽうlog4jが謎のエラー

ところでMRv1に変えたところ、ジョブ完了時に投機的実行タスク*2がkillされるタイミングで該当タスクに以下のようなログが残ってた。

stderr logs
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /var/log/hadoop (Is a directory)
	at java.io.FileOutputStream.openAppend(Native Method)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:192)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:116)
	at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
	at org.apache.log4j.RollingFileAppender.setFile(RollingFileAppender.java:207)
	at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
	at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:307)
	at org.apache.log4j.xml.DOMConfigurator.parseAppender(DOMConfigurator.java:295)
	at org.apache.log4j.xml.DOMConfigurator.findAppenderByName(DOMConfigurator.java:176)
	at org.apache.log4j.xml.DOMConfigurator.findAppenderByReference(DOMConfigurator.java:191)
	at org.apache.log4j.xml.DOMConfigurator.parseChildrenOfLoggerElement(DOMConfigurator.java:523)
	at org.apache.log4j.xml.DOMConfigurator.parseCategory(DOMConfigurator.java:436)
	at org.apache.log4j.xml.DOMConfigurator.parse(DOMConfigurator.java:1004)
	at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:872)
	at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:778)
	at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:526)
	at org.apache.log4j.LogManager.<clinit>(LogManager.java:127)
	at org.apache.log4j.Logger.getLogger(Logger.java:104)
	at org.apache.commons.logging.impl.Log4JLogger.getLogger(Log4JLogger.java:289)
	at org.apache.commons.logging.impl.Log4JLogger.<init>(Log4JLogger.java:109)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
	at org.apache.commons.logging.impl.LogFactoryImpl.createLogFromClass(LogFactoryImpl.java:1116)
	at org.apache.commons.logging.impl.LogFactoryImpl.discoverLogImplementation(LogFactoryImpl.java:914)
	at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:604)
	at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:336)
	at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:310)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:685)
	at org.apache.hadoop.mapred.Child.<clinit>(Child.java:61)

なんでっしゃろねコレ。誰か知ってたら教えてください。

/var/log/hadoopHADOOP_LOG_DIR に指定してある。YARN_LOG_DIR でも同じ指定(今回はたぶん関係ないけど)。うーん、MRv1なログ出力の指定がなにか足りないのかなあ。
他のエラーが出るときはこのログの後に出てたので、まあ実害はなさそうな気はする。気はするんだけどなんか怖い。ううむ。

*1:なんだかなあ。

*2:まだ有効だったとき