正月休みだし1人ハッカソンやろうかなぁと思って、Mesos-Docker でプライベート IaaS っぽいものを構築してみたのでそのメモ。昨日は Mesos を触ったのでその続き。

1

まえおき

最近流行りの Immutable Infrastructure というアイデアは AWS (EC2) のように手軽にインスタンスを作ったり、消したりできるような IaaS があったからこそ発展して出て来たアイデアだと思っている。気軽にインスタンスを作ったり、消したりできないと、稼働中クラスタ(Blue) から、新しく立ち上げたクラスタ (Green) に、LBの向き先をすげかえることでデプロイとするような Blue-Green Deployment なんて正直やる気も起きないと思う。作ったり消したりするだけで何時間かかるんだ、と。

ということで、まずはプライベート IaaS を作ってみようと思う。昨日 Mesos (+ Marathon) を触ったかんじでは、さらに Mesos-Docker を追加すればできそうなかんじ。Immutable Infrastructure 関係なく、社内 IaaS あったらあったで便利だしね ^^ 

おさらい

昨日、Mesos (+ Marathon) を触ってみて

  • Mesos はクラスタリソースマネージャで、物理マシン(物理じゃなくてもいいけど)を一杯ならべて1つのクラスタとして管理し、タスクを走らせる時に効率的にリソースの分配をして実行してくれるもの
  • Mesos は基本的にはタスクを走らせるときにリソースを割り当てて終わったら解放する動きをするため、ウェブアプリのように常時起動するようなアプリケーションを動かすためには別の仕組みが必要で、それを可能にするのが Marathon

ということがわかった。元々プライベート IaaS が作れるのかな?と思って触った Mesos だけれども、Mesos そのものはもっと用途が広くて、Jenkins のジョブを分散処理させるのに使えたりするということもわかった。

今日は、本来の目的だった プライベート IaaS 構築のために、続けて Mesos-Docker を触ってみる。

Mesos-Docker とは

Marthon でウェブアプリを動かせるようになったけれど、Mesos Slave 上でそのままアプリが動くため、例えば rails アプリを動かそうとおもった場合、どの Mesos Slave に配分されても動くように全ての Mesos Slave なホストに ruby とアプリをインストールしておかないといけないわけで、これが perl アプリ、python アプリ、memcached、nginx のように多岐にわたると辛い思いをすることが想像に難くない。

というわけで、Docker コンテナに環境を隔離して、Docker コンテナとアプリを同時に起動させたいと思うのは当然の発想なわけで、それを実現してくれるのが Mesos-Docker である!!ということらしい。

あとは、1. 稼働中インスタンスを Docker イメージに保存して、2. イメージから複数インスタンスを立ち上げる、なんてことができれば IaaS と呼んでいいんじゃないかな!!

ということで試してみる。

Docker のインストール

Mesos から Docker を動かすにあたって、もちろん Docker が動くようにしておく必要があるので入れておく。CentOS の場合、CentOS 6.5 から対応された、と聞いているのでCentOS 6.5 環境に Docker を入れてみる。CentOS 6.5 に Docker をインストールしてみた - Shin x blog を参考にさせて頂いた。

インストール。epel 入れて yum で入れるだけっぽい。簡単ですねー ^^

sudo rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
sudo yum -y install docker-io
sudo chkconfig docker on
sudo service docker start

試す

docker run centos /bin/echo "Hello World"
Hello World

おkおk

補足:RHEL6.4やCentOS6.4でDockerを使う | iOSS という記事もあるので、CentOS 6.5 じゃなくても良いっぽいすね。なんだと。。。

Mesos-Docker のインストール

Mesos-Docker の Tutorial に従ってインストール。

Ubuntu 13.04 環境であれば、Mesos, Marathon, Mesos-Docker をまとめて入れてすぐ試せるようなスクリプトを Mesos-Dockerが用意してくれているのでそれで試せるっぽい。これだけ。

curl -fL https://raw.github.com/mesosphere/mesos-docker/master/bin/mesos-docker-setup | sudo bash

ただ、今回は CentOS 環境に Mesos, Marathon を入れているのと、仕組み理解のため手でインストールしたいので、Mesos-Docker だけ別途入れる。Mesos-Docker 自体は python スクリプト一枚にすぎないっぽい。Mesos, Marathon のインストールは昨日の記事参照。

Mesos-Docker スクリプトが python の mesos モジュールを使うらしいのでそれを入れる。まずは easy_install (もとい、distribute) を入れておく.

curl -O http://python-distribute.org/distribute_setup.py
sudo python distribute_setup.py

mesos の src ディレクトリに行って、egg ファイルを指定して easy_install を使って mesos モジュールを入れる

# cd /path/to/mesos
sudo easy_install ./src/python/dist/mesos-0.14.2-py2.6-linux-x86_64.egg

そしたら Mesos-Docker を所定の位置に配置する。スクリプト1枚。

sudo mkdir -p /var/lib/mesos/executors/
sudo wget https://raw.github.com/mesosphere/mesos-docker/master/bin/mesos-docker -O /var/lib/mesos/executors/docker
sudo chmod a+x /var/lib/mesos/executors/docker

これはついでだけど、Mesos-Docker のサンプルが python な http クライアント httpie を使っているので、一緒にインストールしておこう。curl でも良いとは思うんだけど。

sudo easy_install httpie

Mesos-Docker を試す

昨日触ってた Marathon の Web UI のほうだと、executor (さっき配置した /var/lib/mesos/executors/docker)を指定できないので、REST API を叩いてインスタンスを起動するらしい。Mesos-Docker が python で書かれているということもあって、curl じゃなくて python 製の httpie を推していたので、httpie で REST API を叩いてみる。

Tutorial 通りに johncosta/redis イメージを起動してみる。redis が動くらしい。Docker の動作確認のためにまず単体で動かしてみる. docker pull で時間かかるし。

docker pull johncosta/redis
docker run -p 13000:6379 johncosta/redis

動いたっぽいので、止めて、Marathon の REST API 経由で Mesos-Docker (/var/lib/mesos/executors/docker) を起動し、Docker イメージを起動してみる。インスタンス2つで、それぞれにメモリ 128MB と CPU 1 コア割り当てている。

http POST http://localhost:8080/v1/apps/start \
 id=multidis instances=2 mem=128 cpus=1 \
 executor=/var/lib/mesos/executors/docker \
 cmd='johncosta/redis'

Marathon の REST API と docker ps で確認。2インスタンスが起動していてそれぞれ 31001 と 31000 ポートが Docker イメージの redis が動いている 6379 ポートにマッピングされていることが確認できる。素晴らしい!!

$ http GET http://localhost:8080/v1/endpoints
multidis 10445 localhost:31001 localhost:31000
$ docker ps
CONTAINER ID        IMAGE                          COMMAND                CREATED             STATUS              PORTS                     NAMES
1f334f44596b        johncosta/redis:latest         /usr/bin/redis-serve   20 seconds ago      Up 5 minutes        0.0.0.0:31000->6379/tcp   nostalgic_curie
9ba7e36da75f        johncosta/redis:latest         /usr/bin/redis-serve   20 seconds ago      Up 5 minutes        0.0.0.0:31001->6379/tcp   angry_engelbart

あと、プロセス見るとこんなかんじで起動しているっぽい。

$ ps -ef | grep docker
root      5755  5754  0 Jan5 ?        00:00:41 /usr/bin/docker -d
root     19494 18355  0 15:13 ?        00:00:00 python /var/lib/mesos/executors/docker johncosta/redis
root     19537 19494  0 15:13 ?        00:00:00 docker run -cidfile /tmp/docker_cid.29feb8be79825de5 -c 256 -m 549755813888 -e PORT=31000 -e PORT0=31000 -p 31000:6379 johncosta/redis

Mesos + Marathon だけの時とは違って、Mesos Slave を動かしているホストには Redis を入れていないあたりがポイントになるかな。

一応 redis につないで動作確認。Docker インスタンスにしか redis-cli 入れていないので、telnet で動作確認してみる。

$ telnet localhost 31000
telnet> SET foo bar
+ OK
telnet> GET foo
$3
bar

できてるっぽい ^^

Docker イメージを作って動かしてみる

Tutorial は終わったので、ちょっと応用ということで、試しに @kazeburo さんがやってたGrowthForecast イメージを作って動かしてみる。

以下の内容のDockerfile を用意して

FROM centos
RUN yum -y groupinstall "Development Tools"
RUN yum -y install pkgconfig glib2-devel gettext libxml2-devel pango-devel cairo-devel git ipa-gothic-fonts
RUN git clone https://github.com/tagomoris/xbuild.git
RUN xbuild/perl-install 5.18.1 /opt/perl-5.18
RUN echo 'export $PATH=/opt/perl-5.18/bin:$PATH' > /etc/profile.d/xbuild-perl.sh
RUN /opt/perl-5.18/bin/cpanm -n GrowthForecast
RUN mkdir -p /var/lib/growthforecast
EXPOSE 5125
CMD ["/opt/perl-5.18/bin/growthforecast.pl","--data-dir","/var/lib/growthforecast"]

ビルド。kazeburo さんも言ってたけど perl のインストールからやっているので結構待つ。

docker build -t sonots/growthforecast .

Docker Repository に push。ここでも 8分ぐらい待つ。

docker push sonots/growthforecast

NOTE: push したので、誰でも `docker pull sonots/growthforecast`で GrowthForecast イメージが利用可能ですよ :D

Marathon の REST API を叩いてインスタンスを起動

http POST http://localhost:8080/v1/apps/start \
 id=multidis instances=1 mem=128 cpus=1 \
 executor=/var/lib/mesos/executors/docker \
 cmd='sonots/growthforecast'

今回はお試しなので Public な Docker Repository を使ったけれど、お試しでなければ Docker Private Repository を作ってそこに push すると良いと思う。@hansode 先輩の Private Docker Registryを、あっさりセットアップする | blog.hansode.org あたりが参考になると思う。

Docker イメージを更新してみる

あとは、AWS (EC2) というか一般的な IaaS を思い浮かべると、稼働中インスタンスをイメージに保存したり、それをロードしたりできるわけなので、稼働中インスタンスのイメージを保存できるか試してみた。

普通に考えたら ssh でログインしてイメージをいじる所だけど、イメージ作る時に 22 番ポートを EXPOSE し損なったので、docker run -i /bin/bash 経由で入っててきとうに更新してみる

$ docker run -i -t sonots/growthforecast /bin/bash
bash-4.1# touch foobar

別の端末から。docker commit するのに CONTAINER_ID とやらが必要らしいので docker ps して確認しておく。

$ docker ps
CONTAINER ID        IMAGE                          COMMAND                CREATED             STATUS              PORTS               NAMES
9f1e6bd3b707        sonots/growthforecast:latest   /bin/bash              5 minutes ago       Up 5 minutes        5125/tcp            mad_shockley
1722e2132d86        johncosta/redis:latest         /usr/bin/redis-serve   About an hour ago   Up About an hour    6379/tcp            pensive_euclide

docker commit する。Dockerfile で書いていた EXPOSE とか ENV とかが無効になってしまうらしく、もう1度指定しないといけないっぽい。しかも書式が違う。腐ってる。。。T T

docker commit -run '
{
    "ExposedPorts": {
        "5125/tcp": {}
    }
}' 9f1e6bd3b707 sonots/growthforecast:ver0.1
a19c64a51f57240021a692763d3e4e1ffe4e3c30f9b6bb3d60ffc16d6cc3adc1

commit を push する。

$ docker push sonots/growthforecast
539c0211cd76: Image already pushed, skipping
e0a46671a1a1: Image already pushed, skipping
e9ac1d2181c2: Image already pushed, skipping
73b535b31569: Image already pushed, skipping
0ab2bcc34de5: Image already pushed, skipping
c93a9f909863: Image already pushed, skipping
612ae052964f: Image already pushed, skipping
8ca763201bcf: Image already pushed, skipping
f4dc9b0755e4: Image already pushed, skipping
7b1d554ace5c: Image already pushed, skipping
a19c64a51f57: Pushing [==========>                                        ] 2.048 kB/10.24 kB 25s
Pushing tags for rev [a19c64a51f57] on {https://registry-1.docker.io/v1/repositories/sonots/growthforecast/tags/ver0.1}

push された。あとは、再度 Marathon の REST API を叩いてインスタンスを起動すれば良い。

http POST http://localhost:8080/v1/apps/start \
 id=multidis instances=2 mem=128 cpus=1 \
 executor=/var/lib/mesos/executors/docker \
 cmd='sonots/growthforecast'

まとめ

Mesos-Docker を使って、1. 稼働中インスタンスをイメージに保存して、2. イメージから複数インスタンスを立ち上げる、という IaaS の基礎要素を実現できることが確認できた。あとはそれらをボタン1つでサクッとできるようにすれば、プライベート IaaS の出来上がり、ってかんじかな。

もう少しつっこんで、起動と同時に github からソースコードを pull してアプリを動かす、とかできるようにすると、プライベート PaaS も構築できるのかもしれない。オープンソースアプリだけでこういうものを、サクッとは言えなかったけど ^^; 自分で構築できるようになったとは時代も進んだもんですねー :D

まだまだ触ってみたレベルで、Mesos がやってるタスクの分配アルゴリズムや、リソースの利用を制限している仕組みなど、いろいろ良くわかっていなかったりするので、また時間を取って、もう少し深堀りしていきたい。

ぼやき

ただ、Mesos + Marathon + Docker + Mesos-Docker と4つもコンポーネントを組み合わせて作った上に、それぞれが依存をたくさん持っていて正直辛かった。言語も C++, Python, Java, Scala と多岐に渡ってるし 。

そして、virtualbox イメージ作ろうかと思って、1度環境クリーンにして作り直したら Mesos-Docker が動かなくなったというのはまた別の話。。。腐ってる。。。もう1日正月休みがあれば深堀り出来たと思うんだけど、無念><

以上、@sonotsでした。

追記: @riywo さんに教えてもらった。Vagrant で立ち上げるとデフォルト CPU 1 なので、Marathon にinstances=2 cpus=1 と指定するとコア数が足りなくて2つ目のインスタンスが立ち上がらない、ということだった。ログに ERROR でないとかマジ泣けるT T だから virtualbox 作ろうとしたらおかしくなったのか。Vagrantfile で

   config.vm.provider :virtualbox do |vb|
     vb.customize ["modifyvm", :id, "--cpus", "4"]
     vb.customize ["modifyvm", :id, "--memory", "1024"]
   end

 のように CPU コア数を MBA のコア数 4 に指定したところうまくいくようになった。riywo++