OSvでelasticsearchを動かしてみた

# OSvでelasticsearchを動かしてみた

## 今日の内容

[OSvもくもく会#4](http://connpass.com/event/9512/ "OSvもくもく会#4")でelasticsearchを動かしてみたので、その内容でOSv Advent Calendarを書いてみます。

OSvとelasticsearchはGitHubからcloneしたものを利用しており、今回はOSvはコミットb79223584b(v0.16-27-gb792235)で、elasticsearchはコミット6d872843bdにて動作確認が取れています。

環境はFedora21を利用しています。

## elasticsearch？

公式サイトによると、[elasticsearch](http://www.elasticsearch.org "elasticsearch")は"Elasticsearch is a flexible and powerful open source, distributed, real-time search and analytics engine. "と紹介されており、分散リアルタイム検索・分析エンジンのようです(ずっとKey Valueストアの実装のひとつだとばかり思っていました)。

## elasticsearchのビルド

GitHubからcloneしてきたelasticsearchをビルドします。ソースコードからビルドするのには理由があります。elasticsearchはjarファイルなのですが、実行時にsigarという.so形式のシステムモニタリングライブラリをロードするため、バイナリ配布されているバージョンだと実行できません。
ただ、最新のGitHub上のソースファイルを見るとsigarはビルド時に無効化されているので、バイナリ配布のものでも大丈夫かもしれません(未確認です)。

 * SIGAR - System Information Gatherer And Reporter
   * https://support.hyperic.com/display/SIGAR/Home
   * https://github.com/hyperic/sigar

以下の手順でelasticsearchをビルドします。Maven等のビルドツールが必要なので予めインストールしてください。

```sh
fedora$ git clone https://github.com/elasticsearch/elasticsearch.git
fedora$ mvn clean package -DskipTests
```

ビルドが完了すると、以下のjarファイルが生成されます。

```sh
$ ls target/elasticsearch-2.0.0-SNAPSHOT*.jar 
target/elasticsearch-2.0.0-SNAPSHOT-sources.jar
target/elasticsearch-2.0.0-SNAPSHOT-test-sources.jar
target/elasticsearch-2.0.0-SNAPSHOT-tests.jar
target/elasticsearch-2.0.0-SNAPSHOT.jar
```

## OSvのビルド

続いてOSvをビルドします。REST API経由でjarファイルの転送を行うため、httpserver,javaを指定してビルドします。
cliも忘れずに指定してください。

```sh
fedora$ make image='cli,httpserver,java'
```

## OSv上でのelasticsearch実行

OSvにアプリケーションを移植する場合、appsディレクトリ以下にビルド用の設定ファイルを用意し、"make image=elasticsearch"のような形でビルドできるようにするべきなのですが、今回はそこまでたどり着けていないので、ビルドしたjarファイルを実行する方法で進めてみます。

逆に、移植作業中は以下に示すようなトライアンドエラー的な手順で進め、ある程度動くようになってからappsディレクトリにファイルを用意する、という手もアリかもしれません。

### OSv側での準備

まず、elasticsearch関連のファイルを配置するディレクトリとして/elsと/tmp/elsを予め作成します。

```sh
fedora$ sudo ./scripts/run.py -n
OSv v0.16-27-gb792235
eth0: 192.168.122.89
/# mkdir /els /tmp/els
```

### elasticsearchをOSvにコピーする

次に、ホストOS上からelasticsearchのjarファイル群をOSvに転送します。OSvのIPアドレスは起動時に表示されるので、それを指定します。
ホントはOSv側上でifconfig等を実行してIPアドレスを確認したいのですが、CRaSHからLua CLIに切り替わってからはifconfigが提供されなくなってしまいました...。

ファイルの転送はREST API経由で行います。

```sh
fedora$ curl -XPOST http://192.168.122.89:8000/file/els/elasticsearch.jar -Fname=@./target/elasticsearch-2.0.0-SNAPSHOT.jar
fedora$ for i in `find ./target/lib/*.jar`
        do
            curl -XPOST http://192.168.122.89:8000/file/els/`basename ${i}` -Fname=@${i}
        done
```

elasticsearchのjarファイルは以下のような配置になります。

```sh
/# ls -l /els
total 71052
-rwxrwxrwx 1 osv osv 167735   Dec 23 17:37 antlr-runtime-3.5.jar
-rwxrwxrwx 1 osv osv 448794   Dec 23 17:37 apache-log4j-extras-1.2.17.jar
-rwxrwxrwx 1 osv osv 47359    Dec 23 17:37 asm-4.1.jar
-rwxrwxrwx 1 osv osv 38090    Dec 23 17:37 asm-commons-4.1.jar
-rwxrwxrwx 1 osv osv 41123    Dec 23 17:37 commons-cli-1.2.jar
-rwxrwxrwx 1 osv osv 412739   Dec 23 17:37 commons-lang3-3.3.2.jar
-rwxrwxrwx 1 osv osv 110694   Dec 23 17:37 compiler-0.8.13.jar
-rwxrwxrwx 1 osv osv 79732    Dec 23 17:37 compress-lzf-1.0.2.jar
-rwxrwxrwx 1 osv osv 13247055 Dec 23 17:37 elasticsearch.jar
-rwxrwxrwx 1 osv osv 7065645  Dec 23 17:37 groovy-all-2.3.2-indy.jar
-rwxrwxrwx 1 osv osv 2256213  Dec 23 17:37 guava-18.0.jar
-rwxrwxrwx 1 osv osv 1307968  Dec 23 17:37 hppc-0.6.0.jar
-rwxrwxrwx 1 osv osv 225316   Dec 23 17:37 jackson-core-2.4.2.jar
-rwxrwxrwx 1 osv osv 45230    Dec 23 17:37 jackson-dataformat-cbor-2.4.2.jar
-rwxrwxrwx 1 osv osv 75008    Dec 23 17:37 jackson-dataformat-smile-2.4.2.jar
-rwxrwxrwx 1 osv osv 324650   Dec 23 17:37 jackson-dataformat-yaml-2.4.2.jar
-rwxrwxrwx 1 osv osv 914597   Dec 23 17:37 jna-4.1.0.jar
-rwxrwxrwx 1 osv osv 38460    Dec 23 17:38 joda-convert-1.2.jar
-rwxrwxrwx 1 osv osv 581571   Dec 23 17:38 joda-time-2.3.jar
-rwxrwxrwx 1 osv osv 794991   Dec 23 17:38 jts-1.13.jar
-rwxrwxrwx 1 osv osv 489884   Dec 23 17:38 log4j-1.2.17.jar
-rwxrwxrwx 1 osv osv 1529188  Dec 23 17:38 lucene-analyzers-common-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 365795   Dec 23 17:38 lucene-backward-codecs-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 2230594  Dec 23 17:38 lucene-core-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 75222    Dec 23 17:38 lucene-expressions-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 107452   Dec 23 17:38 lucene-grouping-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 139676   Dec 23 17:38 lucene-highlighter-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 65215    Dec 23 17:38 lucene-join-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 33735    Dec 23 17:38 lucene-memory-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 160334   Dec 23 17:38 lucene-misc-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 201533   Dec 23 17:38 lucene-queries-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 391276   Dec 23 17:38 lucene-queryparser-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 111979   Dec 23 17:38 lucene-sandbox-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 155557   Dec 23 17:38 lucene-spatial-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 177741   Dec 23 17:38 lucene-suggest-5.0.0-snapshot-1646179.jar
-rwxrwxrwx 1 osv osv 1302165  Dec 23 17:38 netty-3.9.3.Final.jar
-rwxrwxrwx 1 osv osv 428580   Dec 23 17:38 sigar-1.6.4.jar
-rwxrwxrwx 1 osv osv 25689    Dec 23 17:38 slf4j-api-1.6.2.jar
-rwxrwxrwx 1 osv osv 102177   Dec 23 17:38 spatial4j-0.4.1.jar
-rwxrwxrwx 1 osv osv 49754    Dec 23 17:38 t-digest-3.0.jar
```

## OSv起動時にelasticsearchが立ち上がるようにする

OSvは基本的にシングルプロセスで動作するため、起動時にLua CLIではなくelasticsearchが起動するように設定します。
この設定もREST API経由で行います。
コマンドが長いのでシェルスクリプト経由で設定すると良さげです。

```sh
$ cat setup_els_to_osv.sh 
#!/bin/sh

cmdline=""
cmdline="$cmdline java.so "
cmdline="$cmdline -Djava.awt.headless=true "
cmdline="$cmdline -Des.foreground=yes "
cmdline="$cmdline -Des.path.home=/tmp/els"
cmdline="$cmdline -cp /els/elasticsearch.jar:/els/* "
cmdline="$cmdline org.elasticsearch.bootstrap.Elasticsearch"

curl -X POST -d cmdline="$cmdline" http://192.168.122.89:8000/os/cmdline
curl -XGET http://192.168.122.89:8000/os/cmdline
```

設定後、OSvを再起動します(REST APIからでも再起動できます)。

```sh
/# reboot
OSv v0.16-27-gb792235
```

再起動すると、何やらいきなり例外が発生しますが、慌てずにぐっと我慢します。

```sh
/# reboot
OSv v0.16-27-gb792235
Failed to configure logging...
org.elasticsearch.ElasticsearchException: Failed to load logging configuration
        at org.elasticsearch.common.logging.log4j.LogConfigurator.resolveConfig(LogConfigurator.java:139)
        at org.elasticsearch.common.logging.log4j.LogConfigurator.configure(LogConfigurator.java:89)
        at org.elasticsearch.bootstrap.Bootstrap.setupLogging(Bootstrap.java:96)
        at org.elasticsearch.bootstrap.Bootstrap.main(Bootstrap.java:171)
        at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:32)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at io.osv.ContextIsolator.runMain(ContextIsolator.java:233)
        at io.osv.ContextIsolator.access$400(ContextIsolator.java:32)
        at io.osv.ContextIsolator$3.run(ContextIsolator.java:118)
Caused by: java.nio.file.NoSuchFileException: /tmp/els/config
        at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
        at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
        at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
        at sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55)
        at sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:144)
        at sun.nio.fs.LinuxFileSystemProvider.readAttributes(LinuxFileSystemProvider.java:97)
        at java.nio.file.Files.readAttributes(Files.java:1684)
        at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:109)
        at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:69)
        at java.nio.file.Files.walkFileTree(Files.java:2600)
        at org.elasticsearch.common.logging.log4j.LogConfigurator.resolveConfig(LogConfigurator.java:123)
        ... 11 more
log4j:WARN No appenders could be found for logger (node).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
WARNING: fcntl(F_SETLK) stubbed
40171 [Thread-3] DEBUG Sigar  - no libsigar-amd64-linux.so in java.library.path
org.hyperic.sigar.SigarException: no libsigar-amd64-linux.so in java.library.path
        at org.hyperic.sigar.Sigar.loadLibrary(Sigar.java:172)
        at org.hyperic.sigar.Sigar.<clinit>(Sigar.java:100)
        at org.elasticsearch.monitor.sigar.SigarService.<init>(SigarService.java:40)
        at org.elasticsearch.monitor.MonitorModule.configure(MonitorModule.java:70)
        at org.elasticsearch.common.inject.AbstractModule.configure(AbstractModule.java:60)
        at org.elasticsearch.common.inject.spi.Elements$RecordingBinder.install(Elements.java:204)
        at org.elasticsearch.common.inject.spi.Elements.getElements(Elements.java:85)
        at org.elasticsearch.common.inject.InjectorShell$Builder.build(InjectorShell.java:130)
        at org.elasticsearch.common.inject.InjectorBuilder.build(InjectorBuilder.java:99)
        at org.elasticsearch.common.inject.Guice.createInjector(Guice.java:93)
        at org.elasticsearch.common.inject.Guice.createInjector(Guice.java:70)
        at org.elasticsearch.common.inject.ModulesBuilder.createInjector(ModulesBuilder.java:59)
        at org.elasticsearch.node.internal.InternalNode.<init>(InternalNode.java:200)
        at org.elasticsearch.node.NodeBuilder.build(NodeBuilder.java:159)
        at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:66)
        at org.elasticsearch.bootstrap.Bootstrap.main(Bootstrap.java:196)
        at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:32)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at io.osv.ContextIsolator.runMain(ContextIsolator.java:233)
        at io.osv.ContextIsolator.access$400(ContextIsolator.java:32)
        at io.osv.ContextIsolator$3.run(ContextIsolator.java:118)
76791 [Thread-3] DEBUG threadpool  - [Key] creating thread_pool [generic], type [cached], keep_alive [30s]
77264 [Thread-3] DEBUG threadpool  - [Key] creating thread_pool [index], type [fixed], size [4], queue_size [200]
```

例外という名の嵐が過ぎ去ると、elasticsearchが起動してきます。

```sh
135654 [elasticsearch[Key][clusterService#updateTask][T#1]] INFO gateway  - [Key] recovered [0] indices into cluster_state
135714 [elasticsearch[Key][clusterService#updateTask][T#1]] DEBUG cluster.service  - [Key] processing [local-gateway-elected-state]: done applying updated cluster_state (version: 2)
142373 [elasticsearch[Key][clusterService#updateTask][T#1]] DEBUG cluster.service  - [Key] processing [routing-table-updater]: execute
142460 [elasticsearch[Key][clusterService#updateTask][T#1]] DEBUG cluster.service  - [Key] processing [routing-table-updater]: no change in cluster_state
```

## elasticsearchをちょっと試してみる

さっそくホストOS(Fedora)側からOSv上のelasticsearchを触ってみます。

まずはelasticsearchの情報を取得してみます。

```sh
fedora$ curl -X GET http://192.168.122.89:9200/
{
  "status" : 200,
  "name" : "Magnus",
  "cluster_name" : "elasticsearch",
  "version" : {
    "number" : "2.0.0",
    "build_hash" : "6d872843bd30c485a4137b2f418bd833d963cc91",
    "build_timestamp" : "2014-12-23T16:49:13Z",
    "build_snapshot" : true,
    "lucene_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}
```

次に、何となくそれっぽいデータを登録してみます。

```sh
fedora$ curl -XPUT 'http://192.168.122.89:9200/naruko/user/mimori' -d '{ "name": "東郷美森" }'
{"_index":"naruko","_type":"user","_id":"mimori","_version":1,"created":true}
$ curl -XPUT 'http://192.168.122.89:9200/naruko/tweet/1' -d '
{                        
    "user"    : "mimori",             
    "postDate": "2014-23-23T21:30:00",                              
    "message" : "次こそはぼた餅食べてくださいね。有無は言わせない。"
}'
{"_index":"naruko","_type":"tweet","_id":"1","_version":1,"created":true}
```

登録したデータを取得してみます。[勇者部](http://yushabu.jp "讃州中学 勇者部電子広報")部員向けのチャットアプリのバックエンドに利用できそうですね。

```sh
fedora$ curl -XGET 'http://192.168.122.89:9200/naruko/user/mimori?pretty=true'
{
  "_index" : "naruko",
  "_type" : "user",
  "_id" : "mimori",
  "_version" : 1,
  "found" : true,
  "_source":{ "name": "東郷美森" }
}
fedora$ curl -XGET 'http://192.168.122.89:9200/naruko/tweet/1?pretty=true'
{
  "_index" : "naruko",
  "_type" : "tweet",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source":
{
    "user"    : "mimori",
    "postDate": "2014-23-23T21:30:00",
    "message" : "次こそはぼた餅食べてくださいね。有無は言わせない。"
}
}
```

## 既知の問題

さて、これでOSv上でelasticsearchが動作しましたが、現状ではelasticsearchを起動してそのまま放置しておくと、以下のエラーが発生してOSvが落ちてしまうようです。
CLIがまだCRaSHだった頃にelasticsearchを動かした時と同じエラーが発生しています...。

```sh
[backtrace]
0x00000000003ea99c <sched::timer_list::fired()+1100>
0x0000000000393576 <interrupt_descriptor_table::invoke_interrupt(unsigned int)+166>
0x000000000039369b <interrupt+91>
0x0000000000391ebc <???+3743420>
0x00000000003ed67f <sched::cpu::idle()+31>
0x00000000003ed6ae <???+4118190>
0x00000000003ed8e7 <thread_main_c+39>
0x0000000000391ee5 <???+3743461>
```

## OSv上でアプリケーションが動くようになったら...

先述のような問題はありますが、OSvに何かアプリケーションを移植した場合は、OSvのMLにパッチを送ると取り込んでもらえるかもしれません。

パッチの送付方法は12/3のOSv Advent Calendarにて[@deep_tkkn](https://twitter.com/deep_tkkn "@deep_tkkn")さんが[OSv のパッチを送るには](http://tokikane-tec.blogspot.jp/2014/12/osv.html "OSv のパッチを送るには")という記事を書かれているので、これに参考にします。

ただ、elasticsearchについては、まだLua CLIな環境で動作させるとこまでしかたどり着いてないので、make image=elasticsearchとか出来るようにするのはこれからです...。

## まとめ

もっと早くにOSv Advent Calendarに記事を書こうと思っていたのですが、延び延びになっていました。

Javaアプリケーションであれば比較的簡単にOSvへの移植ができそうなので、他にも面白そうなアプリを探してみようと思います。
(それよりもパッチを送るのが先ですね...)