Lambda+Elasticsearch(Amazon Elasticsearch Service)でELBのログをサーバーレスでKibanaで可視化する

AmazonのマネージドサービスとしてElasticsearch+Kiban4が使えるAmazon Elasticsearch Serviceが発表されました。

[【AWS発表】Amazon Elasticsearch Service](http://aws.typepad.com/aws_japan/2015/10/amazon-elasticsearch-service.html)

以前fluentdを使ってELBのログをElasticsearchに取り込むことをやってみたのですが、その場合、fluentdを動かすサーバーが必要です。

[ELBのアクセスログをfluentd(fluent-plugin-elb-access-log)+Elasticsearch+kibanaで可視化する](http://qiita.com/toshihirock/items/a90badb49e648af92d35)

今であればLambdaがTokyoリージョンにあって使えるのでS3へのELBアクセスログのアップロードをイベントにして、Lambdaで処理してElasticsearchに登録することでサーバーレスで可視化ができそうだと思い、やってみたのでメモ。

# Lambda用のロール作成

IAMでLambda用のS3へのアクセス及びElasticsearchへの登録ができるロールを予め作成しておきます。

# Elasticsearchの作成

マネージメントコンソールなどからElasticsearchの新規ドメインを作成しておきます。

また、先ほど作成したロールからElasticsearchを操作できるようにします。

作成後、作成したドメインのエンドポイントをメモしておきてください。

# Lambdaのコード作成

LambdaからElasticsearchへ登録するコードを書く必要があります。
と、思ったのですが、以下にLambdaによってS3からapacheのログをElasticsearchに登録するAWS公式のサンプルがありました。

[amazon-elasticsearch-lambda-samples/src/s3_lambda_es.js](https://github.com/awslabs/amazon-elasticsearch-lambda-samples/blob/master/src/s3_lambda_es.js)

ログファイルのパース部分がapache/nginx用のものを使っているようなので、ELB用のパースに変えれば良さげです。
apache/nginxで利用されているものを参考にELBのアクセスログをパースするnpmを作ってみました。

[elb-log-parser](https://www.npmjs.com/package/elb-log-parser)

今回はこれと既存のものを利用して少しのコード変更で実現します。もし、パース部分をもっと細かくする場合も同様にparserを作って登録する部分は既存のものを使ってやると簡単にできるかと思います。

まず、適当な所でAmazon公式のリポジトリからs3からElasticsearchへ登録するコードを取得します。

```
$git clone https://github.com/awslabs/amazon-elasticsearch-lambda-samples.git
```

次に今回利用する用のLambdaのプロジェクトを作成します。

```
# プロジェクト作成
$mkdir es-elb-log
$cd es-elb-log
$cp ../amazon-elasticsearch-lambda-samples/src/s3_lambda_es.js index.js

# 依存ライブラリのインストール。bylineはStreamを1行ごとに取り出せる
$npm install aws-sdk byline elb-log-parser
```

次に少しファイルを変更します。`{your-es-endpoint}`は先ほど作成したElasticsearchのエンドポイントにしてください。

```index.js
- var parse = require('clf-parser');
+ var parse = require('elb-log-parser');

- endpoint: 'my-search-endpoint.amazonaws.com',
+ endpoint: {your-es-endpoint},
- region: 'my-region',
+ region: 'ap-northeast-1',
- doctype: 'apache',
+ doctype: 'elb',
```

ZIPで圧縮します。

```
$zip -r es-elb-log.zip index.js node_modules
```

これでコードができたのであとはLambdaFunctionをマネジーメントコンソールなどから作成し、作成したZIPファイルをアップロードします。

Handlerにはindex.handlerを指定し、Roleには最初に作成したロールを指定してください。また、ログの量によると思いますが、Timeoutの時間は少し長めにしておくと良いかもしれません。

# やってみる

準備ができたので以下を行い、Lambda経由でアクセスログが登録できるか確認します。

+ EC2を起動し、Webサーバーを起動
+ ELBを作成し、EC2にトラフィックが流れるようにする
+ ELBのアクセスログを任意のS3バケットに登録するように設定。アクセスログの間隔を5分にすると結果が早く分かります。
+ LambdaのEventSourcesでELBのアクセスログを配置するバケット、EventTypeとしてObject Created(All)を指定します。

![Screen Shot 2015-10-12 at 6.32.21 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/63d0cc3e-a7ab-0214-8c92-041da1a0b805.png "Screen Shot 2015-10-12 at 6.32.21 PM.png")

あとは適当にELBにアクセスし、ログが出力されるのを待ちます。

設定が無事できていればS3へアクセスログがアップロードされたタイミングでLambdaが実行され、Elasticsearchへログ情報が登録されます。Elasticsearhをマネージメントコンソールで確認すると以下のようにレコードが登録されているのが確認できるかと思います。

![Screen Shot 2015-10-12 at 5.23.49 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/ac6304bc-3b11-7a82-9fa3-f6916423fb7b.png "Screen Shot 2015-10-12 at 5.23.49 PM.png")

CloudwatchLogsでLambdaのログも確認できます。

![Screen Shot 2015-10-12 at 6.39.52 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/15916a9c-29df-f0d9-364b-c571e62a8d9a.png "Screen Shot 2015-10-12 at 6.39.52 PM.png")

また、Elasticsearchのアクセスポリシーを変更し、利用している環境のIPからの接続を許可することでKinabaにアクセスし、以下のようにログ情報を見ることができます。

![Screen Shot 2015-10-12 at 5.35.36 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/824bd4bf-007c-1a92-8769-90ce74628b9b.png "Screen Shot 2015-10-12 at 5.35.36 PM.png")

サーバー管理なしでここまでできるのはかなりいい感じです!