AWS SDK for PHP2を使ってPHPスクリプトからEMRのジョブフローを作成&実行する

fluentdの普及により各種ログが見直されている今日この頃ですがそんなログ集計にベンリなAmazonのサービスといえばElastic MapReduceですね。

memorycraft: EMRってなんじゃ?(ログ、ゆりかごから墓場まで

こちらでfluentdからアクセスログをS3に送ってEMRで集計をする方法が解説されていますが、AWSでは各種言語向けにSDKを公開されていますので、折角なのでAWS SDK for PHP2を使用してPHPスクリプトからEMRのジョブフローを作成、実行してみます。

AWS SDK for PHP

実行するジョブ

先の記事に倣ってLTSVのhttpdログを集計するスクリプトをs3に配置してみます。
ログの件数をpath、refごとに集計した結果をs3://bucket-name/httpd/pv_reports/以下に格納するスクリプトです。
このスクリプトは変数${DATE}を受け取ります。

-- create
CREATE EXTERNAL TABLE IF NOT EXISTS logs (dt string, tag string, json string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
LOCATION 's3://bucket-name/httpd/logs/${DATE}';

CREATE EXTERNAL TABLE IF NOT EXISTS intermediate_logs (time string, forwardedfor string, host string, user string, req string, method string, path string, status string, size bigint, referer string,ref_host string, ua string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
LOCATION 's3://bucket-name/httpd/intermediate_logs/${DATE}';

CREATE EXTERNAL TABLE IF NOT EXISTS pv_reports (path string, ref_host string, cnt bigint)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
LOCATION 's3://bucket-name/httpd/pv_reports/${DATE}';

--update
INSERT OVERWRITE TABLE intermediate_logs
select
a.dt, forwardedfor, host, user, req, method, path, status, size, referer,  parse_url(a.referer, 'HOST') as reref_host, ua
from
(SELECT
logs.dt, forwardedfor, host, user,req, method, path, status, size, referer, ua
FROM
logs LATERAL VIEW json_tuple(logs.json, 'forwardedfor', 'host', 'user', 'req', 'method', 'path', 'status', 'size', 'referer', 'ua') j
AS  forwardedfor, host, user,req, method, path, status, size, referer, ua
ORDER BY dt
) a
;

INSERT OVERWRITE TABLE pv_reports
select path, ref_host, count(*) as count from  intermediate_logs where (status like '2%' or status like '3%') group by path, ref_host order by path;

このスクリプトを

s3://bucket-name/create_report.hql

としてs3上に配置します。

これをerasticmapreduceコマンドで実行すると以下のようになります。

elastic-mapreduce --create --name "create pv report 2013-09-01" --num-instances 1 --master-instance-type m1.medium --hive-script --arg s3://bucket-name/create_report.hql --args -d,DATE='2013-09-01'

ジョブフローが作成され、2013-09-01という引数がhiveスクリプトに渡されEMRが実行されます。

このままコマンドラインのスクリプトを動的に生成してshell_execなどでphpから実行することも出来ますが、折角なので同じことをphp-sdkから実行してみましょう。

以下のようになります。

<?php
require __DIR__  . '/aws.phar';
use Aws\Emr\EmrClient;
use Aws\Ec2\Enum\InstanceType;
use Aws\Common\Enum\Region;

$date_ymd = '2013-09-01';
$client = EmrClient::factory([
 		'key'    => _S3_ACCESS_KEY,
		'secret' => _S3_SECRET_KEY,
		'region' => Region::US_EAST_1
]);

$result = $client->runJobFlow([
	'Name' => "pv report $date_ymd",
	'LogUri' => 's3://log-bucket-name/',
	'AmiVersion' => '2.3.6',
	'Instances' => [
		'MasterInstanceType' => InstanceType::M1_MEDIUM,
		'SlaveInstanceType' => InstanceType::M1_MEDIUM,
		'InstanceCount' => 1,
		],
	'Ec2KeyName' => 'key-name',
	'Steps' =>
		[
			0 => [
				'Name' => 'setup script',
				'HadoopJarStep' => [
					'Jar' => 's3://us-east-1.elasticmapreduce/libs/script-runner/script-runner.jar',
					'Args' => [
						's3://us-east-1.elasticmapreduce/libs/hive/hive-script',
		 				'--base-path',
						's3://us-east-1.elasticmapreduce/libs/hive/',
						'--install-hive',
						'--hive-versions',
						'latest'
					],
				],
			],
			1 => [
				'Name' => 'pv report script',
				'HadoopJarStep' => [
					'Jar' => 's3://us-east-1.elasticmapreduce/libs/script-runner/script-runner.jar',
					'Args' => [
						's3://us-east-1.elasticmapreduce/libs/hive/hive-script',
					 	'--base-path',
					 	's3://us-east-1.elasticmapreduce/libs/hive/',
					 	'--hive-versions',
					 	'latest',
					 	'--run-hive-script',
					 	 '--args',
					 	 '-f',
					 	 's3://bucket-name/create_report.hql',
					 	 '-d',
					 	 'DATE=' . $date_ymd
					 ],
				],
			],
	],
]);

・・・えっとだいぶだるい長いですね。
php5.4のshort array syntaxを使ったりしてますが焼け石に水レベルで長いです。
いくつかポイントを書きます。

  • コマンドラインでは–hive-scriptで簡素に指定されてましたがhiveの立ち上げも含めSTEPごとの指定に分かれてます
  • JarとかArgsとかに指定するオプションはコマンドライン指定時には勝手に出来てましたが明示的に指定する必要があります。具体的には実際に実行されたジョブをEMR管理画面のStepsから見るとわかります。
    EMR-PHP-1
  • AmiVersionは明示的に指定する必要があります。コマンドラインスクリプトでは指定していませんが、指定しないとクソ古いAMIで実行されます。
  • Argsはスペース区切りの部分をすべて配列で指定する必要があります。サボって文字列でまとめて渡すと””でエスケープされて動きません。要素の中にスペースがあると自動で””で括られるため、必要ないところではスペースを含めてはいけません。

その他

  • AWS SDK for PHP 2のインストールはpharパッケージだけでなく、composerからもインストール出来ます。
  • psr-0のオートローダーに対応しています。
  • httpリクエストにguzzleを使ってます。
  • ようするとなんつーかAWS SDK for PHP 2はなかなかモダンな作りになってます。

リクエストの戻り値はオブジェクトでかえってくるのですがそのままechoしてみてもちゃんと整形された文字列が出力されます。toStringがちゃんと実装されているのでしょう。

オプションが複雑ですが、そのあたりはドキュメントと、テストコードを見てみると良いかと思います。あとリージョンの指定などは生で書いてもいいですがなるべくクラス定数を使った方がメンテしやすいと思われます。

まとめ

オプションめんどいけどSDK使えるし中身は結構モダンだよ!


Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>