ALBのアクセスログをAthenaで分析してみる

# 経緯
本番環境でALBから500エラーが返ってきていて、アクセスログをみたかったけど、お客様環境の制限でS3バケット上のALBアクセスログの参照権限が付与されていなかった。
参照権限をもらえればいい話ではあるが、確認に時間がかかるらしく今すぐみることができない。
そもそも運用環境でアクセスログの参照権限がないのは不思議な話ではあるが、ALBから500エラーが返ってくることはよくあることであり、アクセスログを確認することも少なくないと思う。
しかし、この環境の設計では、アクセスログは保管のみの用途とされており、システム障害などが発生した場合や根本原因を調査する場合のログ分析が想定されたものではないため、分析の手法がなく、万が一分析が発生した場合、ログを手作業で見る羽目になってしまう。
運用改善の材料として、ALBのアクセスログをAthenaで分析できるようにし検証してみようと思いました。

# 今回やること
1. ログ転送用のS3バケット作成
2. ALBのアクセスログ有効化
3. Athenaで分析

# やってみる
### ログ転送用のS3バケット作成
まずはバケットを作ります。
![スクリーンショット 2024-06-30 0.58.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/b1348638-619a-06e6-e58c-210479e604b0.png)

次にALBからログを転送する際に受け取る側のS3バケットのポリシーを定義します。
ドキュメントにも記載がありますが、各リージョンによってELBアカウントIDがありますので、プリンシパルのところのIDを変更してください。
東京リージョンの場合は以下の通りです。
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-access-logging.html
```
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": "arn:aws:iam::582318560864:root"
			},
			"Action": "s3:PutObject",
			"Resource": "arn:aws:s3:::alb-log-bucket-2024/alb-accesslog/*"
		}
	]
}
```

### ALBアクセスログを有効化
バケットポリシーを定義したらアクセスログを有効にしましょう。
![スクリーンショット 2024-06-30 0.29.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/341aa723-4a53-880d-fd57-91f3f9211437.png)

有効化すると、S3バケットにログが転送されます。
![スクリーンショット 2024-06-30 1.07.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/fcc9d96f-efbc-efe2-a11b-d712f1c23962.png)


### Athenaで分析
その前に、S3にクエリ分析の結果を格納する場所を聞かれますので先にS3バケットを作成しておきます。
![スクリーンショット 2024-06-30 1.12.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/032b9649-5e86-9086-a53c-b7a9e3922cb1.png)

まずは、「TrinoSQLを使用してデータをクエリする」を押す
![スクリーンショット 2024-06-30 1.09.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/364257ec-6f89-6d55-4f29-3d3ec547ee9a.png)

クエリ分析結果を格納する場所を設定する
![スクリーンショット 2024-06-30 1.14.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/334e82e0-613d-9b32-9e7e-4ad496482cc5.png)

データベースを作成します。今回は「alb_db」というデータベース名にしました。
作成すると、データベースに追加されています。
![スクリーンショット 2024-06-30 1.17.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/d19d7655-ee7a-7c5b-ed30-798bae9fc996.png)

テーブルを作成します。クエリは、公式ドキュメントに記載のものをそのまま使いました。
最後の行は実際にALBアクセスログが格納されているS3バケット名のパスに変更してください。
作成が完了すると、テーブルに追加されていることが確認できます。
```
CREATE EXTERNAL TABLE IF NOT EXISTS alb_access_logs (
            type string,
            time string,
            elb string,
            client_ip string,
            client_port int,
            target_ip string,
            target_port int,
            request_processing_time double,
            target_processing_time double,
            response_processing_time double,
            elb_status_code int,
            target_status_code string,
            received_bytes bigint,
            sent_bytes bigint,
            request_verb string,
            request_url string,
            request_proto string,
            user_agent string,
            ssl_cipher string,
            ssl_protocol string,
            target_group_arn string,
            trace_id string,
            domain_name string,
            chosen_cert_arn string,
            matched_rule_priority string,
            request_creation_time string,
            actions_executed string,
            redirect_url string,
            lambda_error_reason string,
            target_port_list string,
            target_status_code_list string,
            classification string,
            classification_reason string,
            traceability_id string
            )
            ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
            WITH SERDEPROPERTIES (
            'serialization.format' = '1',
            'input.regex' = 
        '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) (.*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\s]+?)\" \"([^\s]+)\" \"([^ ]*)\" \"([^ ]*)\" ?([^ ]*)?( .*)?')
            LOCATION 's3://DOC-EXAMPLE-BUCKET/access-log-folder-path/'

```

![スクリーンショット 2024-06-30 1.22.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/5af54fb6-e456-d0c1-8322-ac3cb7c3a6f3.png)

これでようやく分析の準備が整いました。
実際に分析してみます。

まずは、ステータスコードが４００以上を全て出力してみます。
```
SELECT *
FROM alb_access_logs
WHERE elb_status_code >= 400
```
![スクリーンショット 2024-06-30 1.26.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/94317534-9b92-e265-3ec7-9a7669577ecf.png)
![スクリーンショット 2024-06-30 1.27.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/1c35857b-0a52-17a3-56dc-7435f7fd8615.png)
![スクリーンショット 2024-06-30 1.27.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/56e6fa86-39af-4c76-5f0d-482c92a360c4.png)
![スクリーンショット 2024-06-30 1.28.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/bcdedec6-46c9-c3ba-e14c-c67502690371.png)
![スクリーンショット 2024-06-30 1.28.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478546/588069be-96bc-bcd2-67a1-e2f934ff015b.png)

余談ですが、ALBのアクセスログのエントリは公式ドキュメントに記載があるので、そちらを参照ください。
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-access-logs.html

# まとめ
ALBのアクセスログは圧縮されているけど、その状態でも検索してくれるのはありがたい。
今回の経緯として、ELBのステータスコードとターゲットのステータスコードが大量に出ていたため、切り分けが難しくアクセスログも見たかったので、クエリの絞り込み次第で簡単に調査もできそう。
あとは料金の兼ね合いもあるので、あくまで改善提案の候補としてもう少し調べてみようと思う。