クラウドコンピューティングにまつわる話をつれづれなるままに。。。

2009年6月24日水曜日

Amazon EC2事始め

実際に動かしてみないとよくわからないので、遅ればせながらAmazon EC2をさわってみた。

まずは、何はなくともAWS (Amazon Web Services)のアカウントを作る。そして、X.509証明書も作っておく。

EC2を簡便に利用するためにElasticfoxというFireboxプラグインが存在することは知っていたけど、このためだけにFirefoxを使うのもなぁと思っていたら、最近はAmazon Management Consoleというサービスがあるようだ。両者の違いはわからないけど、AMI(Amazon Machine Image)の検索から、インスタンスのライフタイム管理、Elastic IPなどの設定など、必要そうな機能はそろってそうだ。

Amazon Management Consoleにサインインすると、ダッシュボードが表示されるので、さっそくAMIを探す。コミュニティで作られたAMIは2630個もあってとても選んでられないので、とりあえずQuick StartにあるFedora 8を選択し、インスタンスを起動する。すると、まずAMIにSSHログインするために必要なキーペアを作ることになる。ここでプライベートキーがダウンロードされるんだけど、なぜか下のスクリーンショットのように、「Loading, please wait...」のダイアログが消えずに残ったままになっている。ぜんぜん終わらないなと、しばらく待惚けしてしまった。



あとはファイアウォールの設定をするとインスタンスが起動する。Consoleのステータスを見る限り、インスタンスの起動に50秒、終了に30秒ぐらいかかっているようだった。

さて、さきほどダウンロードしたキーペアを利用して、sshログインしてみる。

$ chmod 400 qstest.pem
$ ssh -i qstest.pem root@ec2-174-129-178-172.compute-1.amazonaws.com

__| __|_ ) Fedora 8
_| ( / 32-bit
___|\___|___|

Welcome to an EC2 Public Image
:-)

Getting Started

--[ see /etc/ec2/release-notes ]--

[root@domU-12-31-39-00-54-94 ~]# uname -a
Linux domU-12-31-39-00-54-94 2.6.21.7-2.fc8xen #1 SMP Fri Feb 15 12:39:36 EST 2008 i686 athlon i386 GNU/Linux


インスタンスはスモール(1時間0.10 USD)を選択したが、CPUは低消費電力版AMD Opteron 2.6GHz(と/proc/cpuinfoでは見えるが、Xenによって1.0から1.2 GHz相当に制限がかかっているかもしれない。コアあたり2個ぐらいインスタンスがわりあてられるとか)、メモリは1.7GBになる。日本とのRTTは200ミリ秒程度。

[root@domU-12-31-39-00-54-94 ~]# cat /proc/cpuinfo
processor : 0
vendor_id : AuthenticAMD
cpu family : 15
model : 65
model name : Dual-Core AMD Opteron(tm) Processor 2218 HE
stepping : 3
cpu MHz : 2599.998
cache size : 1024 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 1
wp : yes
flags : fpu tsc msr pae mce cx8 apic mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow up pni cx16 lahf_lm cmp_legacy svm extapic cr8legacy ts fid vid ttp tm stc
bogomips : 6502.82
clflush size : 64

[root@domU-12-31-39-00-54-94 ~]# cat /proc/meminfo
MemTotal: 1747764 kB
MemFree: 1446716 kB
Buffers: 217076 kB
Cached: 33748 kB
SwapCached: 0 kB
Active: 23096 kB
Inactive: 234136 kB
HighTotal: 1003528 kB
HighFree: 953652 kB
LowTotal: 744236 kB
LowFree: 493064 kB
SwapTotal: 917496 kB
SwapFree: 917496 kB
Dirty: 40 kB
Writeback: 0 kB
AnonPages: 6416 kB
Mapped: 5616 kB
Slab: 8912 kB
SReclaimable: 4568 kB
SUnreclaim: 4344 kB
PageTables: 960 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
CommitLimit: 1791376 kB
Committed_AS: 42364 kB
VmallocTotal: 114680 kB
VmallocUsed: 1360 kB
VmallocChunk: 112936 kB


さて、sshログインに必要な設定(/root/.ssh以下のパブリックキーおよびauthorized_keys)は、/etc/rc.localから呼ばれるシェルスクリプト経由で実行される。あと、ec2-ami-tools RPMもインストールされる。この辺は自分でAMIを作るとしたら押さえておく必要があるな。

(追記)ちょっと勘違いしていた。シェルスクリプト(/usr/local/sbin/get-credentials.sh)は、sshでログインできるように、最初に作ったキーペアのパブリックキーをcurlでダウンロードしてきて、authorized_keysに書き込むという処理を行う。サーバ側はIP要求者のIPアドレスでも見て、どのキーペアを利用しているかをチェックしているのだろうか?
このあたりの挙動は「Amazon EC2の機能を詳しく見てみる(3)--インスタンスデータとAMI」に詳しい。RESTインタフェースによってインスタンスのデータを取得できるとのこと。

2009年6月11日木曜日

Java NIO FileChannel

Hadoopコードリーディング大会その1。

HDFSのDataNodeに対してブロックリードのリクエストを出すと、デフォルトではNIOのチャネルが使われる(もう一つは昔からあるストリームIOを使う実装)。このチャネルのtransferToメソッドはUNIXのsendfileシステムコールみたいなものだ。(裏は取ってないが)実際、内部はsendfileを使って実装されているという記述も見かけた。sendfileはファイルをユーザプロセス空間にコピーすることなく、カーネル内で直接ソケットに書き込んでしまう仕組み。(少なくとも現状では)ファイルからソケットへの書き込みにしか対応していないので、transferFromメソッドはsendfileを使っては実装できない。おそらく、mmapしてからwriteしているのだろう。

今までNIOを使ったことがなかったので、FileChannel.transferTo、transferFromを使ったファイル転送ベンチマークを書いてみた。ソースはここのそれらしいファイル。

1GBファイルを転送したときのスループットが40 MB/sぐらい。ちなみにHDDはMaxtor 7L250S0で、hdparm -tで測ったread性能が63 MB/s、ddで1 GBファイル作ったときが68 MB/s。

この機会にJavaのソケットAPIを少し調べてみたが、Java 1.4になってから、CのソケットAPIでできることは大抵できるようになった気がする。

2009年6月4日木曜日

Jettyのバルクデータ転送性能

Hadoopで内部的にも使われているHTTPサーバJettyの性能を測ってみた。HDFSのブロックサイズは64MBだったと思うので、まずは64MBのファイルをwgetで取得した際のスループットを測った(ちなみにGFSのチャンクのことをHDFSではブロックと呼ぶ)。比較対象としてC言語で実装されたApacheとlighttpdでも同様の実験を行った。なお、ネットワークはGbEで、Iperfで941 Mbps出ることを確認している。Jettyのバージョンは6.1(Hadoop0.19.1に同梱されていのは5.1.4)、ApacheはCentOS標準の2.2.3、lighttpdは1.4.22。

その結果、共に896Mbpsと遜色ない値を示した。

さらに(現実的なシナリオとは言えないけど)ファイルサイズを1GBにしてみた。今回のサーバは1GBのメモリしか積んでないので、ファイル全体がメモリに載らず、性能はディスクで律速するはずである。結果はややばらついたので、それぞれ5回実行した結果を下表に示す。単位はMbps。







12345
Jetty 6.1380.8416.0395.2380.0404.8
Apache 2.2.3262.4264.8263.2256.0252.0
lighttpd 1.4.22251.2244.0252.8248.8250.4


意外なことにJettyが勝っている。いったい何が影響しているのだろうか?特に設定ファイルはいじっていない。ディスクキャッシュの影響かなぁ。よくわからない。

実験をやった後に知ったのだけど、ブロックの転送にHTTPは使われてなさそうで、JettyはJobTrackerのモニタリング機能のために使われているようだ。DataNodeとHDFSクライアント間の通信はTCP/IP通信で、データは64KBのパケットに分割されてから送信される。パケットは512バイトごとにチェックサムが計算される。そもそも、(絶対条件ではないが基本的に)データが存在するノードでタスクが実行されるので、リモートノードと通信は起きないはずだ。

参考までに両者の輻輳ウィンドウの挙動を、TCP Probeモジュールを使って確認してみた。通信開始時は両者に違いがないように見えるが、観測したスループットはこの時点ですでに差がついているんだよな。Linuxカーネルのバージョンは2.6.18で、TCPはBIC(ssthreshの初期値は100)を使っている。TCP関連のパラメータはOSでフォルト設定から特にいじってない。


余談。JettyのコンパイルにはMavenが必要で、デフォルト設定だとビルド時にヒープが足りなくなるので、環境変数MAVEN_OPTSに-Xmx128mなどと指定する必要があった。

2009年6月2日火曜日

Hadoop 0.19.1インストール

今、Hadoopのページを見ると、0.18.3、0.19.1、0.20.0の三つのリリースが存在する。最新版を追っかける気もしないので、0.19.1あたりを試すことにする。Release notesなどを眺めると0.19.0でappendに対応したけど、0.19.1で無効に戻したとかある。データが消えてしまう深刻なバグがあるので0.19.0は使ってはいけないようだ。あと、0.20になるとconfiguration file (hadoop-site.xmlとか)が複数ファイルに分割されたりと変更されている。

さて、早速インストール。実験環境は8ノードのPCクラスタ(ホスト名はnode00-07)で、ホームディレクトリはNFSで共有され、SSHはホストベース認証が設定済み。JDK 1.6.0_13。環境変数JAVA_HOMEは別途.zshenvで設定済みなので、conf/hadoop-env.shはいじらない。

以下は、クラスタ環境での設定メモ。

設定ファイルはconfディレクトリ以下に存在し、サイトごとの設定はhadoop-site.xmlで行う。


conf/hadoop-site.xml:
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://node00:54310</value>
</property>
<property>
<name>mapred.job.tracker</name>
<value>node00:54311</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>


マスターノードとスレーブノードはそれぞれ以下のファイルにホスト名を列挙しておく。


conf/masters:
node00

conf/slaves:
node00
node01
node02
node03
node04
node05
node06
node07


HDFSのフォーマット


$ bin/hadoop namenode -format


Hadoopの起動


$ bin/start-all.sh


念のため、jpsコマンドでマスターノードにJobTrackerとNameNode(とSecondaryNameNode)、スレーブノードにTaskTrackerとDataNodeが起動していることを確認する。

Hadoopの終了


$ bin/stop-all.sh


MapReduceの実行

Quick Startの例を実行してみる。まずはconfディレクトリの内容をHDFSにコピーする。


$ bin/hadoop fs -put conf input


サンプルのgrepを実行。HDFS上のinputからファイルを読み込み、結果をoutputに出力する。


$ bin/hadoop jar hadoop-*-examples.jar grep input output 'dfs[a-z.]+'


出力をローカルファイルに出力する。


$ bin/hadoop fs -get output output


蛇足だけど、httpdにはJettyを使っている。