はじめに
Cloudera は以前から、Hadoop の機能を簡単に試すための VM イメージを配布しています(Cloudera QuickStart VM のダウンロードページ)。配布されているイメージは KVM 版、Virtual Box 版、VMWare 版の3種類です。
しかし、Vagrant で使える box ファイル版は提供されておらず、コマンド一発での環境構築はできませんでした。もちろん、Virtual Box 版のイメージから box ファイルを作ることは可能ですが、Cloudera のバージョンアップに追従する作業は面倒でした。
しかし、去年の12月から、Cloudera 社が QuickStart VM の Docker イメージ版を公式に配布するようになりました。以下は、公式配布に関する Cloudera 公式のブログ記事です。
最近、Spark MLlib を勉強するための環境を作る機会があったので、せっかくなので Cloudera QuickStart Docker Image で環境構築してみました。その際に、普通に進めるとうまくいかないポイントがいくつかあったので、そのときの構築手順をまとめておきます。
動作環境
- MacBook Pro (Retina, 15-inch, Mid 2014)
- 16GB onboard memory
- OS X Yosemite version 10.10.5
構築される環境
今回の手順を実行すると、最終的に以下のような環境が構築されます。
- VirtualBox 上で、Docker 用の Linux VM(名前:default)が動作する。
- この Linux VM は CPU 4コア、メモリ 9GB を使用(デフォルトは CPU 1コア、メモリ 2GB)
- 上記の Linux VM 上で、Cloudera Quickstart Docker Image から作られたコンテナが動作する。
- このコンテナはメモリ 8GB を使用
- Docker コンテナ内のシェルから、spark-shell を実行できる。
Cloudera Express は 8GB 以上のメモリを必要とします(参考:Cloudera QuickStart VM)。そのため、構築手順のなかで、Linux VM のメモリをデフォルトより増やす作業を行います。
構築手順
Docker Toolboxのインストール
OS X への Docker のインストール方法は、Installation on Mac OS X が詳しいです。OS X 上で Linux の Docker イメージを直接動かすことはできないため、VirtualBox で Linux VM(名前は default)を動作させ、この Linux VM 上で Docker daemon を動作させます。
(※ Installation on Mac OS X より抜粋)
まず、Docker Toolbox からインストーラをダウンロードして、Docker Toolbox をインストールします。現時点の最新版は DockerToolbox-1.11.1b.pkg です。
インストール後に以下のコマンドを実行すると、Docker のバージョンを確認できます。この時点では、まだ default VM が動いていないので、Docker daemon に接続できないというメッセージが出ます。
% docker version Client: Version: 1.11.1 API version: 1.23 Go version: go1.5.4 Git commit: 5604cbe Built: Tue Apr 26 23:44:17 2016 OS/Arch: darwin/amd64 Cannot connect to the Docker daemon. Is the docker daemon running on this host?
default VM の初期設定
OS X の Launchpad から Docker Quickstart Terminal を実行します。しばらく待って、Docker のアイコンが出てくれば設定完了です。この例では、Docker ホストの IP アドレスは 192.168.99.100 になりました。
Last login: Sat May 21 00:24:50 on ttys004 bash --login '/Applications/Docker/Docker Quickstart Terminal.app/Contents/Resources/Scripts/start.sh' % bash --login '/Applications/Docker/Docker Quickstart Terminal.app/Contents/Resources/Scripts/start.sh' Running pre-create checks... Creating machine... (default) Copying /Users/myoshiz/.docker/machine/cache/boot2docker.iso to /Users/myoshiz/.docker/machine/machines/default/boot2docker.iso... (default) Creating VirtualBox VM... (default) Creating SSH key... (default) Starting the VM... (default) Check network to re-create if needed... (default) Waiting for an IP... Waiting for machine to be running, this may take a few minutes... Detecting operating system of created instance... Waiting for SSH to be available... Detecting the provisioner... Provisioning with boot2docker... Copying certs to the local machine directory... Copying certs to the remote machine... Setting Docker configuration on the remote daemon... Checking connection to Docker... Docker is up and running! To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: /usr/local/bin/docker-machine env default ## . ## ## ## == ## ## ## ## ## === /"""""""""""""""""\___/ === ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~ \______ o __/ \ \ __/ \____\_______/ docker is configured to use the default machine with IP 192.168.99.100 For help getting started, check out the docs at https://docs.docker.com
ちなみに、この表示が出た後で VirtualBox Manager を開くと、default という名前の VM が「実行中」になっていることが確認できます。
default VM のCPUコア、メモリ使用量の変更
Docker コンテナが 8GB のメモリを使えるようにするには、Docker が動作する default VM のメモリ使用量を 8GB 以上にする必要があります。メモリを増やす方法は、以下のページを参考にしました。
具体的な手順は以下の通りです。
docker-machine stop default
を実行し、default VM を停止- VirtualBox Manager を起動し、"default" という名前の VM の「設定」欄を開く
- 設定の「システム」タブで、「マザーボード」を選択し、メインメモリーを 2048MB から 9216MB(9GB)に変更
- 同じく、設定の「システム」タブで、「プロセッサー」を選択し、プロセッサー数を 1 から 4 に変更
- 「OK」ボタンを押して、設定を閉じる
docker-machine start default
を実行し、default VM を再起動
docker info
コマンドを実行すると、メモリとCPUコア数が増えていることが確認できます。
% docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 1.11.1 Storage Driver: aufs Root Dir: /mnt/sda1/var/lib/docker/aufs Backing Filesystem: extfs Dirs: 0 Dirperm1 Supported: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge null host Kernel Version: 4.4.8-boot2docker Operating System: Boot2Docker 1.11.1 (TCL 7.0); HEAD : 7954f54 - Wed Apr 27 16:36:45 UTC 2016 OSType: linux Architecture: x86_64 CPUs: 4 Total Memory: 8.762 GiB Name: default ID: 5L66:CKKI:BIK7:BRUT:H6NI:XMFL:AR36:R6UQ:YOFG:OQPM:M5L6:PU6L Docker Root Dir: /mnt/sda1/var/lib/docker Debug mode (client): false Debug mode (server): true File Descriptors: 13 Goroutines: 32 System Time: 2016-05-24T13:18:09.388371356Z EventsListeners: 0 Registry: https://index.docker.io/v1/ Labels: provider=virtualbox
Cloudera QuickStart Docker Image のダウンロード
Cloudera QuickStart Docker Image は Docker Hub で公開されています(Docker Hub の cloudera/quickstart ページ)。そのため docker pull
コマンドでイメージをダウンロードできます。ダウンロードサイズが 4.4GB あるので、結構時間がかかりました。
% docker pull cloudera/quickstart:latest latest: Pulling from cloudera/quickstart 1d00652ce734: Pull complete Digest: sha256:f91bee4cdfa2c92ea3652929a22f729d4d13fc838b00f120e630f91c941acb63 Status: Downloaded newer image for cloudera/quickstart:latest % docker images REPOSITORY TAG IMAGE ID CREATED SIZE cloudera/quickstart latest 4239cd2958c6 6 weeks ago 6.336 GB
コンテナの起動
以下のコマンドを実行して、コンテナを起動します。
docker run --hostname=quickstart.cloudera \ --privileged=true -t -i -d -p 18888:8888 -p 17180:7180 -p 10080:80 \ -m 8192m cloudera/quickstart:latest /usr/bin/docker-quickstart
このコマンドは Docker Hub の cloudera/quickstart ページ にあるものをベースに、以下の修正を加えたものです。
- コンテナをデーモンとして起動(あとから docker exec で接続)
-m 8192m
を指定して、メモリ使用量を 8GB に指定- マッピングされるポート番号が起動のたびに変わるのを避けるために、マッピング先を、元のポート番号に10000足した値に固定
コンテナの起動後は、docker exec コマンドで、コンテナ上のシェルに接続します。以下の例では、コンテナ ID の一部を指定して接続しています。
% docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 048c433261a7 cloudera/quickstart:latest "/usr/bin/docker-quic" 23 seconds ago Up 23 seconds 0.0.0.0:10080->80/tcp, 0.0.0.0:17180->7180/tcp, 0.0.0.0:18888->8888/tcp gigantic_poitras % docker exec -it 048c bash
接続後に free コマンドでメモリ使用量を見ると、8GB 以上のメモリを使っているように見えます。
[root@quickstart /]# free total used free shared buffers cached Mem: 9187728 5623316 3564412 169020 34224 1127784 -/+ buffers/cache: 4461308 4726420 Swap: 3227556 0 3227556
ただ、cgroup の設定を確認すると、1024*1024*1024*8 = 8589934592
で 8GB になっていたので、制限は効いているのではないかと思います。このあたりはちょっと自信がないです。
[root@quickstart /]# cat /sys/fs/cgroup/memory/memory.limit_in_bytes 8589934592
Cloudera Express の起動
コンテナ内で以下のコマンドを実行し、Cloudera Express を起動します。このコマンドは、コンテナの再起動後にも実行する必要があります。
[root@quickstart /]# /home/cloudera/cloudera-manager --express --force [QuickStart] Shutting down CDH services via init scripts... kafka-server: unrecognized service JMX enabled by default Using config: /etc/zookeeper/conf/zoo.cfg [QuickStart] Disabling CDH services on boot... error reading information on service kafka-server: No such file or directory [QuickStart] Starting Cloudera Manager server... [QuickStart] Waiting for Cloudera Manager API... [QuickStart] Starting Cloudera Manager agent... [QuickStart] Configuring deployment... Submitted jobs: 15 [QuickStart] Deploying client configuration... Submitted jobs: 16 [QuickStart] Starting Cloudera Management Service... Submitted jobs: 24 [QuickStart] Enabling Cloudera Manager daemons on boot... ________________________________________________________________________________ Success! You can now log into Cloudera Manager from the QuickStart VM's browser: http://quickstart.cloudera:7180 Username: cloudera Password: cloudera
出てくるメッセージは QuickStart VM の場合と全く一緒ですね……。
Web UI の接続確認
ここまで来れば、ホスト OS(OS X)の Web ブラウザから、Cloudera の Web UI の動作を確認できます。
-
- Tutorial
-
- Cloudera Manager(cloudera / cloudera でログイン)
また、この時点ではアクセスできませんが、Cloudera Manager から Hue を起動した後に、以下のアドレスで Hue にアクセスできます。
- http://192.168.99.100:18888/
- Hue(cloudera / cloudera でログイン)
Cloudera Manager からサービス起動
http://192.168.99.100:17180/ にアクセスし、以下の手順で、Spark Shell の動作に必要なサービスを起動します。
- Cloudera Manager Service の起動を待つ
- クロックオフセットの設定を修正する(後述)
- 他のサービスを、以下の順に起動する
- HDFS
- Hive
- YARN
- Spark
サービスの起動までに時間が掛かります。起動した直後は Bad Health のことがありますが、その場合はしばらく待てば起動します。
また、他の人の環境でも起こる問題かはわからないのですが、私の環境では Host のステータスが以下のようになる問題が発生しました。
致命的なヘルスの問題:クロックのオフセット
(英語では Bad health: Clock Offset)
この問題は、同じ OS X マシンで Cloudera QuickStart VM を使ったときにも発生したのですが、そのときと同じ対処方法が有効でした。以下の手順に従って、オフセットの設定を無効にすれば、ステータスが Good に戻って、問題なく動作するようになります。
- Cloudera Managerのメニューから、 Host(ホスト) → Configuration(設定) と遷移する
- 検索キーワード欄を使って、Host Clock Offset Thresholds(ホストクロックオフセットのしきい値) を表示する
- この設定の「Warning(警告)」と「Critical(致命的)」を、両方とも「Never(行わない)」に変更する
- 「Save Changes(変更の保存)」ボタンを押す
Spark Shell の起動
Spark Shell を初めて起動する前に、以下のコマンドを実行してください。
[root@quickstart /]# sudo -u hdfs hadoop fs -chmod 777 /user/spark [root@quickstart /]# sudo -u spark hadoop fs -chmod 777 /user/spark/applicationHistory
上記のコマンドを実行しなかった場合、Spark Shell の起動中に以下のエラーが表示されてしまいます。どうも、Cloudera QuickStart の不備のようです(参考:Solved: [CDH 5.5 VirtualBox] unable to connect to Spark Ma... - Cloudera Community)。
16/05/29 06:37:15 ERROR spark.SparkContext: Error initializing SparkContext. org.apache.hadoop.security.AccessControlException: Permission denied: user=root, access=WRITE, inode="/user/spark/applicationHistory":spark:supergroup:drwxr-xr-x
そして、以下のコマンドを実行すると Spark Shell が起動します。Cloudera Quickstart VM は擬似分散モードで動作しているので、引数には --master yarn-client
を指定する必要があります。
[root@quickstart /]# spark-shell --master yarn-client Setting default log level to "WARN". To adjust logging level use sc.setLogLevel(newLevel). Welcome to ____ __ / __/__ ___ _____/ /__ _\ \/ _ \/ _ `/ __/ '_/ /___/ .__/\_,_/_/ /_/\_\ version 1.6.0 /_/ Using Scala version 2.10.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_67) Type in expressions to have them evaluated. Type :help for more information. Spark context available as sc (master = yarn-client, app id = application_1464516865260_0001). SQL context available as sqlContext. scala>
以上で、Spark MLlib を試すための環境構築は完了です。
Spark MLlib を試す
Apache Spark入門 動かして学ぶ最新並列分散処理フレームワーク (NEXT ONE)
- 作者: 株式会社NTTデータ,猿田浩輔,土橋昌,吉田耕陽,佐々木徹,都築正宜,下垣徹
- 出版社/メーカー: 翔泳社
- 発売日: 2015/10/29
- メディア: 大型本
- この商品を含むブログ (2件) を見る
構築に成功したか確認するために、Apache Spark 入門の8章に掲載されていたコードを Spark Shell で実行してみました。内容は、K-means を用いたクラスタリングです。
HDFS へのサンプルデータのアップロード
Cloudera QuickStart には Spark MLlib のサンプルデータが含まれていませんでした。そのため、まずはテストに使うサンプルデータをダウンロードして、HDFS にアップロードします。
[root@quickstart /]# curl https://raw.githubusercontent.com/apache/spark/master/data/mllib/kmeans_data.txt > kmeans_data.txt % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 72 0 72 0 0 40 0 --:--:-- 0:00:01 --:--:-- 47 [root@quickstart /]# cat kmeans_data.txt 0.0 0.0 0.0 0.1 0.1 0.1 0.2 0.2 0.2 9.0 9.0 9.0 9.1 9.1 9.1 9.2 9.2 9.2 [root@quickstart /]# hadoop fs -put kmeans_data.txt /tmp [root@quickstart /]# hadoop fs -ls /tmp Found 5 items drwxrwxrwx - hdfs supergroup 0 2016-05-29 13:02 /tmp/.cloudera_health_monitoring_canary_files drwxrwxrwt - mapred mapred 0 2016-04-06 02:26 /tmp/hadoop-yarn drwx-wx-wx - hive supergroup 0 2016-05-29 09:57 /tmp/hive -rw-r--r-- 1 root supergroup 72 2016-05-29 13:02 /tmp/kmeans_data.txt drwxrwxrwt - mapred hadoop 0 2016-05-29 10:19 /tmp/logs
サンプルデータの RDD への変換
Spark Shell を起動して、HDFS からサンプルデータを読み込んで、RDD に変換します。
[root@quickstart /]# spark-shell --master yarn-client (中略) scala> import org.apache.spark.mllib.clustering.KMeans import org.apache.spark.mllib.clustering.KMeans scala> import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.linalg.Vectors scala> val data = sc.textFile("hdfs://localhost/tmp/kmeans_data.txt") data: org.apache.spark.rdd.RDD[String] = hdfs://localhost/tmp/kmeans_data.txt MapPartitionsRDD[1] at textFile at <console>:29 scala> val parsedData = data.map{ s => | Vectors.dense( | s.split(' ').map(_.toDouble)) | }.cache() parsedData: org.apache.spark.rdd.RDD[org.apache.spark.mllib.linalg.Vector] = MapPartitionsRDD[2] at map at <console>:31
KMeans のモデルの作成
これ以降は、本に載っているコードをそのまま実行できました。KMeans.train メソッドでモデルを生成します。
scala> val numClusters = 2 numClusters: Int = 2 scala> val numIterations = 20 numIterations: Int = 20 scala> val clusters = KMeans.train(parsedData, numClusters, numIterations) 16/05/29 13:07:00 WARN netlib.BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS 16/05/29 13:07:00 WARN netlib.BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeRefBLAS clusters: org.apache.spark.mllib.clustering.KMeansModel = org.apache.spark.mllib.clustering.KMeansModel@5d27cdd2
上記の WARN は、native library のロードに失敗したことを表しているようです(参考:Using Spark MLlib)。Hardware acceleration が効かないだけなので、この WARN は無視します。
生成されたモデルの確認
生成されたクラスタの数、および各クラスタの中心の座標を確認します。
scala> clusters.k res0: Int = 2 scala> clusters.clusterCenters res1: Array[org.apache.spark.mllib.linalg.Vector] = Array([0.1,0.1,0.1], [9.099999999999998,9.099999999999998,9.099999999999998])
新たなベクトルを与えて、いずれのクラスタに分類されるか確認します。
scala> val vec1 = Vectors.dense(0.3, 0.3, 0.3) vec1: org.apache.spark.mllib.linalg.Vector = [0.3,0.3,0.3] scala> clusters.predict(vec1) res2: Int = 0 scala> val vec2 = Vectors.dense(8.0, 8.0, 8.0) vec2: org.apache.spark.mllib.linalg.Vector = [8.0,8.0,8.0] scala> clusters.predict(vec2) res3: Int = 1
元のファイルに含まれていた座標が、いずれのクラスタに分類されるかを判定します。判定結果は HDFS に出力します。
scala> val predictedLabels = parsedData.map(vec => clusters.predict(vec)) predictedLabels: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[32] at map at <console>:39 scala> predictedLabels.saveAsTextFile("/tmp/output") scala> exit [root@quickstart mllib]# hadoop fs -ls /tmp/output Found 3 items -rw-r--r-- 1 root supergroup 0 2016-05-29 07:12 /tmp/output/_SUCCESS -rw-r--r-- 1 root supergroup 8 2016-05-29 07:12 /tmp/output/part-00000 -rw-r--r-- 1 root supergroup 4 2016-05-29 07:12 /tmp/output/part-00001 [root@quickstart mllib]# hadoop fs -cat /tmp/output/part-* 1 1 1 0 0 0
まとめ
一通りの作業を試してみて、Cloudera QuickStart Docker Image で Spark MLlib のお試し環境を構築できることがわかりました。
使ってみた感想としては、コマンドラインから QuickStart のコンテナを起動・停止できるのは便利でした。その一方で、この QuickStart Docker Image は、Cloudera QuickStart VM を単純に Docker image にしただけ、という感じで、Web ブラウザから行わなければいけない設定やサービス起動は相変わらず多かったです。そのため、この Docker image をベースに環境構築を自動化するのは大変そうです。
Spark を CDH と一緒に使いたいという特別な理由がなければ、もっと軽量な Docker イメージを探したほうがよいかもしれませんね……。