5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IDCFクラウドのDebian JessieでMulti-Host Docker Networking - Part 5: AmbariとZeppelinをDocker Swarmにセットアップ

Last updated at Posted at 2015-12-30

 これまでAmbari on Dockerシングルホストで使っていました。ようやくMulti-host newtworkDocker Swarmクラスタに慣れてきたので、AmbariZeppelinSparkの学習環境を移行していきます。

このシリーズ

Docker Network

 Part 3のRethinkDBやPart 4のCouchbase ServerではDocker Composeを使っていたので自動的にプロジェクト名のネットワークが作成されました。今回はDocker Composeを使わないため、直接ネットワークを作成します。zeppelinという名前で、サブネットをフラグをつけて指定しました。

$ docker network create \
  --subnet=192.168.0.0/24 \
  --driver=overlay \
  zeppelin

ambari-functions

 ambari-functionsはAmbari on Dockerのセットアップ用関数などが入っています。Apache Zeppelinでデータ分析を分散処理する - Part 4: Ambari on Dockerのambari-functionsを使ってみるで中身を確認しました。

 リポジトリからgit cloneしてabari-functionsファイルをDocker SwarmとDocker Networkの環境で動くように編集していきます。

$ git clone https://github.com/sequenceiq/docker-ambari/
$ cd docker-ambari

ネットワークを指定する

 ネットワーク名を環境変数に定義します。クリア用のamb-cleanにも追加します。 

: ${NETWORK:="zeppelin"}
...
amb-clean() {
  unset NODE_PREFIX AMBARI_SERVER_NAME IMAGE DOCKER_OPTS CONSUL CONSUL_IMAGE DEBUG SLEEP_TIME AMBARI_SERVER_IP EXPOSE_DNS DRY_RUN NETWORK
}

 docker run--net $NETWORKフラグをつけてコンテナを起動するようにします。

_amb_run_shell() {
...
  run-command docker run --net $NETWORK -it --rm -e EXPECTED_HOST_COUNT=$((NODES-1)) -e BLUEPRINT=$BLUEPRINT -e AMBARI_HOST=$AMBARI_SERVER_NAME --entrypoint /bin/sh $IMAGE -c $COMMAND
}
...
amb-start-first() {
...
  run-command docker run -d --net $NETWORK $dns_port_command --name $CONSUL -h $CONSUL.service.consul $CONSUL_IMAGE -server -bootstrap
  sleep 5

  run-command docker run -d --net $NETWORK -e BRIDGE_IP=$(get-consul-ip) $DOCKER_OPTS --name $AMBARI_SERVER_NAME -h $AMBARI_SERVER_NAME.service.consul $IMAGE /start-server
...
amb-start-node() {
...
  run-command docker run $MORE_OPTIONS --net $NETWORK -e BRIDGE_IP=$(get-consul-ip) $DOCKER_OPTS --name ${NODE_PREFIX}$NUMBER -h ${NODE_PREFIX}${NUMBER}.service.consul $IMAGE /start-agent

 --netフラグをつけて作成したコンテナはdocker inspectするとNetworkSettingsフィールドの階層が増えます。コンテナのIPアドレスを取得するget-host-ipを追加された階層に合わせて編集してます。

get-host-ip() {
  HOST=$1
  docker inspect --format="{{.NetworkSettings.Networks.$NETWORK.IPAddress}}" ${HOST}
}

コンテナの中でcurlを使う

 デフォルトのdocker0 bridgeを使わないのでコンテナのIPアドレスには直接Dockerホストからアクセス出来ません。amb-membersなどcurlでConsulのAPIを実行していた箇所はdocker exec $CONSUL curlのようにコンテナの中で使うようにします。

amb-members() {
  docker exec $CONSUL curl http://$(get-consul-ip):8500/v1/catalog/nodes | sed -e 's/,{"Node":"ambari-8080.*}//g' -e 's/,{"Node":"consul.*}//g'
}
...
_consul-register-service() {
  docker exec $CONSUL curl -X PUT -d "{
    \"Node\": \"$1\",
    \"Address\": \"$2\",
    \"Service\": {
      \"Service\": \"$1\"
    }
  }" http://$(get-consul-ip):8500/v1/catalog/register
}

 編集した箇所のdiffは以下のようになりました。 

$ git diff ambari-functions
diff --git a/ambari-functions b/ambari-functions
index 8b07152..6e2f077 100644
--- a/ambari-functions
+++ b/ambari-functions
@@ -18,6 +18,7 @@ USAGE
 : ${DNS_PORT:=53}
 : ${EXPOSE_DNS:=false}
 : ${DRY_RUN:=false}
+: ${NETWORK:="zeppelin"}

 run-command() {
   CMD="$@"
@@ -30,7 +31,7 @@ run-command() {
 }

 amb-clean() {
-  unset NODE_PREFIX AMBARI_SERVER_NAME IMAGE DOCKER_OPTS CONSUL CONSUL_IMAGE DEBUG SLEEP_TIME AMBARI_SERVER_IP EXPOSE_DNS DRY_RUN
+  unset NODE_PREFIX AMBARI_SERVER_NAME IMAGE DOCKER_OPTS CONSUL CONSUL_IMAGE DEBUG SLEEP_TIME AMBARI_SERVER_IP EXPOSE_DNS DRY_RUN NETWORK
 }

 get-ambari-server-ip() {
@@ -43,11 +44,11 @@ get-consul-ip() {

 get-host-ip() {
   HOST=$1
-  docker inspect --format="{{.NetworkSettings.IPAddress}}" ${HOST}
+  docker inspect --format="{{.NetworkSettings.Networks.$NETWORK.IPAddress}}" ${HOST}
 }

 amb-members() {
-  curl http://$(get-consul-ip):8500/v1/catalog/nodes | sed -e 's/,{"Node":"ambari-8080.*}//g' -e 's/,{"Node":"consul.*}//g'
+  docker exec $CONSUL curl http://$(get-consul-ip):8500/v1/catalog/nodes | sed -e 's/,{"Node":"ambari-8080.*}//g' -e 's/,{"Node":"consul.*}//g'
 }

 amb-settings() {
@@ -95,7 +96,7 @@ _amb_run_shell() {
   : ${COMMAND:? required}
   get-ambari-server-ip
   NODES=$(docker inspect --format="{{.Config.Image}} {{.Name}}" $(docker ps -q)|grep $IMAGE|grep $NODE_PREFIX|wc -l|xargs)
-  run-command docker run -it --rm -e EXPECTED_HOST_COUNT=$((NODES-1)) -e BLUEPRINT=$BLUEPRINT --link ${AMBARI_SERVER_NAME}:ambariserver --entrypoint /bin/sh $IMAGE -c $COMMAND
+  run-command docker run --net $NETWORK -it --rm -e EXPECTED_HOST_COUNT=$((NODES-1)) -e BLUEPRINT=$BLUEPRINT -e AMBARI_HOST=$AMBARI_SERVER_NAME --entrypoint /bin/sh $IMAGE -c $COMMAND
 }

 amb-shell() {
@@ -123,10 +125,11 @@ amb-start-first() {
   if [[ "$EXPOSE_DNS" == "true" ]]; then
      dns_port_command="-p 53:$DNS_PORT/udp"
   fi
-  run-command docker run -d $dns_port_command --name $CONSUL -h $CONSUL.service.consul $CONSUL_IMAGE -server -bootstrap
+  run-command docker run -d --net $NETWORK $dns_port_command --name $CONSUL -h $CONSUL.service.consul $CONSUL_IMAGE -server -bootstrap
   sleep 5

-  run-command docker run -d -e BRIDGE_IP=$(get-consul-ip) $DOCKER_OPTS --name $AMBARI_SERVER_NAME -h $AMBARI_SERVER_NAME.service.consul $IMAGE /start-server
+  run-command docker run -d --net $NETWORK -e BRIDGE_IP=$(get-consul-ip) $DOCKER_OPTS --name $AMBARI_SERVER_NAME -h $AMBARI_SERVER_NAME.service.consul $IMAGE /start-server
+

   get-ambari-server-ip

@@ -169,13 +172,13 @@ amb-start-node() {
     MORE_OPTIONS="$@"
   fi

-  run-command docker run $MORE_OPTIONS -e BRIDGE_IP=$(get-consul-ip) $DOCKER_OPTS --name ${NODE_PREFIX}$NUMBER -h ${NODE_PREFIX}${NUMBER}.service.consul $IMAGE /start-agent
+  run-command docker run $MORE_OPTIONS --net $NETWORK -e BRIDGE_IP=$(get-consul-ip) $DOCKER_OPTS --name ${NODE_PREFIX}$NUMBER -h ${NODE_PREFIX}${NUMBER}.service.consul $IMAGE /start-agent

   _consul-register-service ${NODE_PREFIX}${NUMBER} $(get-host-ip ${NODE_PREFIX}$NUMBER)
 }
 _consul-register-service() {
-  curl -X PUT -d "{
+  docker exec $CONSUL curl -X PUT -d "{
     \"Node\": \"$1\",
     \"Address\": \"$2\",
     \"Service\": {

Zeppelinのインストール

 AmbariクラスタとZeppelinをインンストール方法はこちらとほぼ同じです。あとでHiveの設定も行います。

Swarm Manager

 docker runはSwarm Managerを指定して実行する必要があるのでDOCKER_HOST環境変数にexportして使います。3333ポートはPart 2で作成したSwarm Managerのポートです。

$ export DOCKER_HOST=tcp://10.3.0.101:3333

Ambariクラスタの作成

 ambari-functionsを読み込みamb-start-clusterを実行してAmbariクラスタをセットアップします。

$ source ambari-functions
$ amb-start-cluster 4

 minion[1-3]のDockerホストにそれぞれコンテナが作成されました。

$ docker ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                                                              NAMES
f2a8699786d7        sequenceiq/ambari:2.2.0-v1    "/start-agent"           10 seconds ago      Up 7 seconds        8080/tcp                                                           minion-1/amb3
43b8edf67263        sequenceiq/ambari:2.2.0-v1    "/start-agent"           11 seconds ago      Up 10 seconds       8080/tcp                                                           minion-2/amb2
93b610b677a6        sequenceiq/ambari:2.2.0-v1    "/start-agent"           13 seconds ago      Up 11 seconds       8080/tcp                                                           minion-3/amb1
3ba95ef16526        sequenceiq/ambari:2.2.0-v1    "/start-server"          15 seconds ago      Up 12 seconds       8080/tcp                                                           minion-3/amb-server
b7c46370dba6        sequenceiq/consul:v0.5.0-v6   "/bin/start -server -"   21 seconds ago      Up 19 seconds       53/tcp, 53/udp, 8300-8302/tcp, 8400/tcp, 8301-8302/udp, 8500/tcp   minion-2/amb-consul

DockerネットワークとConsul DNS API

 Hadoopクラスタなどでは各ノードの名前解決が必要になります。Docker SwarmとMulti-Host network環境での名前解決は/etc/hostsを使います。-hフラグでホスト名をつけてをコンテナを起動すると自分のhostnameはランダムのコンテナ名でなく指定したホスト名になります。

$ docker exec amb1 cat /etc/hosts
192.168.0.4	amb1.service.consul amb1
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
192.168.0.6	amb3
192.168.0.6	amb3.zeppelin
192.168.0.3	amb-server.zeppelin
192.168.0.2	amb-consul
192.168.0.2	amb-consul.zeppelin
192.168.0.3	amb-server
192.168.0.5	amb2
192.168.0.5	amb2.zeppelin

 ConsulのDNS APIを使うとgliderlabs/registrator
などを使いConsulに登録したサービス名はservice.consulをプリフィックスしてクエリすることができます。ambari-functionsでは-h ${NODE_PREFIX}${NUMBER}.service.consulのように-hフラグで指定するホスト名はあらかじめservice.consulをつけてコンテナを作成しています。ただし他のノードは上記のようにDockerネットワーク名がドメイン名としてプリフィックスされます。amb1.service.consulからはamb2.service.consulでなくamb2.zeppelinになってしまいます。

/etc/resolv.conf

 通常DockerコンテナはDockerホストの/etc/resolv.confコピーされます。以下はIDCFクラウドの場合です。

$ docker run -d -P --name=redis redis
$ docker exec redis cat /etc/resolv.conf
domain cs3d0idcfcloud.internal
search cs3d0idcfcloud.internal
nameserver 10.3.0.1
nameserver 158.205.237.131
nameserver 158.205.229.244

 Ambari on DockerではConsul DNS APIを使いノード間の名前解決をするため/etc/resolv.confにConsulのノードを指定します。コンテナのCOMMANDに指定する[start-agent]
(https://github.com/sequenceiq/docker-ambari/blob/master/ambari-server/start-agent)スクリプトの中で`/etc/resolv.conf`を上書きしています。

$ docker exec amb1 cat /etc/resolv.conf
nameserver 192.168.0.2
search service.consul node.dc1.consul

 ややこしいですがDNS用のConsulはDocker Swarmで使うため各DockerホストにセットアップしたConsulクラスタとは異なり、Docker Swarm上にある独立したConsulコンテナです。Consul DNS API用にDNSが8600でなく53ポートで使えるようにするなど、gliderlabs/docker-consulからforkしたsequenceiq/docker-consulを使っています。

public-hostname.sh

 Multi-host network環境でのDNSは次の課題です。/etc/hostsの本来の使い方はノードに閉じられているので、 $(hostname -f)などで取得したホスト名をノードの外で使うのは良くないらしいのです。

 update amb functions to use ambari 2.2.0
のコミットでambari-functionsが使うAmbari on Dockerのバージョンが2.2.0になりました。

${IMAGE:="sequenceiq/ambari:2.2.0-v1"}

 この変更でAmbari Agentの設定ファイル(/etc/ambari-agent/conf/ambari-agent.ini)で使うpublic-hostname.shがパブリックIPアドレスを使うようになりました。

/etc/ambari-agent/conf/ambari-agent.ini
[agent]
hostname_script=/etc/ambari-agent/conf/internal-hostname.sh
public_hostname_script=/etc/ambari-agent/conf/public-hostname.sh
/etc/ambari-agent/conf/public-hostname.sh
#!/bin/bash

STATUS=$(curl -s -m 5 -w "%{http_code}" http://169.254.169.254/latest/meta-data/public-ipv4 -o /dev/null)
if [ "$STATUS" == "200" ]; then
        curl -s -m 5 http://169.254.169.254/latest/meta-data/public-ipv4 
else
        dig +short myip.opendns.com @resolver1.opendns.com
fi

 このスクリプトはAWS、GCE、Azureなどのクラウドではhostnameが取得できます。残念ながらIDCFクラウドでは動きません。IDCFクラウドのCloudStackの実装の場合は仮想マシンごとにパブリックIPアドレスが付いていません。digコマンドは仮想ルーターのパブリックIPアドレスが返ります。そのためAmbari AgentごとにIPアドレスが一意にならず、Ambari Shellを使いホストグループのアサインで失敗してしまいます。

 IDCFクラウド用にDockerイメージをビルドしようと思いましたが、コンテナを起動した後にdocker execで入り修正することにしました。

$ docker exec -it amb1 bash

 public-hostname.shを編集してhostname -fを返すようにします。

/etc/ambari-agent/conf/public-hostname.sh
#!/bin/bash

STATUS=$(curl -s -m 5 -w "%{http_code}" http://169.254.169.254/latest/meta-data/public-ipv4 -o /dev/null)
if [ "$STATUS" == "200" ]; then
	curl -s -m 5 http://169.254.169.254/latest/meta-data/public-ipv4
else
#	dig +short myip.opendns.com @resolver1.opendns.com
    echo $(hostname -f)
fi

 コンテナを再起動します。

$ docker restart amb1 amb2 amb3

 この感じだとCloudbreakをIDCFクラウドに対応させるのは難しそうな気がしてきました。クラウドで簡単にHadoopクラスタを構築できるので便利なのですが。。

Ambari Shell

 Ambari Shellを起動してBlueprintsを使いZeppelinをセットアップしていきます。

 ambari-functionsamb-shellを使い起動します。

$ amb-shell

 Ambari Shellで利用できるBlueprintの書き方は'slave_'ではじまるホストグループが必要など制約があります。

AmbariClient.groovy
  private static final String SLAVE = 'slave_'

        def slaves = groups.findAll { it.name.toLowerCase().startsWith(SLAVE) }
        if (slaves) {
          int k = 0
          for (int j = i; j < hostSize; j++) {
            result[slaves[k].name] = result[slaves[k].name] ?: []
            result[slaves[k].name] << hostNames[j]
            result << [(slaves[k].name): result[slaves[k++].name]]
            k = k == slaves.size ? 0 : k
          }
        } else {
          throw new InvalidHostGroupHostAssociation("At least one '$SLAVE' is required", groupSize)
        }

 同じBlueprintでもHortonworks GalleryにあるZeppelin Serviceは利用できません。CloudbreakBlueprintのような書き方でないと実行時にエラーになります。

ambari-shell> blueprint add --url https://raw.githubusercontent.com/sequenceiq/cloudbreak/master/core/src/main/resources/defaults/blueprints/datascientist.bp

 Ambariクラスタを構成するためのコンテナを確認します。

ambari-shell>host list
amb1.service.consul [HEALTHY] 192.168.0.4 centos6:x86_64
amb2.service.consul [HEALTHY] 192.168.0.5 centos6:x86_64
amb3.service.consul [HEALTHY] 192.168.0.6 centos6:x86_64

 cluster buildコマンドで表示するHOSTNAMEpublic-hostname.shを使っているようです。

ambari-shell>cluster build --blueprint datascientist
  HOSTNAME             STATE
  -------------------  -------------------
  amb3.service.consul  amb3.service.consul
  amb1.service.consul  amb1.service.consul
  amb2.service.consul  amb2.service.consul

 autoAssignでなく個別にホストをアサインしていきます。

CLUSTER_BUILD:datascientist>cluster assign --hostGroup master_1 --host amb1.service.consul
amb1.service.consul has been added to master_1
CLUSTER_BUILD:datascientist>cluster assign --hostGroup slave_1 --host amb2.service.consul
amb2.service.consul has been added to slave_1
CLUSTER_BUILD:datascientist>cluster assign --hostGroup client --host amb3.service.consul
amb3.service.consul has been added to client

 createしてAmbariクラスタが完成されるのを待ちます。

CLUSTER_BUILD:datascientist>cluster create --exitOnFinish true

consul-templateとNginxのリバースプロキシ

 Ambari on DockerではDockerコンテナのポートはDockerホストにマップしません。AmbariとZeppelinの管理画面にアクセスできるようにconsul-templateを使ってNginxのリバースプロキシを作ります。

 シングルノードの時はローカルでビルドしたイメージを使い、consul-template用のテンプレートもDockerホストからボリュームをマウントしていました。Docker SwarmクラスタになるとローカルでビルドしたイメージやDockerホストのボリュームをマウントできなくなります。各ノードからdocker pullできるようにリポジトリにイメージをおきます。

 Docker Swarmでdocker pullをするとすべてのノードでDockerイメージをダウンロードしてくれます。

$ export DOCKER_HOST=tcp://10.3.0.101:3333
$ docker pull masato/nginx-consul
Using default tag: latest
minion-2: Pulling masato/nginx-consul:latest... : downloaded
minion-1: Pulling masato/nginx-consul:latest... : downloaded
minion-3: Pulling masato/nginx-consul:latest... : downloaded

 IDCFクラウドのパブリックIPアドレスのポートフォワードを特定の仮想マシンに固定したいので、-e constraint:node==minion-2の制約を入れます。

 NginxコンテナはAmbariクラスタ用のConsulコンテナを--dnsフラグの先頭に指定して起動します。

$ AMB_CONSUL=$(docker inspect --format="{{.NetworkSettings.Networks.zeppelin.IPAddress}}" amb-consul)
$ echo $AMB_CONSUL
192.168.0.2
$ docker run -d \
  --name nginx \
  --env constraint:node==minion-2 \
  --env CONSUL_URL=$AMB_CONSUL:8500 \
  --net zeppelin \
  -p 8080:8080 -p 9995:9995 -p 9996:9996 \
  --dns $AMB_CONSUL \
  --dns 172.17.0.1 \
  --dns 8.8.8.8 \
  --dns-search service.consul \
  masato/nginx-consul 

 psでDocker Swarmのコンテナの状態を確認します。

$ docker ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED              STATUS              PORTS                                                                             NAMES
a81a30d32213        masato/nginx-consul           "/bin/start.sh"          About a minute ago   Up About a minute   80/tcp, 10.3.0.102:8080->8080/tcp, 443/tcp, 10.3.0.102:9995-9996->9995-9996/tcp   minion-2/berserk_swartz
b770b58d4b05        sequenceiq/ambari:2.2.0-v1    "/start-agent"           27 minutes ago       Up 25 minutes       8080/tcp                                                                          minion-3/amb3
df84975d8196        sequenceiq/ambari:2.2.0-v1    "/start-agent"           27 minutes ago       Up 25 minutes       8080/tcp                                                                          minion-2/amb2
7443b0b8c6ba        sequenceiq/ambari:2.2.0-v1    "/start-agent"           27 minutes ago       Up 25 minutes       8080/tcp                                                                          minion-1/amb1
52118d892cb1        sequenceiq/ambari:2.2.0-v1    "/start-server"          27 minutes ago       Up 27 minutes       8080/tcp                                                                          minion-2/amb-server
383f3c220e8f        sequenceiq/consul:v0.5.0-v6   "/bin/start -server -"   28 minutes ago       Up 28 minutes       53/tcp, 53/udp, 8300-8302/tcp, 8400/tcp, 8500/tcp, 8301-8302/udp                  minion-3/amb-consul

Zeppelinの設定

 ZeppwlinのHDFSとHiveの設定は以下を参考にして、NoteBookを実行する準備をします。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?