Bigtopと戯れる その2


[Bigtopと戯れる その1](http://qiita.com/ajis_ka/items/2510a530aacf0ec80108)の続きです。ひたすら戯れましたが、課題が残ってしまったので続きを書くかもしれません。

## 目標

* 最新版のHadoopのRPMファイルを作ること
* 自分でパッチを当てたHadoopのRPMファイルを作ること

両方ともこの記事で達成します。

## 最新版のHadoopのRPMファイルを作る

2015/12/22時点のmasterだと、BigtopでインストールするHadoopのバージョンは2.7.1になる。

```bigtop.bom
    'hadoop' {
      name    = 'hadoop'
      relNotes = 'Apache Hadoop'
      version { base = '2.7.1'; pkg = base; release = 1 }
      tarball { destination = "${name}-${version.base}.tar.gz"
                source      = "${name}-${version.base}-src.tar.gz" }
      url     { download_path = "/$name/common/$name-${version.base}"
                site = "${apache.APACHE_MIRROR}/${download_path}"
                archive = "${apache.APACHE_ARCHIVE}/${download_path}" }
    }
```

現状、Apache Hadoopの最新リリースは2.7.1なので、単にビルドするだけでHadoop 2.7.1のRPMが完成するのですが、バージョンを変更したい場合は`base`を2.7.1から変更します。例えば、Hadoop 2.6.3のRPMを作成したい場合は以下のように修正します。

```bigtop.bom
    'hadoop' {
      name    = 'hadoop'
      relNotes = 'Apache Hadoop'
      version { base = '2.6.3'; pkg = base; release = 1 }
      tarball { destination = "${name}-${version.base}.tar.gz"
                source      = "${name}-${version.base}-src.tar.gz" }
      url     { download_path = "/$name/common/$name-${version.base}"
                site = "${apache.APACHE_MIRROR}/${download_path}"
                archive = "${apache.APACHE_ARCHIVE}/${download_path}" }
    }
```

ただし、このまま`gradle hadoop-rpm`を実行しても、hadoop-2.6.3は既にダウンロード済だと言われて、ビルドが実行されない。ビルドするためには、`dl/hadoop-2.7.1-src.tar.gz`を消す必要がある。
消してから再実行すると、既にBigtopでHadoop 2.7.1のために提供されているパッチをHadoop 2.6.3に適用しようとして失敗する。

```エラーログ
Patch #0 (patch0.diff):
patching file hadoop-common-project/hadoop-common/pom.xml
Hunk #1 FAILED at 238.
1 out of 1 hunk FAILED -- saving rejects to file hadoop-common-project/hadoop-common/pom.xml.rej
```

パッチを消して、`build`ディレクトリも消去すればよい。

```
$ rm bigtop-packages/src/common/hadoop/patch0.diff
$ rm -rf build
$ gradle hadoop-rpm
(snip)
Wrote: /home/azureuser/git/bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-hdfs-fuse-2.6.3-1.el6.x86_64.rpm
Wrote: /home/azureuser/git/bigtop/build/hadoop/rpm/RPMS/x86_64/hadoop-debuginfo-2.6.3-1.el6.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.C4ymKd
+ umask 022
+ cd /home/azureuser/git/bigtop/build/hadoop/rpm//BUILD
+ cd hadoop-2.6.3-src
+ /bin/rm -rf /home/azureuser/git/bigtop/build/hadoop/rpm/BUILDROOT/hadoop-2.6.3-1.el6.x86_64
+ exit 0
Executing(--clean): /bin/sh -e /var/tmp/rpm-tmp.C0a7C8
+ umask 022
+ cd /home/azureuser/git/bigtop/build/hadoop/rpm//BUILD
+ rm -rf hadoop-2.6.3-src
+ exit 0

BUILD SUCCESSFUL

Total time: 25 mins 41.469 secs
```

## 自分でパッチを当てたHadoopのRPMファイルを作る

Hadoop 2.6.3にいくつかパッチを当てたHadoopのRPMを作ってみます。

### パッチ適用のために新規ブランチを作成

```
$ git clone https://github.com/apache/hadoop.git
$ cd hadoop
$ git checkout release-2.6.3
HEAD is now at cc865b4... Set release date for 2.6.3
$ git checkout -b myrelease-2.6.3
```

### パッチを適用する

以下のパッチを適用してみる。

  * [HADOOP-8989](https://issues.apache.org/jira/browse/HADOOP-8989): hadoop fs -find feature 
  * [HADOOP-7984](https://issues.apache.org/jira/browse/HADOOP-7984): Add hadoop --loglevel option to change log level

```
$ wget https://issues.apache.org/jira/secure/attachment/12667105/HADOOP-8989.patch
$ git apply -p0 HADOOP-8989.patch --whitespace=fix
../patch/HADOOP-8989.patch:966: trailing whitespace.
   * 
../patch/HADOOP-8989.patch:1132: trailing whitespace.
   * 
../patch/HADOOP-8989.patch:1142: trailing whitespace.
   * 
../patch/HADOOP-8989.patch:1183: trailing whitespace.
   * 
../patch/HADOOP-8989.patch:2549: trailing whitespace.
  
warning: squelched 7 whitespace errors
warning: 12 lines applied after fixing whitespace errors.

# 良い感じにコミットする
$ git add -A
$ git commit -a
$ git log | head -5
commit 345e583734f3bce0b1d8d8a64060c8a6a139e37e
Author: Akira Ajisaka <aajisaka@apache.org>
Date:   Tue Dec 22 15:47:49 2015 +0900

    HADOOP-8989 hadoop fs -find feature.

$ wget https://issues.apache.org/jira/secure/attachment/12681196/HADOOP-7984-branch-2.3.patch
$ git apply -p0 HADOOP-7984-branch-2.3.patch
$ git commit -a
$ git log | head -5
commit f965e1501720b3b218bd448b0da0d908f46d4572
Author: Akira Ajisaka <aajisaka@apache.org>
Date:   Tue Dec 22 16:20:21 2015 +0900

    HADOOP-7984. Add hadoop --loglevel option to change log level.
```

ちなみに、パッチによって`git apply`するときに`-p0`や`--whitespace=fix`や`--binary`が必要かどうかが異なる。(あまり統一感がない)

### Bigtop用のパッチを作る

`git format-patch`コマンドを使うとパッチが生成される。

```
$ git format-patch -p release-2.6.3
0001-HADOOP-8989-hadoop-fs-find-feature.patch
0002-HADOOP-7984.-Add-hadoop-loglevel-option-to-change-lo.patch
```

ただし、Bigtopでパッチを適用するには、これらを`patch*.diff`にrenameして`patch -p1`コマンドで当てられるようにしないといけない。だがしかし、HADOOP-7984のパッチがpatchコマンドで当てられない(CR/LF問題)。この問題が発生するたびに手でパッチの中身をいじるのは、とてもつらい。

### パッチを諦めたい

そもそも、ソースコードをパッチ適用済のものに差し替えればよいのではないか!

* source tarballの作成

```
$ mvn package -Psrc -Dtar -DskipTests
(snip)
$ ls -l hadoop-dist/target/hadoop-2.6.3-src.tar.gz 
-rw-r--r--  1 aajisaka  staff  45689815 Dec 22 18:19 hadoop-dist/target/hadoop-2.6.3-src.tar.gz
```

以下の手順でソースコードを差し替えてRPMを作成できました。

1. `dl/hadoop-2.6.3.tar.gz`を差し替え
1. `build/`を削除
1. `gradle hadoop-rpm`

## RPMのバージョンを変えたい

先ほどの方法で自作RPMは完成するのですが、バージョンが同一だとupdateができません。バージョンを上げてみましょう。まずは、specファイルを確認します。

```bigtop-packages/src/rpm/hadoop/SPECS/hadoop.spec
(snip)
Name: %{hadoop_name}
Version: %{hadoop_version}
Release: %{hadoop_release}
Summary: Hadoop is a software platform for processing vast amounts of data
License: ASL 2.0
(snip)
```

以下、VersionとReleaseに分けて紹介します。

### Versionを上げる

これは、Hadoop側でも対応する必要があります。`pom.xml`を編集して、再ビルドしましょう。

```diff:
$ git diff
diff --git a/pom.xml b/pom.xml
index a5b3ce0..686e384 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.apache.hadoop</groupId>
   <artifactId>hadoop-main</artifactId>
-  <version>2.6.3</version>
+  <version>2.6.3+2.ajisaka0.1.0</version>
   <description>Apache Hadoop Main</description>
   <name>Apache Hadoop Main</name>
   <packaging>pom</packaging>
```

`mvn package -Psrc -Dtar -DskipTests`を実行すると、いつもと違う名前のtarballが出来上がりました。

```
$ ls hadoop-dist/target/
antrun					maven-archiver
hadoop-2.6.3+2.ajisaka0.1.0-src.tar.gz	test-dir
hadoop-dist-2.6.3.jar
```

このtarballを使ってBigtopでビルドしてみます。

1. `dl/`にtarballを配置する
2. `bigtop.bom`を修正する

```bigtop.bom
     'hadoop' {
       name    = 'hadoop'
       relNotes = 'Apache Hadoop'
release = 1 }
       version { base = '2.6.3+2.ajisaka0.1.0'; pkg = base; release = 1 }
       tarball { destination = "${name}-${version.base}.tar.gz"
                 source      = "${name}-${version.base}-src.tar.gz" }
       url     { download_path = "/$name/common/$name-${version.base}"
```

これで、`gradle hadoop-rpm`を実行すると、なんと失敗しました。

```
:hadoop-download
	File /home/azureuser/git/bigtop/dl/hadoop-2.6.3+2.ajisaka0.1.0-src.tar.gz appears to be already downloaded. Exiting...
:hadoop-tar
Copy /home/azureuser/git/bigtop/dl/hadoop-2.6.3+2.ajisaka0.1.0-src.tar.gz to /home/azureuser/git/bigtop/build/hadoop/tar/hadoop-2.6.3+2.ajisaka0.1.0.tar.gz
:hadoop-srpm
error: File /home/azureuser/git/bigtop/build/hadoop/rpm/SOURCES/hadoop-2.6.3+2.ajisaka0.1.0.tar.gz: No such file or directory
:hadoop-srpm FAILED

FAILURE: Build failed with an exception.

* Where:
Script '/home/azureuser/git/bigtop/packages.gradle' line: 511

* What went wrong:
Execution failed for task ':hadoop-srpm'.
> Process 'command 'rpmbuild'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 9.564 secs
```

ログを見てもさっぱりわからない...

とりあえずVersionから`+2`を外してみると、かなり進みました。`+`がエスケープできてないのはBigtopのバグっぽい。CDHっぽく適用したパッチ数を`+2`として表してみたかったけど、何か直す必要がありそうです。

そして、エラーログ

```
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-site-plugin:3.3:site (default-site) on project hadoop-main: Error during page generation: Error rendering Maven report: Unable to find artifact:groupId = 'org.apache.hadoop'
[ERROR] artifactId = 'hadoop-annotations'
[ERROR] version = '2.6.3.ajisaka0.1.0': Failure to find org.apache.hadoop:hadoop-annotations:pom:2.6.3.ajisaka0.1.0 in https://repository.apache.org/content/repositories/snapshots was cached in the local repository, resolution will not be reattempted until the update interval of apache.snapshots.https has elapsed or updates are forced
[ERROR] 
[ERROR] org.apache.hadoop:hadoop-annotations:pom:2.6.3.ajisaka0.1.0
```

pomを用意して、専用のrepositoryに置く必要がありそうです。CDHでいうところのrepository.cloudera.comのようなところ。

今後の課題です。

### Releaseを上げる

Releaseを上げるには、`%{hadoop_release}`をいじればよさそうです。おそらく、`rpmbuild`コマンドを叩くときに定義しているはずです。雑に探します。

```
$ find . -type f | xargs grep rpmbuild
./bigtop-packages/src/common/hue/install_hue.sh:# remove RECORD files since it contains "real" paths confusing rpmbuild
./src/site/xdoc/release-notes.xml:					] - Add normal rpmbuild options for make rpm
./build/hadoop/rpm/BUILD/hadoop-2.6.3-src/hadoop-common-project/hadoop-common/CHANGES.txt:    HADOOP-9611 mvn-rpmbuild against google-guice > 3.0 yields missing cglib
grep: ./build/hadoop/rpm/BUILD/hadoop-2.6.3-src/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/webapps/static/dt-1.9.4/images/Sorting: No such file or directory
grep: icons.psd: No such file or directory
./packages.gradle:    def RELEASE_DIST = "rpmbuild --eval '%{?dist}' 2>/dev/null".execute().text.trim().replaceAll("'",'')
./packages.gradle:      executable 'rpmbuild'
./packages.gradle:      executable 'rpmbuild'
./packages.gradle:    def RELEASE_DIST = "rpmbuild --eval '%{?dist}' 2>/dev/null".execute().text.trim().replaceAll("'",'')
./CHANGES.txt:  - [BIGTOP-60] - Add normal rpmbuild options for make rpm
```

`packages.gradle`を読めば良さそうです。

```packages.gradle
411     def command = [
412         '--define', "_topdir $PKG_BUILD_DIR/rpm/",
413         '--define', "${NAME}_base_version $BASE_VERSION",
414         '--define', "${NAME}_version ${PKG_VERSION}",
415         '--define', "${NAME}_release ${BIGTOP_BUILD_STAMP}%{?dist}",
416         '--rebuild', SRCRPM,
417     ]
418     exec {
419       workingDir BASE_DIR
420       executable 'rpmbuild'
421       args command
422     }
```

`${BIGTOP_BUILD_STAMP}`を修正すればよさそうです。設定している部分を探します。

```packages.gradle
 48 config.bigtop.buildstamp = 1
 49 
 50 def bomVersions = []
 51 
 52 def final BASE_DIR = projectDir.absolutePath
 53 def final REPO_DIR = "$BASE_DIR/bigtop-repos"
 54 def final BUILD_DIR = config.bigtop.builddir
 55 def final OUTPUT_DIR = config.bigtop.outputdir
 56 def final DIST_DIR = config.bigtop.distdir
 57 def final DL_DIR = config.bigtop.dldir
 58 def final BIGTOP_BUILD_STAMP = System.getenv('BIGTOP_BUILD_STAMP') ?: config.bigtop.buildstamp
```

`config.bigtop.buildstamp`を差し替えればOKです。試しに、2に設定してビルドし直してみます。すると、見事にReleaseが上がりました。

```
$ ls build/hadoop/rpm/RPMS/x86_64/
hadoop-2.6.3-2.el6.x86_64.rpm                         hadoop-hdfs-zkfc-2.6.3-2.el6.x86_64.rpm
hadoop-client-2.6.3-2.el6.x86_64.rpm                  hadoop-httpfs-2.6.3-2.el6.x86_64.rpm
hadoop-conf-pseudo-2.6.3-2.el6.x86_64.rpm             hadoop-libhdfs-2.6.3-2.el6.x86_64.rpm
hadoop-debuginfo-2.6.3-2.el6.x86_64.rpm               hadoop-libhdfs-devel-2.6.3-2.el6.x86_64.rpm
hadoop-doc-2.6.3-2.el6.x86_64.rpm                     hadoop-mapreduce-2.6.3-2.el6.x86_64.rpm
hadoop-hdfs-2.6.3-2.el6.x86_64.rpm                    hadoop-mapreduce-historyserver-2.6.3-2.el6.x86_64.rpm
hadoop-hdfs-datanode-2.6.3-2.el6.x86_64.rpm           hadoop-yarn-2.6.3-2.el6.x86_64.rpm
hadoop-hdfs-fuse-2.6.3-2.el6.x86_64.rpm               hadoop-yarn-nodemanager-2.6.3-2.el6.x86_64.rpm
hadoop-hdfs-journalnode-2.6.3-2.el6.x86_64.rpm        hadoop-yarn-proxyserver-2.6.3-2.el6.x86_64.rpm
hadoop-hdfs-namenode-2.6.3-2.el6.x86_64.rpm           hadoop-yarn-resourcemanager-2.6.3-2.el6.x86_64.rpm
hadoop-hdfs-secondarynamenode-2.6.3-2.el6.x86_64.rpm  hadoop-yarn-timelineserver-2.6.3-2.el6.x86_64.rpm
```

## 感想

* Bigtopの仕組みでパッチを当てるのは大変。ソースコード自体を差し替えてしまったほうが楽。
* ただし、差し替えるソースコード自体はgitで管理し、バージョンを変えるたびにtagを打つべき。
* Versionをいじるのは、非常に手間がかかる