Apollo
ApolloはMesosクラスタおよび関連のサービスをデプロイするためのツールです。
現在は、AWS, Digital Ocean, GCPに対応しています。OpenStackへの対応も予定されています。
関連のサービスとしては現在はChronos, Marathon等に対応しています。将来的にはKubernetesやSparkにも対応していく計画のようです。
Apollo/roadmap.md at master · Capgemini/Apollo
様々なオープンソースのツールを使っているのが特徴です。以下のようなツールが使用されています。
AWSにMesosクラスタをデプロイする
AWSへのデプロイはVPCを使った方法と使わない方法の2種類が用意されていますが、今回は構成要素が少ないVPCを使わない方法で試してみます。
Apollo/aws-public.md at master · Capgemini/Apollo
AWS, Atlasアカウントの用意
- AWSアカウント
- AmazonEC2FullAccessポリシーを設定し、インスタンスやELBの操作ができるようにしておきます
- Atlasアカウント
- TerraformからAtlasに登録されたArtifactを使用するためにアカウントを作成しトークンを取得しておきます
- Atlas by HashiCorp
- AWSについてはTerraformの不具合のためAMIのIDをハードコーディングしています(2015/09/23時点)
- TerraformからAtlasに登録されたArtifactを使用するためにアカウントを作成しトークンを取得しておきます
Terraform, Pythonのインストール
- Terraformは最新版をインストールしておきます
- ドキュメントだと>=0.5.0になっていますが古いとバージョンチェックで弾かれる可能性があります
- Python >=2.7.5をインストールしておきます
SSH鍵の作成
ドキュメントに従いSSH鍵を作成します。
Apollo/aws-public.md at master · Capgemini/Apollo
レポジトリのクローン
$ git clone https://github.com/Capgemini/apollo.git
$ cd apollo
Pythonパッケージのインストール
Ansibleなどクラスタ構築に使用されるPythonパッケージがインストールされます。
$ pip install -r requirements.txt
環境変数の設定
Apolloがクラスタ構築を行うための環境変数を設定します。
今回は以下の環境変数を設定します。AWS, Atlasの認証情報についてはお使いの環境にあわせて書き換えてください。
direnvなどを使うと何度もコマンドを実行しなくて済むので楽です。
export APOLLO_PROVIDER=aws-public
export TF_VAR_access_key=AWS_ACCESS_KEY
export TF_VAR_secret_key=AWS_SECRET_KEY
export TF_VAR_key_name="deployer"
export TF_VAR_key_file=AWS_SSH_KEY
export ATLAS_TOKEN=ATLAS_TOKEN
slaveの数、リージョン、インスタンスタイプ等も環境変数により設定することが可能です。
使用可能な環境変数については以下のファイルを参照してください。
bootstrap/aws-public/config-default.sh
クラスタの構築・削除
以下のコマンドでクラスタが構築・起動されます。
Terraform・AnsibleでAWSのリソースが作成・設定されていく様子をみることができます。
完了するとブラウザでConsulやMarathon等のウィンドウが開きます。
$ /bin/bash bootstrap/apollo-launch.sh
Ansibleの設定等で失敗してしまう場合は、pipやAnsibleのバージョンをあげるとうまくいくかもしれません。
クラスタの削除
クラスタが不要になったら以下のコマンドでクラスタが削除されます。
$ bash bootstrap/apollo-down.sh
Mesosクラスタを使ってみる
Ansible Dynamic Inventoryを使ってホストの情報を取得する
ApolloはTerraformで作成したインスタンスをAnsibleでデプロイする際に、Terraformで作成したインスタンスの情報を.tfstateファイルからDynamic Inventoryを使って取得しています。
Dynamic InventoryのスクリプトはCiscoCloud/terraform.py(をフォークしたCapgemini/terraform.py)を使用しています。
inventory/terraform.py
にファイルがあるので、次のようにホストの情報を取得することができます。
$ ./inventory/terraform.py --list
$ ./inventory/terraform.py --host apollo-mesos-master-0
Anisbleを使った各ホスト上でのコマンド実行とロールによるホストのグルーピング
Dynamic Inventoryで取得したホスト情報を使って、各ホストでコマンドを実行することができます。
$ ansible -i inventory/terraform.py "*" -a hostname
apollo-mesos-slave-0 | success | rc=0 >>
ip-10-0-0-51
apollo-mesos-master-2 | success | rc=0 >>
ip-10-0-2-8
apollo-mesos-master-1 | success | rc=0 >>
ip-10-0-1-247
apollo-mesos-master-0 | success | rc=0 >>
ip-10-0-0-43
またロールごとにコマンドを実行することも可能です。
次のようにマスター、スレーブごとにコマンドを実行することができます。
$ ansible -i inventory mesos_masters -a hostname
apollo-mesos-master-1 | success | rc=0 >>
ip-10-0-1-247
apollo-mesos-master-0 | success | rc=0 >>
ip-10-0-0-43
apollo-mesos-master-2 | success | rc=0 >>
ip-10-0-2-8
$ ansible -i inventory mesos_slaves -a hostname
apollo-mesos-slave-0 | success | rc=0 >>
ip-10-0-0-51
ロールごとのコマンド実行を可能にするための設定はinventory/inventory
に書かれています。このファイルの先頭部分をみると次のようになっておりロールでホストをグループ分けしていることがわかります。(詳しい書き方はドキュメントを参照)
[role=mesos_masters]
[role=mesos_slaves]
[mesos_masters:children]
role=mesos_masters
[mesos_slaves:children]
role=mesos_slaves
ロールはTerraformでaws_instanceを定義するときにtagで設定されており、terraform.pyがAnsibleのロールとして出力しています。
tags = {
Name = "apollo-mesos-master-${count.index}"
role = "mesos_masters"
}
"tags.#": "2",
"tags.Name": "apollo-mesos-master-0",
"tags.role": "mesos_masters",
$ ./inventory/terraform.py --host apollo-mesos-master-0 | jq .role
"mesos_masters"
各ホストで起動しているDockerコンテナを調べる
ApolloはMesosクラスタのコンポーネントをホストに直接インストールするのではなくDockerコンテナで実行しています。
Ansibleでdocker ps
コマンドを実行することで、各ホストで起動しているコンテナを調べることができます。root権限が必要なので-b
オプションを付ける必要があります。
$ ansible -b -i inventory "*" -a "docker ps"
apollo-mesos-master-2 | success | rc=0 >>
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
142da24684f5 mesosphere/marathon:v0.10.1 "./bin/start --artifa" 26 minutes ago Up 26 minutes 0.0.0.0:8080->8080/tcp marathon
cd390ecd4616 mesosphere/mesos-master:0.23.0-1.0.ubuntu1404 "mesos-master --regis" 26 minutes ago Up 26 minutes mesos-master
2922dffe01d9 mesosphere/mesos:0.23.0-1.0.ubuntu1404 "/usr/share/zookeeper" 26 minutes ago Up 25 minutes zookeeper
b8f0bbe70dd1 google/cadvisor:latest "/usr/bin/cadvisor" 27 minutes ago Up 27 minutes 0.0.0.0:8081->8080/tcp cadvisor
88300025f4a5 gliderlabs/registrator:master "/bin/registrator -in" 27 minutes ago Up 27 minutes registrator
82658e458a68 andyshinn/dnsmasq "dnsmasq -k -r /etc/d" 27 minutes ago Up 27 minutes dnsmasq
apollo-mesos-slave-0 | success | rc=0 >>
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
07910110c741 asteris/haproxy-consul "/launch.sh" 24 minutes ago Up 24 minutes haproxy
fb02b0131fd1 mesosphere/mesos-slave:0.23.0-1.0.ubuntu1404 "mesos-slave" 25 minutes ago Up 25 minutes mesos-slave
c09d8e11694b google/cadvisor:latest "/usr/bin/cadvisor" 27 minutes ago Up 27 minutes 0.0.0.0:8081->8080/tcp cadvisor
27d3f8363a00 gliderlabs/registrator:master "/bin/registrator -in" 27 minutes ago Up 27 minutes registrator
a1ed902190ca andyshinn/dnsmasq "dnsmasq -k -r /etc/d" 27 minutes ago Up 27 minutes dnsmasq
apollo-mesos-master-0 | success | rc=0 >>
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
29b10699aaeb mesosphere/marathon:v0.10.1 "./bin/start --artifa" 26 minutes ago Up 26 minutes 0.0.0.0:8080->8080/tcp marathon
a58baf126727 mesosphere/mesos-master:0.23.0-1.0.ubuntu1404 "mesos-master --regis" 26 minutes ago Up 26 minutes mesos-master
61f62dc803c5 mesosphere/mesos:0.23.0-1.0.ubuntu1404 "/usr/share/zookeeper" 26 minutes ago Up 25 minutes zookeeper
7f8d7bb4d946 google/cadvisor:latest "/usr/bin/cadvisor" 27 minutes ago Up 27 minutes 0.0.0.0:8081->8080/tcp cadvisor
e711621714e9 gliderlabs/registrator:master "/bin/registrator -in" 27 minutes ago Up 27 minutes registrator
69c456497a81 andyshinn/dnsmasq "dnsmasq -k -r /etc/d" 27 minutes ago Up 27 minutes dnsmasq
apollo-mesos-master-1 | success | rc=0 >>
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b5962e6a9bb4 mesosphere/marathon:v0.10.1 "./bin/start --artifa" 26 minutes ago Up 26 minutes 0.0.0.0:8080->8080/tcp marathon
765e78175de9 mesosphere/mesos-master:0.23.0-1.0.ubuntu1404 "mesos-master --regis" 26 minutes ago Up 26 minutes mesos-master
225f9ee1ff8f mesosphere/mesos:0.23.0-1.0.ubuntu1404 "/usr/share/zookeeper" 26 minutes ago Up 25 minutes zookeeper
c9a7ef7a0b0c google/cadvisor:latest "/usr/bin/cadvisor" 27 minutes ago Up 27 minutes 0.0.0.0:8081->8080/tcp cadvisor
75844473c11b gliderlabs/registrator:master "/bin/registrator -in" 27 minutes ago Up 27 minutes registrator
c332d88a7194 andyshinn/dnsmasq "dnsmasq -k -r /etc/d" 27 minutes ago Up 27 minutes dnsmasq
mesos-executeを使ってコマンドを実行する
ここからはMesosphereのチュートリアルに従ってMesosを使ってみます。
まずは、Exercise 3: Using Apache Mesosで説明されているmesos-execute
コマンドを使ってコマンドを実行してみます。
まずapollo-mesos-master-0
にログインします。SSHに必要なIP、ユーザー名はterraform.pyで確認することができます。
$ ./inventory/terraform.py --host apollo-mesos-master-0 | jq ".ansible_ssh_user, .ansible_ssh_host, .ansible_ssh_port"
ホストにログインできたらdocker exec
でmesos-masterコンテナに入ります。
コンテナに入るとプロンプトがubuntu@ip-10-0-0-43:~$
からroot@ip-10-0-0-43:/#
のように変わります。
$ sudo docker exec -it mesos-master /bin/bash
コンテナ内で次のコマンドを実行するとMesosクラスタでsleepコマンドが実行されます。
$ MASTER=$(mesos-resolve `cat /etc/mesos/zk` 2>/dev/null)
$ mesos-execute --master=$MASTER --name="cluster-test" --command="sleep 40"
ブラウザでhttp://<apollo-mesos-master-0のIP>:5050
を開くと、実行したコマンドが表示されていることを確認することができます。
Marathonを使ってコマンドを実行する
ブラウザでhttp://<apollo-mesos-master-0のIP>:8080
を開くとMarathonのGUIが開きます。
「+ New App」ボタンを押して、IDに「test」、Commandに「python -m SimpleHTTPServer」と入力し「+ Create」ボタンを押します。
「/test」がRunningになっていることを確認します。
MesosのGUIにも実行したコマンドが表示されています。
この画面の右のほうに表示されているIPを調べてhttp://<表示されているIP>:8000
をブラウザで開くと以下のような画面が開きWebサーバーが起動していることを確認することができます。
MarathonのGUIで「/test」を選択し、「Destroy App」を押していったんWebサーバーを削除します。
もう一度MaratonのGUIから「+ New App」を押して次のように入力します。
Commandに$PORT
変数を使うことで、ポートを衝突させることなくスケールさせることができるようになります。
「/test」を選択し、「scale」ボタンを押して適当な数を指定するとアプリケーションがその数になることが確認できます。GUIのリンクをクリックすると実際にWebサーバーが起動していることが確認できます。
うまくいかない場合
Waitingから進まない場合
ステータスが「Waiting」のまま変わらなくなってしまう場合があるようです。(特に起動直後の/chronosとか)
このときはmarathonコンテナを1つ再起動してみると処理が進むことがあります。
再起動が成功するのにしばらく待っても処理が進まない場合には別のホスト(apollo-mesos-master-1
, apollo-mesos-master-2
)のmarathonコンテナを再起動してみます。
$ ansible -b -i inventory "apollo-mesos-master-0" -a "docker restart marathon"
marathonコンテナの再起動自体に失敗してしまう場合はdockerを再起動してみます。
$ ansible -b -i inventory "apollo-mesos-master-0" -m service -a "name=docker state=restarted"
状態が「Waiting」から「Deploying」や「Running」になれば成功です。
Deployingから進まない場合
「Deploying」から「Running」に進まない場合は、slaveへのコンテナのデプロイに失敗している場合があります。
MesosのUIからstderrを確認したり、適当なコンテナをデプロイして正常に動いているか確認します。
$ ansible -b -i inventory "apollo-mesos-slave-0" -a "docker run --rm hello-world"
うまく動いていないようであればdockerを再起動してみます。
$ ansible -b -i inventory "apollo-mesos-slave-0" -m service -a "name=docker state=restarted"
Marathonを使ってDockerコンテナを動かす
MarathonからでDockerコンテナを動かすこともできます。
以下の内容でjsonファイルを作成します。
{
"id": "outyet",
"cpus": 0.2,
"mem": 20.0,
"instances": 1,
"container": {
"type": "DOCKER",
"docker": {
"image": "goexample/outyet",
"network": "BRIDGE",
"portMappings": [
{
"containerPort": 8080,
"hostPort": 0,
"servicePort": 0,
"protocol": "tcp"
}
]
}
}
}
MarathonのAPIを使ってAppを作成します。
$ curl -X POST -HContent-Type:application/json -d @outyet.json http://<MarathonのIP>:8080/v2/apps
ステータスがRunningにならない場合は、Marathonからのコマンド実行がうまくいかない場合 と同様の対処を試してみます。
Runningになっていれば/outyet
をクリックして、表示されるリンク先にとぶと下図のような画面が表示されます。
コマンドを実行した場合と同様に「scale」で数を増減させたりすることが可能です。
Chronosを使って定期的にコマンドを実行する
MarathonのGUIから「/chronos」のリンクを開き、表示されているIPアドレスのリンクをクリックすると下図のようなChronosのGUIが開きます。
Chronosを使うとMesosクラスタ上で定期的にコマンドを実行することができます。
「+ New Job」を押してNAME, COMMAND, SCHEDULEを次のように入力し、1分ごとに日時を出力するジョブを作成します。
「Create」を押すとジョブが作成されます。
しばらくするとLASTがSUCCESSになります。
MesosのGUIを確認みるとCompleted TasksにChronosにより実行されたタスクが表示されています。
各タスクのSandbox>stdoutを確認するとコマンドの出力を確認することができます。
Registered executor on xxx.xxx.xxx.xxx
Starting task ct:1443008927000:0:date:
Forked command at 392
sh -c 'date'
Wed Sep 23 11:48:49 UTC 2015
Command exited with status 0 (pid: 392)
Chronosはあるジョブを他のジョブに依存して実行させることができます。
新規に先ほど作成した「date」に依存する「hello」というジョブを作成してみます。
より詳細なChronosの使い方はドキュメントを参照してください。
トップで「Graph」ボタンを押すと依存関係のグラフを表示することができます。
MesosのUIをみると1分ごとに「date」が実行され、直後に「hello」が実行されている様子がわかります。
最後に
Mesosや関連ツールについてほとんど知識がなかったのですが、構築自体は割とスムーズにできました。使いたいツール群をまとめて試せるのはありがたいです。
構築にはそれほど苦労しませんでしたが、コマンド実行が失敗するなど使い方にはつまずくところが結構ありました。特にDockerコンテナの実行はかなり失敗する確率が高かったです。各ツールについて理解を含めて対応できるようにしていきたいです。Dockerコンテナを複数ホストで動かすだけなら、構築もコマンドの実行もDocker Swarmの方がずっと簡単だと思います。