プログラマでありたい

おっさんになっても、プログラマでありつづけたい

AWS Lambda Meetup #0に参加した感想

 先日開催されたAWS Lambda Meetup #0に参加してきました。もともと聞きに行く予定だったのですが、主催者の@Keisuke69さんから打診を受けて急遽登壇することになりました。

発表資料



 発表内容は、Lambda Advent Calendar用にネタ的に作ったクローラー/スクレイピングを題材にしています。

Lambdaクローラーの背景



 最初にLambdaのサービスの発表を受けて、SIer脳の私はバッチサーバの代替として使いたいと考えました。なぜならばバッチサーバの運用は、思いのほか面倒くさいのです。レガシーな作りになりやすく、フェイルオーバーなど自動復旧の仕組みが取りづらいためです。そんなバッチサーバを、そもそもサーバレスのLambdaに置き換えると、運用が楽になってハッピーです。
 と言うことでLambdaに注目し、Advent Calendarなどを見ながら情報収集をしています。今のところ、旧来のバッチサーバをそのまま置き換えるのは難しいけど、幾つかの部分は吸収できるなと感じています。それよりも、バッチの考え方自体を変えた方が良いかもと考えています。そんなこともあり、Lambda使ったアプリを一つ作ってみようというのが、Lambdaクローラーを試してみた理由です。

SNSとSQS



 今回の登壇者が口々に言っていたのが、LambdaとSNSの連携をしてくれという要望でした。私もその通りだと思います。一方で、現状のLambdaの仕様だと実行結果が受け取れません。SNSとLambdaが連携しても、果たしてその処理が正常に完了したのかが解らない(そもそも受け取れたのか)という問題があります。
 その点を考えると、現在のLambdaの在り方と全く逆になりますが、SQSをポーリングするような連携機能があれば嬉しいですね。それだと、処理の終了時にキューを消す処理を追加するだけで、バッチ的な使い方がしやすくなります。旧時代的な発想かもしれませんが、使い所は多いのではと思っています。

 

感想



 Lambda Meetupの初回でしたが、発表者も参加者もどちらもレベルが高くかなり刺激を受けました。正直、今回の発表はちょっと触ってみました以上のものではなく、他の登壇者とのレベルの差が大きく申し訳なかったなと思います。一方で面白かった点としては、発表者それぞれが自分のユースケースを元にLambdaを捉えている点です。それだけ、懐が深いサービスなんだと思います。自分としては、SIer的な発想は抜け切れないと思うので、その点を突き詰めてLambdaの利用の仕方を考えていこうと思います。
 またLambdaと直接関係ないのですが、今回の発表の中ではアンダースコア株式会社の諏訪悠紀さんの「Lambda × Mobile の可能性」が一番気になりました。ちょうどモバイルサービスを提供するインフラ構成について考えていたところで、Cognitoの出現でモバイルから直接AWSを使うという手が良さそうだなと思っていました。それを実際に試みている話を聞けて、いろいろ参考になりました。これについては、改めてまとめて見ようと思います。

See Also:
AWS Lambdaで作るクローラー/スクレイピング
Lambdaで作るクローラー/スクレイピング

参照:
Lambda Meetup #0に参加してみて #LambdaMeetup
AWS Lambda Meetup #0 で Lambda × Mobile の話をしてきた | Developers.IO
AWS Lambda Meetup #0

Lambdaで作るクローラー/スクレイピング

 話題のAWS Lambda Advent Calendar 2014の14日目です。クロスポストで、クローラー/スクレイピング Advent Calendar 2014の14日目でもあります。

 re:Inventで発表されて以来、注目のLambdaです。サーバを用意しなくても、バッチを直接実行できるとあって、ユースケースを考えるだけで夢が広がります。今年はクローラー本を出したこともあって、Lambdaで作るクローラー/スクレイピングをテーマにします。

クローラー/スクレイピングとは?



 Webクローラーは、Webサイトを巡回してデータを取得するプログラムです。スクレイピングは、取得したデータから目的の情報を抜き出すことを指します。一般的には、クローラーの中に、スクレイピングの機能を包含していることが多いです。また、特定のページだけ取得してデータを抜き出すことを、スクレイピングと呼ぶことが多いです。

クローラーの機能



 クローラーの機能は、ざっくり分類すると下記の4つです。
今回は、2と3の部分をLambdaで実施します。

1. 巡回先を決定する
2. ダウンロード
3. 情報を抜き出す
4. (データを保存する)

Lambdaクローラーのモデル



 Lambdaクローラーのモデルとしては、次のような形になります。
f:id:dkfj:20141214045906p:plain

 LambdaCrowlerで、指定されたURLをNode.jsのhttpモジュールを使ってダウンロードします。ダウンロードをしたファイルをS3に保存します。保存先のS3にEventの設定をしておき、parseHtmlのLambdaファンクションを呼び出すようにします。呼び出されたparseHtmlは、イベント情報から呼び元のs3ファイルを取得します。ファイル取得後に、cheerioというスクレイピング用のモジュールを利用して所定の情報を抜き出します。今回は、Yahoo!Financeから株価情報を取得する例とします。

Lambdaクローラーの実装



 まずLambdaCrowlerの実装です。諸般の事情により、取得先のURLと保存するファイル名はリテラルで記述しています。

console.log('Loading event');
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var http = require ('http');

exports.handler = function(event, context) {
    var bucket = 'lambda-crawler';
    var key = 'test';
    var body;
    http.get("http://stocks.finance.yahoo.co.jp/stocks/history/?code=9984.T", function(res) {
        console.log("Got response: " + res.statusCode);
        res.on("data", function(chunk) {
            console.log('chunk: ');
            body += chunk;
        });

        res.on('end', function(res) {
            console.log('end')
            putObject(context, bucket, key ,body);
        });
    }).on('error', function(e) {
        context.done('error', e);
    });

    function putObject(context, bucket, key ,body) {
        var params = {Bucket: bucket, Key: key, Body: body};
        console.log('s3 putObject' + params);

        s3.putObject(params, function(err, data) {
            if (err) {
                console.log('put error' + err);  
                context.done(null, err);
            } else {
                console.log("Successfully uploaded data to myBucket/myKey");
                context.done(null, "done");
            }
        });
    }
};

 注意点としては、http.requestがcallbackを利用している点です。リクエストの終了イベントを拾い、その後にS3に保存するように記述しています。実行すると次のようなログがでます。

START RequestId: 2b379992-8300-11e4-88d6-37d91d3def55
2014-12-13T19:42:26.841Z	2b379992-8300-11e4-88d6-37d91d3def55	Got response: 200
2014-12-13T19:42:26.842Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:26.842Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.022Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.022Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.203Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.384Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.384Z	2b379992-8300-11e4-88d6-37d91d3def55	end
2014-12-13T19:42:27.384Z	2b379992-8300-11e4-88d6-37d91d3def55	s3 putObject[object Object]
2014-12-13T19:42:27.574Z	2b379992-8300-11e4-88d6-37d91d3def55	Successfully uploaded data to myBucket/myKey
END RequestId: 2b379992-8300-11e4-88d6-37d91d3def55
REPORT RequestId: 2b379992-8300-11e4-88d6-37d91d3def55	Duration: 2558.28 ms	Billed Duration: 2600 ms 	Memory Size: 128 MB	Max Memory Used: 18 MB	

 次にparseHtmlの実装です。非標準のモジュールであるcheerioを利用しています。

console.log('Loading event');
var cheerio = require('cheerio');
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var http = require ('http');

exports.handler = function(event, context) {
    var record = event.Records[0];
    var bucket = record.s3.bucket.name;
    var key = record.s3.object.key;
    console.log('record:' + record);
    console.log('bucket:' + bucket);
    console.log('key:' + key);
    getObject(context, bucket, key)
    function getObject(context, bucket, key) {
        console.log('s3 getObject');
        s3.getObject({Bucket:bucket, Key:key},
            function(err,data) {
                if (err) {
                    console.log('error getting object ' + key + ' from bucket ' + bucket +
                       '. Make sure they exist and your bucket is in the same region as this function.');
                    context.done('error','error getting file'+err);
                }
                else {
                    var contentEncoding = data.ContentEncoding;
                    var contentBody = data.Body.toString(contentEncoding);
                    parseHtml(context, contentBody); 
                }
            }
        );
    }
    function parseHtml(context, body) {
        console.log('parseHtml');
        var $ = cheerio.load(body);
        var title = $("title").text();
        var stockPrice = $('td[class=stoksPrice]').text();
        console.log('stockPrice:'+stockPrice);
    }	
};

 まずイベントからバケット名とファイル名を取得します。その情報を元に、S3からファイルを取得します。ファイルを取得したら、cheerioでスクレイピングです。cheerioの使い方は割愛しますが、タグ名+クラス名で、要素を指定しています。
 尚、非標準のモジュールを利用しているので、ソースと一緒にモジュールもzipで固めてアップロードする必要があります。

├── node_modules
│     ├── aws-sdk
│     └── cheerio
├parseHtml.js

 上記のような構造のディレクトリをまとめてzip化します。

zip -r code.zip parseHtml.js node_modules/

アップロードしたファイルの中で、どのファイルを実行するかは「File name」で指定します。
f:id:dkfj:20141214051542p:plain

実行すると、次のようなログが出てきます。stockPriceの部分が、今回取得しようとした情報です。

START RequestId: 1d834bf2-8300-11e4-800c-bd34141de807
2014-12-13T19:42:05.492Z	1d834bf2-8300-11e4-800c-bd34141de807	record:[object Object]
2014-12-13T19:42:05.492Z	1d834bf2-8300-11e4-800c-bd34141de807	bucket:lambda-crawler
2014-12-13T19:42:05.492Z	1d834bf2-8300-11e4-800c-bd34141de807	key:test
2014-12-13T19:42:05.493Z	1d834bf2-8300-11e4-800c-bd34141de807	s3 getObject
2014-12-13T19:42:06.174Z	1d834bf2-8300-11e4-800c-bd34141de807	parseHtml
2014-12-13T19:42:07.173Z	1d834bf2-8300-11e4-800c-bd34141de807	stockPrice:7,336
Process exited before completing request

END RequestId: 1d834bf2-8300-11e4-800c-bd34141de807
REPORT RequestId: 1d834bf2-8300-11e4-800c-bd34141de807	Duration: 1920.75 ms	Billed Duration: 2000 ms 	Memory Size: 128 MB	Max Memory Used: 27 MB	

ハマリポイント



 成果物としてのソースは非常に簡単ですが、かなりハマりました。ハマったポイントは、以下の通りです。

  1. そもそもNode.js知らん
  2. S3 Event Notificationのロール登録時に、ロール中にprincipalのserviceにs3.amazonaws.comが必要

課題



 一応完成しましたが、1つ課題があります。最初のLambdaファンクションを呼び出すのを誰にするかです。呼び出し方として、s3 event notification,Kinesis stream, DyanmoDB stream、もしくはプログラムから呼び出すなどの手段あります。そもそも今回Lambdaでクローラーを作ろうと思った目的は、サーバレスで作りたかったからです。どの呼び出し元を選んだとしても、イベント起こす為の起点が必要です。その辺りどうするかがポイントになりますね。
 クローラーという用途で考えると、何らかの方法で取得対象をDynamoDBに登録して、Lambdaを呼び出して結果を記載するという方法があっているかもしれません。もしくは、呼び元の小さなインスタンスを用意して、処理自体をLambdaの潤沢なリソースを使うというのも考えられます。いずれにせよ、Lambdaを使うには、信頼性の高いスケジュール・ジョブの実行主体が必要になります。この辺りもサービス化して貰えるとありがたいですね。

まとめ



 駆け足でしたが、やはりLambdaは魅力的なサービスです。クローラーを作ってると、IPアドレスとリソースの確保が大変です。もう少し色々考察してみたいと思います。ちなみに、Rubyによるクローラー開発技法という本を書いたのですが、リクエストの中で多いのがPython版とNode.js版を出してとのことです。Lambdaが出たことなので、誰かNode.js版書きませんか?

See Also:
KimonoLabsと今後のサービスのあり方のはなし
プログラミング・レスで5分でサックリWebスクレイピング「kimonolabs」
『Rubyによるクローラー開発技法』を書きました


参照:
AWS Lambda Advent Calendar 2014
クローラー/スクレイピング Advent Calendar 2014
全部俺Advent Calendar

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

かなりリッチに3,000円〜10,000円の贅沢ワイン

 2,000円台のワインに引き続き、3,000円〜10,000円の贅沢ワインです。このクラスになると、レストランで飲むと全て1万円超えです。ヘタしたら3万円くらいになると思います。レストランでは手が出なくても、宅飲みであれば何とかなるのが魅力ですね。
 私は年に1回、都内の高級ホテルに泊まるのですが、そんな時に良いワインを1本持ち込んでいます。そんな楽しみ方もあるので、いろいろ試してみてください。

シャトー タルボ



香り高く、ボルドーらしい堂々としたワインです。懐に余裕がある時にボルドーらしいワインを味わいと聞かれると、私はこれを勧めます。
シャトー タルボ 2011 赤 750ml

シャトー タルボ 2011 赤 750ml

テラドーロ ホームヴィンヤード ジンファンデル



 ジンファンデル好きですが、その中で1,2を争うくらいのお気に入りです。記念日や年に一度の都内高級ホテル巡りのお供にと、大活躍です。なかなか手に入りづらいのが難点です。
f:id:dkfj:20140127214318j:plain

ルビコン エステート ジンファンデル



 私が一番好きなワインです。出世魚のように名前が変わっていきますが、映画監督のコッポラーさんがオーナーをしている名門ワイナリーです。アメリカ在住時代、日本からお客が来る度に、ここのワイナリーに連れて行っていました。日本で入手することが出来ず寂しい思いをしていましたが、最近日本でも手に入るようになっています。常に何本かストックしておくようにしています。お勧め

シャトー・イガイ・タカハ シャルドネ 「波紋」



 白ワインはあまり飲まないのですが、これを飲んだ時は思わず旨いなぁと唸ってしまいました。神の雫で見て飲んでみたいと思いながら、なかなか巡り会えませんでした。阪神百貨店で、関西初上陸の時に飲んで結構感動したのを覚えています。
※今見ると、普通にネットで売っていました。

カロン・セギュール



 フルボトルだと1万円を超えるので、ちょっと反則でハーフボトルを紹介。ハートのラベルで有名なカロン・セギュールです。オーク熟成でバニラの香りが高く、ちょっとチョコレートのような甘ったるさがあります。そんなワインなので、クリスマスに飲むのにぴったりです。ハーフボトルでの販売も多いので、ちょっと背伸びすれば手が届く価格も魅力です。

まとめ



 3,000円以上となると、途端に飲んだ経験が少なくなります。もうちょっと広げていかないとなぁと思いました。財布と相談しながら。

See Also:

ワインの品種。まずは3つだけ覚えておけば大丈夫
私が旨いと思う、普段飲みの1,000円台のワイン
ちょっとプチ贅沢な2,000円台のワイン
身も蓋も無い1,000円台のワインの選び方

ちょっとプチ贅沢な2,000円台のワイン

f:id:dkfj:20141210084320p:plain

 ブログタイトルを入力すると、ワインをサジェストされることに気がついた今日ころ頃です。昨年末にワインネタを出したところかなり好評だったので、今年も少し紹介します。今回は2,000円台のワインです。ちょっとプチ贅沢したい時に、いかがでしょうか?この価格帯だと、お店で飲むと7,000〜10,000円位するので、家飲みのお得感ご出てきますね。
 選定の基準としては、私の好みです。殆ど赤しか飲みません。また、ピノ・ノアールよりカベルネ・ソーヴィニヨンとか、ジンファンデルのような、割としっかり目のワインが好きです。その辺りを考慮の上で見てください。

シャトー・モンペラ



 神の雫で一躍有名になったワインです。割と手に入りやすく安定して美味しいです。

ケンダル・ジャクソン



 こちらもカベルネ・ソーヴィニヨンです。濃いので肉と一緒にどうぞ

ベイビーバッドボーイ



 ラベルが特徴的で、覚えやすいです。上の二つに較べて軽くて口当たりが良いので、女性に人気のようです。ら
[2010] ベイビー・バッド・ボーイ 750ml 1本 Baby Bad Boy

[2010] ベイビー・バッド・ボーイ 750ml 1本 Baby Bad Boy

カレラ ピノ・ノワール



 カリフォルニアのピノ・ノワール。通称カリピノ。ブルゴーニュに比べると、手軽で飲みやすいのが多いです。女性に人気の一本だと思います。

レンウッド ジンファンデル



 アメリカのジンファンデル。2,000円台でかなりボリュームがあって美味しいです。赤ラベルのが2,000円台でお手頃です。しかし、ネットで売っているところを見つけられませんでした。ヴィノスやまざきで、在庫豊富だと思います。
http://kitadaya.co.jp/shopdetail/001000000056/

レイヴェンスウッド ナパヴァレー ジンファンデル オールドヴァイン



 もう一本、カリフォルニアのジンファンデル。ジンファンデルといえば、Ravens Wood というぐらい、ジンファンデルで有名なワイナリーです。レイバンズウッドは1,000円台でも美味しいですが、やはり2,000円台だと1ランクあがります。

デヘサ・ガゴ



 スペインの鬼才、テルモ・ロドリゲスのワインです。もう1ランク上の、ガーゴも美味しいですが、デヘサ・ガゴも捨てがたいです。ちょっと酸っぱい系ですが、それもまた良しです。

ラ・アタラヤ



 スペインのワインです。オーク熟成でバニラのような香りがします。味の傾向としては、カロン・セギュールのような感じです。かなりお勧めです。

まとめ

 飲んだ記録をロクに取っていなかったので、有名ドコロしか思い出せませんでした。反省。2,000円台だと、かなり美味しいのが増えてきます。外で飲み食いすると、これくらいの値段は簡単に超えてしまうので、家でちょっと良いワインというのも面白いのではないでしょか?
 あと、いいワインあったら教えてください。

See Also:
ワインの品種。まずは3つだけ覚えておけば大丈夫
私が旨いと思う、普段飲みの1,000円台のワイン
身も蓋も無い1,000円台のワインの選び方

クローラー/スクレイピングのWebサービス 「Kimono」のユースケース

 クローラー/スクレイピング Advent Calendar 2014の8日目です。あと、全部俺Advent Calendarも開催中です。

 クローラー/スクレイピングをするのであれば、是非知っておいて欲しいサービスが「Kimono」です。Kimonoは、KimonoLabsという会社が作ったクローラー/スクレイピングを行うWebサービスです。特徴としては、プラグインを入れてブラウザの操作のみでスクレイピングができることです。操作を覚えれば、ITエンジニアでなくても充分使いこなせると思います。
 Kimonoの使い方については、プログラミング・レスで5分でサックリWebスクレイピング「kimonolabs」というエントリーで紹介しています。今回は取得したデータの利用方法とユースケースを考えてみたいと思います。

Kimonoで収集したデータの利用方法



 Kimonoで収集したデータは、API経由で参照できます。基本的には、APIを利用して表示用のプログラムを作成するという形になります。しかし、KimonoLabsが参照用のテンプレートを作成しているので、それを活用するという方法もあります。現在、KimonoLabsが提供しているテンプレートは、以下の6種類です。

  1. メール送信
  2. WebHooks
  3. MobileApp
  4. Embed
  5. Google SpreadSheet
  6. WordPress

f:id:dkfj:20141208003010p:plain

 MobileAppとEmbed、WordPressは基本的には同じです。URLもしくはHTMLが吐き出されるので、任意の場所に埋め込むという形です。MobileAppの場合は、URLをアプリとして登録するといった形になります。興味深いのが、Google SpreadSheetとWebHooksです。Google SpreadSheetについては、同じくChromeの拡張を入れた上でKimonoからスプレッドシートに関する操作権限を付与することで、データを取得したタイミングで直接書き込めるようにします。WebHooksについては、データ取得に任意のURLを呼び出すという機能です。呼び出された先の方で、リクエストをキーに処理を書いておけば基本的には何でもできます。
※ちなみに、Google SpreadSheetについては、メインのアカウントで使うには少し怖いのでまだ試していません。そのうちに、Kimono用のアカウントでも作って試そうかと考えています。

Kimonoのユースケース



 上記機能のユースケースとして考えられるのは、まずは可視化です。単純な例でいうと、グラフや時系列で見えるようにすることです。そういう意味で、GoogleのSpreadSheetに直接流し込めるのは、非常に面白いです。自分ならどう使うかというと、WebHooksを利用して後続処理を何らか作ります。図にすると次のような形ですね。
f:id:dkfj:20141208095953p:plain
 今の御時世だと、全部サーバレスで作れそうな勢いです。GoogleのBigQueryもいい加減試してみたいのですね。

まとめ



 AWS Lambdaが登場して、サーバ不要でバッチ的な処理が実行できるようになってきています。そして、Kimonoのように最近のWebサービスは、システム間を疎結合でつなぐ為の方法を当たり前のように出してきています。そうなると今までのように、自前のサーバで1から10まで全部実装するという考え方は、確実に時代遅れになってきています。この辺りの流れに上手く乗っかれば、小さな労力で面白い仕組みが作れるようになっていると思います。チャレンジしたいものですね!!
※大ヒットしたら最初のアーキテクチャでは耐えられないという場合もあるでしょうが、そうなってから考えれば充分です。

See Also:
KimonoLabsと今後のサービスのあり方のはなし
プログラミング・レスで5分でサックリWebスクレイピング「kimonolabs」

参照:
クローラー/スクレイピング Advent Calendar 2014
全部俺Advent Calendar

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

AWSのアカウント管理の話

  AWS Advent Calendar 2014の7日目です。あと、全部俺Advent Calendarも開催中です。

 運用絡みで何か書くと宣言したので、AWSのアカウント運用について書いてみます。テクニックや技術より、考え方の面での整理です。

AWSのアカウントの種類



 AWSで利用するアカウントは2種類あります。AWSアカウントとIAMアカウントです。AWSアカウントは、マスターアカウントと呼ぶこともあって大元のアカウントになります。AWSにサインアップ時に作るものが、AWSアカウントで1つだけ存在します。それに対して、IAMアカウントはユーザアカウントです。AWSの管理コンソールから、個々のユーザ向けなどに作成します。

AWSアカウントの取扱について



 AWSアカウントは、全権限を持っています。強力すぎるアカウントで、日常の運用に利用するには危険すぎます。日常の運用には使わないというのが基本となります。また、デフォルトではメールアドレスとパスワードのみで認証となります。アカウント作成した段階で、二要素認証の導入を強く推奨します。
 AWSでの二要素認証には、ハードウェアまたは仮想MFAデバイスを利用できます。ハードウェアについては、AWSのページから購入可能です。現在は、キータイプのものとカードタイプのものの2種類が購入可能です。仮想MFAデバイスについては、スマホやPCにインストールして利用します。iOSであれば、GoogleのAuthenticatorなどがあります。PCの場合は、Chromeの拡張などを利用すると便利です。
 AWSアカウントのMFAの選定については、どのような利用をするかがポイントになります。何故ならAWSアカウントには、接続元のIPアドレス制限が出来ません。その為、特定の場所からのみしか接続できないという運用を確実に行うには、物理MFAを設定して持ち運べないようにする必要があります。具体的なアドバイスとしては、金庫を買って放り込んでおくのが確実です。

IAMアカウントの取扱について



 IAMについては、IAMユーザとIAMグループ、IAMロールがあります。IAMユーザは個々のユーザに配布するもので、上の方でIAMアカウントと呼んでいたものです。IAMグループは、IAMユーザに所属させることにより一括で権限管理が出来るようになります。IAMロールはインスタンスに紐付けるものです。
 AWSを運用する上では、IAMの利用が基本となります。まずIAMユーザとグループの使い分けですが、組織で利用する上ではIAMユーザには基本的には認証の情報のみ設定し、権限に関する情報を付与しない方が良いと思います。理由としては、ユーザごとの設定で漏れなどを発生する確率を少しでも下げるためです。またそれ以前のAWSの仕様として、IAMユーザに付与できるポリシーのバイト数がIAMグループより小さいということがあります。その為、IAMユーザには複雑なポリシーを付与しにくいです。図でまとめると次のような形になります。

f:id:dkfj:20141207014716p:plain

IAMのポリシー設計について



 IAMで利用できるリソース等をIAMポリシーと呼びます。ポリシーについては、許可(Allow)/禁止(Deny)を組み合わせて記述します。記述の際には、3つの原則を理解しておく必要があります。

  1. 初期状態では、どの機能も利用することが出来ない(デフォルト拒否)
  2. 許可を付与することで、指定されたリソースが利用できる
  3. 同一の条件で、許可と禁止の両方を指定された場合は禁止になる(明示的拒否の優先)

 マトリックスにすると、次のようになります。

f:id:dkfj:20141207020558p:plain

 じゃぁ必要なものだけ単純に許可していけば良いかというと、そうでもなかったりします。例えば、EC2やS3といったサービスレベルであれば、それでも問題はないです。しかし、EC2のネットワーク機能を除いてといったことをすると、途端にややこしくなります。方法としては、EC2の機能を全部許可した上でネットワークに関するアクションを明示的に拒否していく(Allow-Deny)か、ネットワーク以外のアクションを許可していくということで実現できます。許可制の場合であれば、アクションが増える度に追加していく必要があり面倒くさいです。かつ、アクションが増えても明示的にアナウンスされることはないです。それならば、Allow-Denyで行くかというと都合が悪いケースもあります。
 例えば、機能単位でグループを作っていって、複数のグループに所属させることによってユーザの権限の強弱をつける場合があります。この場合のメリットとしては、個々のグループのポリシーをシンプルに保てるという点にあります。しかし、この方法だと拒否優先の原則が有るために、グループ重ねの効力が発揮されないケースが出てきます。

 そんな時に重宝するのが、NotActionです。NotActionは、指定されたものを反転して許可します。Allow-Actionでec2を指定した場合は、ec2が許可されます。Allow-NotActionでec2を指定した場合は、ec2以外の全てのリソースが許可されます。ややこしいので図にすると次のような形になります。
f:id:dkfj:20141207022153p:plain

 それぞれのポリシーは、次のような形です。

{
  "Statement":[
    {
      "Effect":"Allow",
      "Action":"ec2:*",
      "Resource":"*"
    }
  ]
}
{
  "Statement":[
    {
      "Effect":"Allow",
      "NotAction":"ec2:*",
      "Resource":"*"
    }
  ]
}

IAMのポリシーの管理について



 ストレートにCloudFormationで記述して、Gitなどのバージョン管理システムで管理しておきましょう。ポリシー自体をExcel等で管理しようとすると大変です。(ポリシー設計をExcelで管理するのは、別に構わないと思います。)

まとめ



 NotAction便利だよと書きたかっただけです。あまり日本語の情報はありません。ちょっとでも知られるキッカケになればと思います。後は、セキュリティを高める為には、やはりアカウントの適切な運用が第一歩だと思います。その辺りについての知見が共有されるようになればと思います。

See Also:
クローラーとAWSが出会ったら?第3回Webスクレイピング勉強会@東京
Amazon Elastic Load Balancing (ELB)の内部構造および拡張・障害時の動き
アプリケーション・サーバのセッションの保存先の話

参照:
AWS Advent Calendar 2014
IAM Policy Statementにおける NotAction / NotResource とは? : Developers.IO
Permissions and Policies - AWS

Ruby+Mechanizeで対話型のスクレイピング

 クローラー/スクレイピング Advent Calendar 2014の6日目です。あと、全部俺Advent Calendarも開催中です。

 リクエストがあったので、Mechanizeを使ったスクレイピング方法についてです。Mechanizeは、対話型の処理を得意とするスクレイピングの補助ツールです。対話型とは、例えばIDとパスワードを使ってログインするようなサイトなので、それぞれ項目に入力して次のページに遷移するような行為です。

Mechanizeのサンプルソース



 下記は、Amazonアソシエイトサイトから売上を取得するサンプルです。

require 'mechanize'

uri=URI.parse('https://affiliate.amazon.co.jp/')
agent = Mechanize.new
agent.user_agent_alias = 'Mac Safari'
page = agent.get(uri)
next_page = page.form_with(:name => 'sign_in') do |form|
  form.username = 'your_username'
  form.password = 'your_password'
end.submit
puts next_page.search('//*[@id="mini-report"]/div[5]/div[2]').text

 Mechanizeのオブジェクトを作成し、ユーザエージェントなどの属性情報を指定します。次に対象のURIを指定して、ページを開けます。ページを開くと、入力する対象のフォームを探します。幾つか探し方はありますが、今回はフォーム名で探しています。そして、フォーム中のユーザ名とパスワードにそれぞれ入力しサブミットしています。
 次のページでは、xpath形式で取得対象の情報を指定してデータを抜き出しています。Mechanizeも、データのスクレイピングにはNokogoriを内部的に利用しています。

Mechanizeが得意とする処理と苦手とする処理



 Mechanizeの基本は対話型の処理になります。その為、ページごとに必要な処理を記述する必要があります。その為、認証が必要なページやPOST送信が必要な場合には、絶大な威力を発揮します。反面、サイト内をくまなく巡回して、全てのリンクを取得するといったような処理は苦手とします。出来ないことはないですが、記述する処理量が多くなり非効率となります。

感想



 Mechanizeは、Rubyによるクローラー開発技法でも少し取り上げています。しかし、あまり詳しくは書いていない為、もっと色々書いてというリクエストを受けます。根強い人気なんだなぁと度々実感しています。今回はホンのさわり部分だけなので、いつか掘り下げて取り扱おうと思います。

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例