ConoHaでお手軽Hadoop&Spark開発
筆者が所属している次世代システム研究室では、Hadoopを活用したビッグデータシステムを開発しています。以前の案件では、弊社のサービスであるGMOアプリクラウドを使用していましたが、新規案件では新しくなったConoHa を使用し、Hadoopクラスタの構築と周辺のアプリケーション開発を行いました。以下、その設計と料金の具体的な事例を紹介したいと思います。
ConoHaで構築するHadoopクラスタのインスタンス構成と料金
試験などのための開発環境として、図1 に示す構成を設計しました。Hadoopディストリビューションは、Hortonworks社のHortonworks Data Platform(HDP)2.3を使用しています。HDPにあらかじめ用意されているAmbari、YARN、HBase、Hive、SparkなどのHadoopエコシステムをフル活用しています(表1 ) 。
図1 インスタンスの図
表1 HDPに用意されているHadoopエコシステム
Ambari HDPのHadoop管理システム
YARN Hadoopクラスタ上で動作するソフトウェアのスケジュール管理、リソース管理を行う
HBase GoogleのBigTableをモデルとした列指向の分散データベース
Hive Hadoopの上に構築されたデータベース。HiveQLというSQLに似たクエリを使用できる
Spark インメモリで分散処理をするソフトウェア。リアルタイムストリーミング処理が行えるSparkStreaming、SQLが実行できるSpark SQLがある
リージョンは「日本」 、OSはゲートウェイがCentOS 7.1、それ以外はCentOS 6.6を使用します。Hadoopのノード系インスタンスにはメモリ8GBは必要です。インスタンス14台、ロードバランサ1台で、合計67,640円でHadoopクラスタを使用したシステムがConoHaで構築できます(表2 ) 。Hadoopクラスタを使ったサービスを月額7万以下で作れるのは割安だと思います。ConoHaは転送料金など使用量に関する追加料金は発生しないので、この値段が固定の月額料金となります。
表2 Hadoopクラスタを使用したシステムの見積り料金
マシン名 役割 CPU メモリ SSD 月額料金
gateway インターネットから接続できるSSHサーバ 2コア 1GB 50GB 900円
ci_ambari HDPのHadoop管理システムAmbariとデプロイサーバ 4コア 4GB 50GB 3,420円
master_node1~3 Hadoopのマスタノード 6コア 8GB 50GB 6,670円×3
slave_node1~3 Hadoopのスレーブノード 6コア 8GB 550GB 11,170円×3
dev-web1、2 Nginxサーバ 2コア 1GB 50GB 900円×2
dev-ap1、2 HTTPアプリケーションサーバ 3コア 2GB 50GB 1,750円×2
dev-batch1、2 集計バッチサーバ(spark-submit) 3コア 2GB 50GB 1,750円×2
ロードバランサ1台 Webサーバで使用 1,000円
合計 67,640円
システム構築作業の際にインスタンスを作って壊すことも何度かはあるため、プラス数千円が予算を通す際の値段になるでしょう。請求は1時間単位で、明細書に詳細に記載されます。
HadoopクラスタでのSparkの活用
構築したクラスタで、spark-shellのYARNクライアントモードを試してみます(YARNを使ってSparkの処理をクラスタで分散させます) 。レコメンドの元ネタとなるデータとしてWebページのアクセスログを解析し、「 ユーザごとの閲覧Webページの一覧」を算出するためSpark SQLを利用します。
1.ダミーデータの作成(1,000万レコード)
データベーステーブルの元となるアクセスログのダミーデータ(TSV)をPerlで作成します(リスト1 ) 。
リスト1 ダミーデータを作成するPerlスクリプト
#!/usr/bin/perl
for ($i=0; $i < $ARGV[0]; $i++) {
$user_id = "uid_".int(rand ($ARGV[1]));
$page_url = "http://maru/page/".int(rand $ARGV[2]);
print $user_id."\t".$page_url."\n";
}
あるサイトに1万ページのURLがあり、そのサイトにアクセスした100万人のユーザが合計1,000万アクセスしたと仮定し、ログ(TSV)を生成します。
[hive@slave_node1]$./dummy.pl 10000000 1000000 10000 > dummy10m.tsv
生成したログデータは次のようなものです。
uid_1 http://maru/page/1
uid_1 http://maru/page/2
uid_2 http://maru/page/3
uid_3 http://maru/page/4
uid_3 http://maru/page/5
このデータをリスト2のようなデータ形式にSpark SQLで変換してみます。
リスト2 Spark SQLで変換したデータ
uid_1 http://maru/page/1 http://maru/page/2
uid_2 http://maru/page/3
uid_3 http://maru/page/4 http://maru/page/5
2.テーブルにデータ(TSV)を登録
spark-sqlを起動し、データベースとテーブルを作成します。
spark-sql>
create database noda_conoha_sd4;
use noda_conoha_sd4;
CREATE TABLE raw_log_data (
uid STRING,
url STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE;
CREATE TABLE log_data (
uid STRING,
url STRING
)
STORED AS ORC tblproperties ("orc.compress"="SNAPPY");
TSVファイルをTEXTFILEフォーマットテーブルにロードしたあと、処理が最適化されたORCフォーマットテーブルに変換します。
spark-sql>
LOAD DATA LOCAL INPATH "./dummy10m.tsv" into table raw_log_data;
INSERT OVERWRITE TABLE log_data select uid, url from raw_log_data;
select * from log_data limit 10;
uid_387616 http://maru/page/2197
uid_734748 http://maru/page/6714
uid_982660 http://maru/page/6854
3.spark-shellのYARNクライアントモード
spark-shellをYARNクライアントモードで起動し、シェルで実行するSparkの処理をHadoopクラスタで分散させます。
[hive@slave_node1]$ spark-shell --master yarn-client
scala>
Hiveテーブルを扱うため、Hiveのパッケージをインポート
import org.apache.spark.sql.hive.HiveContext
SparkContextでspark-shellを起動すると、scは自動的に作られる
val hiveCtx = new HiveContext(sc)
HiveQLでSQLを実行
val rows = hiveCtx.sql("select uid, url from noda_conoha_sd4.log_data")
重複行を削除
val rdd = rows.distinct.rdd
map形式に変換 (ユーザID => ページURL)
val pairs = rdd.map( u => (u{0}.asInstanceOf[String], u{1}.asInstanceOf[String]))
ユーザIDをキーにデータを集約する (ユーザID => ページ URL1, ページURL2)
val group = pairs.groupByKey()
データを10個取り出す
group.take(10)
結果は次のようになります。
Array[(String, Iterable[String])] =
Array((uid_203708,CompactBuffer(http://maru/page/1909,
http://maru/page/4316),(uid_66952,CompactBuffer(http://maru/page/3593,http://maru/page/3497),
……略……
イメージ保存機能を利用したサーバのチューニング
今回は、ConoHaが持つイメージ保存機能の活用方法も紹介しておきます。ここで取り上げるのは、Webサーバのチューニングの事例です。
Nginxの設定(nginx.conf)を変更してチューニングをしたかったのですが、あいにくほかのメンバーが試験で使用中のため、気軽にWebサーバの設定を変更できない状況でした(設定の変更によってNginxの動作に不具合が生じ、バックエンドの処理で試験データが破損するのを避けたかったからです) 。そこで、ConoHaのイメージ保存機能を使ってWebサーバのインスタンスをコピーし、新たに検証用のWebサーバを構築してチューニングをしました。
図2 は、そのチューニングの手順の全体像を示したものです。以下、順を追って説明します。
図2 チューニング全体のイメージ図
1.イメージを保存する
ConoHaのインスタンスは、起動中はイメージを保存できないため、いったんWEB1を停止してからイメージを保存します。イメージのネームタグは「WEB1_COPY」とします(図3 ) 。
図3 インスタンスのイメージの作成
2.イメージから新規インスタンスを構築する
WEB1を再起動し、試験で使用可能な状態に戻します。次に、メニューの「サーバー追加」からイメージタイプ「保存イメージ」を選択し、ネームタグから「WEB1_COPY」を選択して、新規インスタンスをWEB3として1台構築します(図4 ) 。
図4 イメージを元にインスタンスを構築
3.Nginxをチューニングする
WEB3単体でNginxをチューニングします。httperfなどでHTTPのパフォーマンスを見ながら、nginx.confの最適値を算出します。
[noda@gateway]# httperf --server=WEB3のIP --port=80 --uri=/index.html --rate 5000 --num-conn 250000 --num-call 5 --timeout 5
4.チューニングしたインスタンスをコピーする
WEB3のチューニングが終わったらWEB3を停止します。WEB3の保存イメージを作成し、そのイメージを使用してさらにインスタンスWEB4を1台構築します。ロードバランサ経由でのHTTPアクセスの動作を確認するため、ロードバランサ2を追加します。
ロードバランサ2をWEB3とWEB4に接続し、ロードバランサ経由でのチューニングと動作確認を行います。結果に問題がなければ、夜間などWebインスタンスが使用されていないときに、ロードバランサ1に紐付けているWEB1とWEB2を、それぞれWEB3とWEB4に変更します。インスタンスを残しておくと料金が発生するため、WEB1、WEB2、ロードバランサ2は削除します[1] 。
まとめ
今回は、実際の業務でConoHaを使った事例を紹介しました。月額7万円以下でSparkも使えるHadoopクラスタが構築できるのは魅力です。また、イメージ保存機能を利用したサーバのチューニングについてイメージをつかんでいただけたら幸いです。皆さんもぜひ、業務での開発にConoHaを活かしてください。