[簡単]LambdaのログをKinesis Data Firehose経由でS3に保存してみた

## この記事について
アイレット株式会社Advent Calendar2023 14日目の記事です！
携わっている案件にてAWS Kinesis Data Firehoseに触れる機会があり、興味を持ったので検証してみました。



## 実現したいこと
実現したいことは、タイトルの同じでLambdaのログをKinesis Data Firehose経由でS3に保存することです。
Lambdaで実行したログはCloud Watch Logsに保存されます。短期間だけ利用する場合は費用についてあまり気にしなくてもいいですが、長期間利用したり関数の実行回数が多くなることでログデータが膨大になり高い費用が発生してしまいます。料金の比較でS3はUSD 0.025/GBに対して、CloudWatch Logsでは収集でUSD0.76/GB,保存でUSD0.033/GBと高く設定されています。
そのため、ログの保存費用を削減するために一般的に1次保存先としてCloudWatch Logsを選択して2次保存先としてS3を選択するケースが多いです。(費用を削減する方法として、ロググループの保存期間の設定、S3のライフルサイクル設定・・が挙げられます)

CloudWatch LogsからS3に転送する方法でもエクスポート機能やAWS Lamdaを利用することやKinesis Data Firehoseを利用する方法があります。今回は楽に設定できるFirehoseを選択して実現していきたいと思います。構成図は以下の通りです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/9e168bcd-c476-eebd-a573-86af3032e90d.png)

今回は簡易的な検証を行うので、Terraformは使用せずマネジメントコンソール上で操作します。

## 各サービスの設定

### S3の設定
まずは、ログを格納するためS3を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/4c107ff4-277b-c2be-fe21-cf2e5690273e.png)

設定値は以下の通りです。
|設定項目 |設定値 |
|---|---|
|バケット名  |advent2023-lambda-log |
|AWS リージョン  |東京  |
|オブジェクト所有者  |ACL無効  |
|このバケットのブロックパブリックアクセス設定  |「パブリックアクセスをすべて ブロック」 |
|バケットのバージョニング  |無効にする  |
|タグ |追加しない  |
|デフォルトの暗号化  |無効にする  |
|オブジェクトロック(詳細設定)	 |無効にする  |

バケット名は全てのAWSアカウントの中で一意に決まるので、検証したい場合は違う名前を付与してください。

### IAMの設定
次にKinesis Data FirehoseからS3にデータを保存するために必要な権限を付与します。
#### advent2023-firehose-role
```ruby:信頼関係
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "firehose.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```
#### advent2023-firehose-policy

```ruby:ポリシー
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": [
				"s3:AbortMultipartUpload",
				"s3:GetBucketLocation",
				"s3:GetObject",
				"s3:ListBucket",
				"s3:ListBucketMultipartUploads",
				"s3:PutObject"
			],
			"Resource": [
				"arn:aws:s3:::advent2023-lambda-log",
				"arn:aws:s3:::advent2023-lambda-log/*"
			]
		},
		{
			"Sid": "Statement2",
			"Effect": "Allow",
			"Action": [
				"lambda:InvokeFunction",
				"lambda:GetFunctionConfiguration"
			],
			"Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxx:function:advent2023-logs-processor-func"
		}
	]
}
```
#### advent2023-cloudwatch-logs-role 
CloudwatchlogsがFirehoseへとデータを送信するために必要な権限を付与します。

```ruby:信頼関係
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```
#### advent2023-cloudwatch-logs-policy
xxxxxxxの部分はご自身のアカウントIDを入力してください。
```ruby:ポリシー
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": [
				"s3:AbortMultipartUpload",
				"s3:GetBucketLocation",
				"s3:GetObject",
				"s3:ListBucket",
				"s3:ListBucketMultipartUploads",
				"s3:PutObject"
			],
			"Resource": [
				"arn:aws:s3:::advent2023-lambda-log",
				"arn:aws:s3:::advent2023-lambda-log/*"
			]
		},
		{
			"Sid": "Statement2",
			"Effect": "Allow",
			"Action": [
				"lambda:InvokeFunction",
				"lambda:GetFunctionConfiguration"
			],
			"Resource": [
				"arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:advent2023-lambda-func"
			]
		}
	]
}
```
### Firehoseの設定
次にFirehoseの設定を行います。Amazon Kinesis→Data Firehose→配信ストリームを作成をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/a2055daa-824f-4dd0-c342-28d358d1f877.png)

設定値は以下の通りです。
|設定項目 |設定値 |
|---|---|
|ソース  |Direct PUT|
|送信先  |Amazon S3 |
|配信ストリーム名  |ACL無効  |
|S3 バケット  |advent2023-lambda-log |
|サービスアクセス  |advent2023-firehose-role   |

後でデータ変換の機能を使って検証を行いますが、上記以外の設定は一旦デフォルトでOKです。

### Lambdaの設定
lambdaの実行ログが欲しいだけなのでコードはシンプルです。
```ruby:advent2023-lambda-func
import json

def lambda_handler(event, context):
    
    print("advent2023-test")
    
    return {
        'statusCode': 200,
        'body': json.dumps('advent2023-test')
    }

```

### Cloudwatch Logsの設定
Cloudwatchlogsにサブスクリプションフィルターの設定を行います。対象のロググループ→サブスクリプションフィルター→作成→Kinesis Firehose サブスクリプションフィルターを作成のボタンをクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/e39ec0a2-3918-e467-c020-85163ed117b0.png)

設定値は以下の通りです。
|設定項目 |設定値 |
|---|---|
|送信先アカウント  |現在のアカウント|
|Kinesis Firehose 配信ストリーム  |advent2023-firehose-log |
|既存のロールを選択 |advent2023-cloudwatch-logs-role  |
|ログの形式|その他  |
|サブスクリプションフィルターのパターン |  |
|サブスクリプションフィルター名|advent2023-lambda-subfilter  |

### 検証
lambdaのテストを実行すると、S3のadvent2023-lambda-logにログデータが格納されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/5a1c57d2-2bcb-00fe-9685-0c21e1d03232.png)



ログデータをダウンロードして開いてみると


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/2052248d-25e6-21fc-24ef-abbeee813d0d.png)


文字化けが起きてしまいました・・
調べてみると、CloudWatchLogsからAmazonkinesisDataFirehoseに送信されたデータはgzipに圧縮されていて、そのデータを無理やり開こうとしたのが原因らしいです。7zipで圧縮されたデータを解凍してみると・・

https://7-zip.opensource.jp/

無事にログデータの内容を確認できました。(念のため、一部データログを隠してます)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/05572e50-a77c-05c4-1920-4b7844427b8c.png)

ログのデータが改行されていなくて見にくいので、firehoseの機能である、データ変換の機能を使用してログデータを見やすいように整形しようと思います。


advent2023-firehose-logの設定でレコードを変換および転換の編集をクリックします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/a68273bf-aa3b-3467-088e-b0fb3d6874ae.png)


次に、AWS Lambdaでソースレコードを変換でデータ変換をオンにチャックを入れ、関数の作成をクリックします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/f930b106-4cef-fd70-f646-a12940a0504b.png)


今回は1からLambdaのコードを作成するのではなく、Blueprintを使用します。
Blueprintとは、AWS Lambda関数を作成する際に使用できるテンプレートのことを指します。
特定のイベントトリガーや使用ケースに対応したサンプルコードと設定が提供されているので、さまざまなシナリオに合わせた関数を素早く作成できます。


Kinesis Data Firehose Cloudwatch Logs Processorのプルーフプリントを選択します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/6661d4fc-a309-f205-fa5c-97bfe83a7c82.png)


関数の作成の画面では設計図の使用のボタンをクリックします。設計図名でProcess Cloudwatch logs sent  to Kinesis Firehoseを選択、関数名をadvent2023-logs-processor-func、実行ロールは基本的なLambdaアクセス権限で新しいロールを作成を選択し関数の作成を行います。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/601663c6-247b-ee33-c155-8736d3780fee.png)

処理時間がかかることを想定してタイムアウトを10分に修正しました。ちなみに、AWSの公式ドキュメントでは最大5分のLambdaの呼び出し時間がサポートされています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/b22f2e7f-4ced-6679-a2ce-3fd08c271791.png)

再度、Lambdaのadvent2023-lambda-funcでテストを実行してみると、S3のadvent2023-lambda-logにログデータが格納されます。ログデータの中身は整形されいてて前回に比べて見やすくなりました。


![Photokako-mosaic-pKLlKI5BAM8AXmJj.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748961/ac22c289-811e-031b-6b67-2d8a8efd36e0.png)


### まとめ
今回はLambdaのログをKinesis Data Firehose経由でS3に保存してみました。検証してみてより一層理解が深まりました。
初めて記事を執筆したので拙い文章になってしまいましたが、最後まで読んでいただきありがとうございました。

### 参考資料
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#FirehoseExample

https://d1.awsstatic.com/whitepapers/jp/whitepaper-streaming-data-solutions-on-aws-with-amazon-kinesis.pdf

https://docs.aws.amazon.com/ja_jp/firehose/latest/dev/data-transformation.html#data-transformation-execution-duration