Fluentd&Kinesis&Lambdaによる柔軟で高可用性なログ収集基盤の構築

## 概要

Fluentdによるログ収集基盤を、Lambda&Kinesisを利用した形で構成を変更したので、その時のメモ。
先に一言で感想を述べるのであれば、巷の噂通り、**Kinesis&Lambdaは素敵**でした。

## Fluentd Aggregatorが抱える問題点

ログデータをFluentdのForwarder & Aggregatorを経由し、S3とRedshiftに保存するような構成でしたが、Aggregatorは冗長化しづらくボトルネックとなっていました。

![Screen Shot 2016-07-17 at 9.34.06 AM.png](https://qiita-image-store.s3.amazonaws.com/0/18841/a6b3de0d-1af2-ff65-741f-51a3e89fabee.png "Screen Shot 2016-07-17 at 9.34.06 AM.png")

## ログ収集に求める要件

ログ収集の構成を改善するにあたり、先に挙げた目の前の問題点を解決するのは勿論ですが、他にも様々な要件があり、大別すると以下4つ。

- 信頼性: データをロスなく収集することができる。
- 安定性: ログ量に関係なく安定的なパフォーマンスを維持できる。
- 冗長性: 安全かつ簡単にスケーリングが可能
- 汎用性: 複数ログを複数サービスに転送可能。

これらの要件を満たすことができる技術ということで、AWS Kinesis(&Lambda)の登場です。

## AWS Kinesis & Lambdaとは?

AWS開発者ガイドから、それぞれの特徴と制限をざっくりと調べてみると以下の通り。

### Kinesisの特徴と制限

Fluentdの場合は、PUSH型

- 特徴
	1. 簡単管理: フルマネージド
	2. リアルタイム: ストリーミングタイプの継続処理
	3. 伸縮自在: シームレスにスループットやボリュームを変えることができる。
	4. インテグレーション(S3, Redshift, Dynamo)
	5. リアルタイム処理: KCL(Kinesis Client Library)を用いて容易にリアルタイムストリーミングデータの処理が実装可能。
	6. Low Cost: コスト効率が高い。
- 制限
	1. 1レコードあたりの容量: 25KB
	2. 1シャードあたりの転送量: 1MB/秒、1000レコード/秒 
	3. シャード数上限: 25シャード (申請すれば引き上げ可能らしい)
	4. ストリームの保持期間は24時間 (1週間まで延長可能)

### Lambdaの特徴と制限

- 特徴
	- コードを書くだけでいい(対応言語:Nodejs,Java,Python)
	- オートスケールする
	- インフラの管理が不要
	- メモリ容量: 128mb〜1.5GB
	- CPU: メモリ容量に対して変動
	- タイムアウトは15s〜300sで設定可能(デフォルト15s)
- 制限
	- 同時実行可能数: 100/s

### Kinesis&Lambdaのログデータの流れ

データの流れを図にしてみると以下の通り。

FluentdのAggregaterの場合は、単純にデータのルーティング処理でデータが通過していくだけという形(PUSH型)でしたが、Kinesisの場合は、データを保持し続け(デフォルト24時間)、それをLambdaが取りにいくという形(PULL型)になります。

従来のLog Aggregatorでは、例えばサーバー・ネットワークで問題が起きてログデータが欠損した場合、復旧は困難でしたが、そこをKinesisが担保してくれます。さらにLambda側で何か問題が起きた場合でも、Lambdaでは処理を再実行させることもできますし、処理を一旦停止しておくこともできます。 (素晴らしい・・)

![Screen Shot 2016-07-17 at 9.35.48 AM.png](https://qiita-image-store.s3.amazonaws.com/0/18841/f6f019f6-82f6-d1cb-40e3-0713a201d60f.png "Screen Shot 2016-07-17 at 9.35.48 AM.png")

## KPLとKCLとは?

ログ収集でKinesisとLambdaを導入するにあたり、重要なライブラリがKPCとKCLです。簡単に言ってしまうと、データを送信する際に1レコードずつ送信するのではなく、1レコード内に複数レコード集約してまとめて送ってしまおう、というもので、スループットが劇的に向上します。
(ちなみにこれらを使わずにKinesisを利用することもできます。)

- KPL(Kinesis Producer Library) : ログを集約する。
	- コア部分はC++で実装
	- プロトコルバッファ実装
	- リトライ、シャード数最適化、レコード集約、メトリクス送信
- KCL(Kinesis Client Library) : 集約されたログを展開(Disaggregation)する。

それぞれのライブラリはgithubにてAWSから提供されています。

- aws-fluent-plugin-kinesis: https://github.com/awslabs/aws-fluent-plugin-kinesis: 
- kinesis-aggregation : https://github.com/awslabs/kinesis-aggregation

## Kinesis&Lambdaを活用したログ収集構成

WEBアプリケーションのログデータをS3とRedshiftに保存しているのですが、KinesisとLambdaの導入により、以下のように構成を変更しました。

![MyRoomClip.020.jpeg](https://qiita-image-store.s3.amazonaws.com/0/18841/eb6a1acb-490b-436d-60bc-ff0bdb87984c.jpeg "MyRoomClip.020.jpeg")

## Fluentdの設定: Kinesis利用時の最適設定

Kinesisを導入にあたりFluentdの設定も見直しが必要になってきます。設定は、各々の環境や負荷状況により異なると思いますので、Fluentd・Kinesis・Lambdaの主要な設定項目を図にまとめてみました。
ログデータはこまめに送るよりは、なるべく一纏めに送るような設定にしたほうがパフォーマンスが良くなります。
実際に運用しながら微調整していくことが必要かなと思います。

![MyRoomClip.031.jpeg](https://qiita-image-store.s3.amazonaws.com/0/18841/1ae25ca0-c8bc-d2b1-f493-9d7363c4fbff.jpeg "MyRoomClip.031.jpeg")

設定例。

```
<match kinesis.**>
  type kinesis_producer
  region ap-northeast-1
  stream_name YOUR_KINESIS_NAME
  include_tag_key true

  buffer_type file
  buffer_chunk_limit 1m
  buffer_queue_limit 512
  buffer_path /var/log/td-agent/buffer/

  flush_interval 15s
  try_flush_interval 1
  queued_chunk_flush_interval 1
  num_threads 1
  detach_process 1
</match>
```

## 複数ログの取り扱いについて: Lambdaでのルーティング処理

ログの種類がアクセスログだけとか1種類だけならいいのですが、実際にサービスを運用すると様々な種類のログが出力され、必要なログもサービスに合わせて増えていくと思います。その度に、新しくKinesisとLambdaを構築するのは冗長的ではなく、手間がかかります。そのため、Kinesisには全てのログを流し込み、Lambdaでログを分類し、それぞれS3、Redshiftテーブルに放り込むというやり方ができます。

下記の図は、仮にログがaccess,photo,userと3種類あった場合のログデータの流れを表したものです。(Kinesis&Lambaの組み方は色々あると思いますので、あくまで一例として)

![MyRoomClip.032.jpeg](https://qiita-image-store.s3.amazonaws.com/0/18841/aa7d8f1d-6d1e-f3fb-cd5f-13f0ba836b1b.jpeg "MyRoomClip.032.jpeg")

## 備考 (メモ)

#### ラムダ関数が失敗した場合はどうなるか?

Q: 関数がイベントの処理に失敗した場合はどうなりますか?

> Amazon S3 バケット通知およびカスタムイベントに関しては、コードにエラーがある場合、サービスまたはリソースの上限を超えた場合、AWS Lambda は関数の実行を 3 回試行します。Amazon DynamoDB ストリームおよび Amazon Kinesis ストリームなどお客様に代わって AWS Lambda がポーリングする指定イベントソースに関しては、開発者のコードにエラーがある場合、Lambda はデータの有効期限が切れるまで実行を試行します。Amazon Kinesis および Amazon DynamoDB コンソール、AWS Lambda が関数に向けて生成した Amazon CloudWatch メトリクスを使用して進行状況をモニタリングできます。エラーまたは実行スロットリング率に基づいて、Amazon CloudWatch アラームを設定することもできます。

#### Kinesisと連携した際のLambdaの同時実行回数は?

> Amazon Kinesis または DynamoDB のストリームを読み取る Lambda 関数の場合、Lambda ではストリームのシャードごとに関数が同時に実行されます。たとえば、Amazon Kinesis ストリームに 100 のシャードがある場合、関数の実行数はどの時点でも最大 100 件になります。関数は、(イベントソース定義内で設定したバッチサイズを上限として) レコード数に応じて呼び出され、1 秒に複数回、アクティブな各シャードからの読み取りを順次行います。たとえば、Amazon Kinesis ストリームに 10 のアクティブなシャードがあるとします。この場合、Lambda 関数の同時実行数は 10 件となります。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/intro-core-components.html#concurrent-executions

#### プロトコルバッファ(Protocol Buffer)とは?


> Protocol Buffersのデザインの目的はシンプルさとパフォーマンスである。とりわけ、XMLより高速になるようデザインされている。Google は XML との比較で、3〜10倍小さく、20〜100倍高速であると主張している[2]。Google が挙げている例では、XML では 69 バイト以上の物が Protocol Buffers では 28 バイトであり、パースに 5〜10 マイクロ秒かかるのが、0.1〜0.2 マイクロ秒ですむとしている。

https://ja.wikipedia.org/wiki/Protocol_Buffers

## 参考URL

・Amazon Kinesisの集約データをAWS Lambdaで処理する by AWS Solution Archtectブログ (2016/2)
http://aws.typepad.com/sajp/2016/02/process-amazon-kinesis-aggregated-data-with-aws-lambda.html

・秒間数万のログをいい感じにするアーキテクチャ (2016.6.3)
https://speakerdeck.com/kanny/miao-jian-shu-mo-falseroguwoiigan-zinisuruakitekutiya

・Kinesis Producer Library(KPL)とfluentdとLambdaを連携してKinesisのスループットを上げる (2016.6.5)
http://dev.classmethod.jp/cloud/aws/high-throughput-messaging-system-with-kinesis-kpl-fluentd-lambda/

・AWS Black Belt Tech シリーズ 2015 (2015.9.4)
http://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-tech-2015-amazon-kinesis

・AWS Black Belt Techシリーズ Amazon (2014.5.28)
http://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-tech-amazon-kinesis

・BufferedOutput pluginの代表的なoptionについて
http://qiita.com/tatsu-yam/items/bd7006e483f3b3c64309