sgykfjsm.github.com

mesosをDEBパッケージからインストールする

mesosをインストールする場合、ソースをコンパイルしてインストールすることが公式のGetting Started with Apache Mesosに記載されているが、この方法だとコンパイルに非常に時間がかかる(10分から20分ぐらい)ので、mesosphereが提供しているパッケージを利用したほうが良い。
ここでは、パッケージからインストールする手順をまとめる。

事前準備の前に

mesosだけを動かすならたぶんメモリは512MBとかでも動くと思うけど、実際にはmesosの上でHadoopやSpark、Chronosを動かす必要がある、というかこれらのアプリケーションと協働しないと意味がないので、クラスターのノードには2GBぐらいのメモリとCPUは2発以上欲しいところ。とりあえず動かすならCPUは1発でも大丈夫。

また、mesosのWeb UIではSlaveがホスト名で表示され、その情報を見ることができるが、Slaveのホスト名が名前解決できるようになっていないといけない。ローカルのVMで試す場合やグローバルで名前解決できない場合は、それぞれを/etc/hostsに登録するなどして置く必要がある。

というか、お金がある人はこんなことに時間を使わずにElastic Apache Mesosを使ったほうが良いと思う。マジで。

私のようにお金がない人は以下のように手元の環境で試さざるを得ないと思うので、このポストがそういう人たちの役に立てたら結構うれしい。

先に読んでおくと捗る記事

  • Mesos: A Cloud Scheduler (1)
    • ソースについての解説があって、mesosを知る上でかなり勉強になる。ただ、記事自体は着目が早かった為か、もしかしたら若干古い可能性がある。
  • 夢とガラクタの集積場
    • 色々と試行錯誤をされており、大変参考になる。これがなかったら多分mesosは諦めていた。
  • 正月休みだし Mesos 触ってみた
    • はっきり言って、私の以降のポストを見るよりかはこっちのほうが読んでてわかりやすい。

事前準備など

今回はmacのvagrantでubuntu on Virtual Boxな環境で試した。なので、vagrantでの設定も事前準備としてついでにまとめておく。

Vagrantの設定

まずは以下のプラグインをインストールする。

$ vagrant pluin install vagrant-cachier
$ vagrant pluin install vagrant-vbguest
$ vagrant pluin install vagrant-vbox-snapshot

多分使わないけど、なんとなく各VMに対して共有フォルダを用意しておく。 $ mkdir data{1,2,3}

Ubuntuをローカルにダウンロードしておく。

$ wget -q -O ~/local/vagrant/box/precise-server-cloudimg-amd64-vagrant-disk1.box http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box

適当な名前でVagrantに登録する。

$ vagrant box add precise ~/local/vagrant/box/precise-server-cloudimg-amd64-vagrant-disk1.box

Vagrantの設定ファイルを作る $ vagrant init precise

設定ファイルを以下のようにする。

# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "precise"
  config.vm.box_url = "/Users/sgyk/local/vagrant/box/precise-server-cloudimg-amd64-vagrant-disk1.box"
  config.cache.auto_detect = true

  config.vm.define :master1 do |master1|
    master1.vm.hostname = "master1"
    master1.vm.network :private_network, ip: "192.168.56.100"
    master1.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--memory", 2048]
      v.name = "mesos_master1"
    end
    master1.vm.synced_folder "data1", "/vagrant_data"
    master1.vm.provision :shell, :path => "./setup.sh"
  end

  config.vm.define :slave1 do |slave1|
    slave1.vm.hostname = "slave1"
    slave1.vm.network :private_network, ip: "192.168.56.101"
    slave1.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--memory", 2048]
      v.name = "mesos_slave1"
    end
    slave1.vm.synced_folder "data2", "/vagrant_data"
    slave1.vm.provision :shell, :path => "./setup.sh"
  end

  config.vm.define :slave2 do |slave2|
    slave2.vm.hostname = "slave2"
    slave2.vm.network :private_network, ip: "192.168.56.102"
    slave2.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--memory", 2048]
      v.name = "mesos_slave2"
    end
    slave2.vm.synced_folder "data3", "/vagrant_data"
    slave2.vm.provision :shell, :path => "./setup.sh"
  end
end

setup.shの内容はこれ。大したことは全くしていなくて、Vagrant用のsshノーパス設定とmesosの依存モジュール(ただしコンパイル時の。いらないかも?)のインストール、mesos本体のインストールを行なう。

だいたいこれでmesosを構築する準備が整う。

hostsを設定する。

mesosを構築する前に先にhostsを設定しておいたほうが何かとハマらずにすむので、やっておくこと。これが意外と大事。

vagrant@master1:~/scripts$ cat /etc/hosts
# 127.0.0.1     master1 master1 localhost

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
192.168.56.100 master1
192.168.56.101 slave1
192.168.56.102 slave2
127.0.0.1 localhost

要は以下のように他ノードの名前解決ができればいいのだけど、ポイントは以下の2つ。

  • 127.0.0.1 hostnameのエントリをコメント文にするなどして無効にすること。
  • 127.0.0.1 localhostのエントリは一番下に置いておくこと。

それぞれはmesosとは直接関係ないけど、まず1つ目は後でmesos上でSpark Shellを動かす際に、Spark Shellはホスト名にプライベートIPのエントリがあってもなぜかループバックアドレスの方を優先してしまい、その情報を他ノードに渡してしまうため、他ノードからマスターノードへタスクを返すことができなくなってしまう。よって、ホスト名に対応するエントリを1つに限定することでこのような事態を防ぐ必要がある。これってなんか変な気がするけど、わざわざのログでYour hostname, master1 resolves to a loopback address: 127.0.1.1; using 192.168.56.100 instead (on interface eth1)とかって出しているぐらいなのでバグじゃなくて仕様なのだと思う。

2つ目は、Hadoopの利用を見越しての設定。namenodeがローカルで名前解決するときに、/etc/hostsのエントリを上から舐めるんだけど、最初に合致したエントリをnamenodeが採用するため、今回の場合だと127.0.0.1でバインドしてしまう。そうすると、以下の様なエラーが出る。

2013-12-23 14:41:13, 046 INFO org.apache.hadoop.ipc.Client: Retrying connect to server: vagrant-mesos-master1/192.168.56.100:55000. Already tried 9 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10,  sleepTime=1 SECONDS)
2013-12-23 14:41:13, 048 WARN org.apache.hadoop.hdfs.server.datanode.DataNode: Problem connecting to server: master1/192.168.56.100:55000

これを防ぐには、上記のように削除orコメントアウトするか、あるいは一番下の行に持っていくとよい。なお、これはすべてのマシンでやっといたほうがいいらしい。詳細はここを参照のこと。

このhostsファイルの設定は全ノードでやっておくこと。スクリプトでばらまいても良いと思う。

mesosを設定する。

で、やっと本筋に到達。

Hadoopにしてもそうだけど、分散処理というかクラスタリングというか、その辺のことを行なうための製品は設定が結構ハマりやすい(ような気がする)。なので、ここからがmesosの構築の本番だったりする。

まず、DEBパッケージでインストールすると、/etc/initにファイルが配置されており、自動起動サービスに登録されていることがわかる。

vagrant@master1:~$ ls -l /etc/init/mesos-*
-rw-r--r-- 1 root root 368 Dec 19 23:12 /etc/init/mesos-master.conf
-rw-r--r-- 1 root root 366 Dec 19 23:12 /etc/init/mesos-slave.conf

そして、ソースからインストールした場合、mesosの起動オプションはmesos-master-env.shだったりコマンドライン引数で渡したりするが、serviceから起動する場合は、オプションによって設定箇所が異なる。

# masterとslaveの両方
vagrant@master1:~$ cat /etc/default/mesos
LOGS=/var/log/mesos
ULIMIT="-n 8192"
# masterのみ
vagrant@master1:~$ cat /etc/default/mesos-master
PORT=5050
ZK=`cat /etc/mesos/zk`
# slaveのみ
vagrant@master1:~$ cat /etc/default/mesos-slave
MASTER=`cat /etc/mesos/zk`

とりあえずmasterを起動してみようとすると、以下のエラーが出て起動できない。

vagrant@master1:/usr/local/var/mesos/deploy$ mesos-master -h
mesos-master: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory

なので、共有ライブラリにlibjvm.soを登録する。

vagrant@master1:/usr/local/var/mesos/deploy$ sudo vi /etc/ld.so.conf.d/libjvm_so.conf
vagrant@master1:/usr/local/var/mesos/deploy$ cat /etc/ld.so.conf.d/libjvm_so.conf
/usr/lib/jvm/java-1.7.0-openjdk-amd64/jre/lib/amd64/server
vagrant@master1:/usr/local/var/mesos/deploy$ sudo /sbin/ldconfig
vagrant@master1:/usr/local/var/mesos/deploy$ sudo ldconfig -p | grep jvm
        libjvm.so (libc6,x86-64) => /usr/lib/jvm/java-1.7.0-openjdk-amd64/jre/lib/amd64/server/libjvm.so
        libjsig.so (libc6,x86-64) => /usr/lib/jvm/java-1.7.0-openjdk-amd64/jre/lib/amd64/server/libjsig.so

これでmesos-mastermesos-slaveが動くようになる。

masterやslaveに設定する内容は-hか、こことかここを参考にすると良い。

とりあえず、今回は以下のように設定した。なお、ホワイトリストも用意しておくこと。このファイルが無いと、延々と「whitelistが無いよ」というログを吐き続けてしまうし、mesosのマスターは誰彼構わずslaveに組み込んでしまうため、セキュリティ的に用意しておいたほうが良い。

mater1:/etc/default$ cat /etc/default/mesos
LOGS=/var/log/mesos
ULIMIT="-n 8192"
vagrant@master1:/etc/default$ cat /etc/default/mesos-master
PORT=5050
ZK=zk://master1:2181,slave1:2181,slave2:2181/mesos
export MESOS_whitelist=file:///usr/local/var/mesos/deploy/slave.lst
vagrant@master1:/etc/default$ cat /etc/default/mesos-slave
MASTER=zk://master1:2181,slave1:2181,slave2:2181/mesos
export MESOS_work_dir=/tmp/`hostname`/mesos
vagrant@master1:~/scripts$ cat /usr/local/var/mesos/deploy/slave.lst
master1
slave1
slave2

これらの設定ファイルは起動スクリプト内で読み込まれるためコマンドを記述することができる。地味に便利である。
が、良く見てもらうとわかるようにzk://file://という記述があったりするので、地味に注意が必要である。

なお、以下については起動スクリプト内の変数として設定されているため、それらの初期値となるように記述する必要がある

  • zk(masterのみ)
  • master(slaveのみ)
  • ip
  • log_dir
  • isolation

設定ファイル以外の場所で設定値を記述したい場合は以下の様にする。詳細は起動スクリプト/usr/bin/mesos-init-wrapper参照のこと。 # echo setting parameter > /etc/mesos-[master|slave]/(attribute) echo ‘192.168.56.100’ > /etc/mesos-slave/ip

これでmesosの設定は完了したが、まだmesosを起動させてはいけない。設定ファイルにzkプロトコルを記述していることからわかると思うけど、Zookeeperをインストールする必要がある。

Zookeeperのインストール

ZookeeperのinstallはClouderaのマニュアルにしたがってリポジトリを追加して、apt-getでインストールすればOK

vagrant@master1:/tmp$ wget http://archive.cloudera.com/cdh4/one-click-install/precise/amd64/cdh4-repository_1.0_all.deb
vagrant@master1:/tmp$ sudo dpkg -i cdh4-repository_1.0_all.Debian
vagrant@master1:/tmp$ curl -s http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh/archive.key | sudo apt-key add -
OK
vagrant@master1:/tmp$ sudo apt-get update
vagrant@master1:/tmp$ sudo apt-get -y install zookeeper-server zookeeper

設定ファイルを以下のように更新する。

vagrant@master1:/tmp$ cat /etc/zookeeper/conf/zoo.cfg | grep -v -e "^#\|^$"
maxClientCnxns=50
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper
clientPort=2181
server.1=master1:2888:3888
server.2=slave1:2888:3888
server.3=slave2:2888:3888

zookeeper-serverが起動していたら、一旦停止させて起動する。

vagrant@master1:/tmp$ sudo service zookeeper-server status
zookeeper-server is running
vagrant@master1:/tmp$ sudo service zookeeper-server stop
JMX enabled by default
Using config: /etc/zookeeper/conf/zoo.cfg
Stopping zookeeper ... STOPPED
vagrant@master1:/tmp$ sudo -u zookeeper /usr/lib/zookeeper/bin/zkServer-initialize.sh --force --myid 1

Force enabled, data/txnlog directories will be re-initialized
Using myid of 1
vagrant@master1:/tmp$ sudo service zookeeper-server start
JMX enabled by default
Using config: /etc/zookeeper/conf/zoo.cfg
Starting zookeeper ... STARTED

これらの作業をslaveでも実施しておく。こんなスクリプトを用意しておくと便利かもしれない。

mesosを起動する。

ここまでやって、ようやくmesosを起動することができる。全ノードでmesosのマスターとスレーブを起動する。これもスクリプトを使って全ノードを対象にマスターとスレーブの起動・停止ができるようにしておくとよさ気。

vagrant@master1:~$ sudo service mesos-master start
vagrant@master1:~$ sudo service mesos-slave start

適当にどっかのマシン(例えばhttp://192.168.56.101:5050/#/ とか)のmesosのWebUIにアクセスして回ると、This master is not the leader, redirecting in 1 seconds … go nowという警告が出て、自動的にZookeeperが選出したmesosのマスターにリダイレクトされる。


とりあえず、mesosの構築はここまで。まぁ結構色々設定に気を使うところが多いけど、慣れてしまえば大丈夫なような気がする。続きはHadoop on mesosとSpark on mesosについて書くので、そこで。色々と触ってmesosの感覚を掴んだら、こまかいアーキテクチャなり関連する論文なりを読んで理解を深めていこうと思う。

以下、気になること。

  • 新しくノードをmesosに追加した場合にwhitelistを更新しないといけないけど、そうするとmesos-masterを再起動しないといけなくなる。serviceコマンドのオプションにはgraceful restartみたいなのは無いと思うので、そうなると順番にマスターを再起動するようなローリング・リスタートみたいなことをしないといけない?
    • ノード数が多いとスゲーめんどくさそう。
  • mesosをHadoopとかSpark抜きで素で使うには?
    • Frameworkなるものを自分で作る必要があるけど、Examplesを見る限り、JavaとかPyhtonのバインディングはあるけどC++で書くのが楽っぽい?ように見えるけど、C++やったこと無いので個人的にはハードルが高い…。
  • じゃあ結局のところ、なんでmesos使うの?
    • カジュアルに大規模な分散(あるいは並行?)処理を実現するにはマシンを並べて処理をぶん回すっていうのが多分今のところの最適解だと思っていて、それを実現しているのがHadoopなのだと思う。ただ、Hadoopが本領を発揮できるのは多分数十から数百GB以上のデータを1時間単位でバッチ処理するような要件だと適切だろうけど、そうじゃないと運用コストのほうがメリットより大きかったり、何よりHive使わずにMapReduce書くのは結構しんどいし、Hiveでは記述できる処理には限界があるので、どうしても大規模だったり計算量の多い処理では行き詰まってしまいがち。mesosはそういった状況に対する一つの解になりうるんじゃないかと思っている。けど、この考えが正しいのか気になるし、他の視点も当然あるだろうから、もっと勉強しないといけない。

というわけで、mesosをちょっと頑張って追究してみようと思った次第。