👨🏻‍💻

AthenaでALBのアクセスログを分析して、リクエスト/レスポンスの平均サイズを求め、簡易的な分布集計も行う

2022/06/19に公開

AthenaでALBのアクセスログを分析するクエリの紹介です。

背景

あるAPIに対するリクエストのサイズとレスポンスのサイズがどんなものなのか調べる必要がありました。あるAPIはALBの背後にいるので、ALBのアクセスログを分析することで分かります。やりたいことは、リクエスト/レスポンスの平均サイズを求めること、どのくらいのサイズのリクエスト/レスポンスが多いのか簡易的な分布集計をすること、この二つです。

平均サイズを求める

/*
received_bytesの平均
*/
SELECT AVG(received_bytes) FROM example_table;

/*
received_bytesの平均(0を分母から除く)
*/
SELECT SUM(CASE WHEN received_bytes>0 THEN received_bytes ELSE 0 END)/COUNT(received_bytes>0 OR NULL) FROM example_table;
received_bytesの平均

/*
sent_bytesの平均
*/
SELECT AVG(sent_bytes) FROM example_table;

/*
sent_bytesの平均(0を分母から除く)
*/
SELECT SUM(CASE WHEN sent_bytes>0 THEN received_bytes ELSE 0 END)/COUNT(received_bytes>0 OR NULL) FROM example_table;

received_bytesがリクエストサイズ、sent_bytesがレスポンスサイズです。集計対象とするフィールドはALBのドキュメントから確認可能です。
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-access-logs.html

単に平均を求めるだけであればAVG関数を使えばよいですが、集計対象としているフィールドは値が0になっている場合があります。値が0の場合は除きたいので、フィールドの値が0以上のログ行をカウントした数でフィールドの値の合計を割っています。

簡易的な分布集計をする

/*
received_bytesのざっくりした分布集計
*/
SELECT 
count (*) as total,
count((0 <= received_bytes and received_bytes <= 10000) or null) as "10KB以下",
count((10000 < received_bytes and received_bytes <= 20000) or null) as "10KB超、20KB以下",
count((20000 < received_bytes and received_bytes <= 30000) or null) as "20KB超、30KB以下",
count((30000 < received_bytes and received_bytes <= 40000) or null) as "30KB超、40KB以下",
count((40000 < received_bytes and received_bytes <= 50000) or null) as "40KB超、50KB以下",
count((50000 < received_bytes and received_bytes <= 60000) or null) as "50KB超、60KB以下",
count(60000 < received_bytes or null) as "60KB超"
FROM example_table;

/*
sent_bytesのざっくりした分布集計
*/
SELECT 
count (*) as total,
count((0 <= sent_bytes and sent_bytes <= 10000) or null) as "10KB以下",
count((10000 < sent_bytes and sent_bytes <= 20000) or null) as "10KB超、20KB以下",
count((20000 < sent_bytes and sent_bytes <= 30000) or null) as "20KB超、30KB以下",
count((30000 < sent_bytes and sent_bytes <= 40000) or null) as "30KB超、40KB以下",
count((40000 < sent_bytes and sent_bytes <= 50000) or null) as "40KB超、50KB以下",
count((50000 < sent_bytes and sent_bytes <= 60000) or null) as "50KB超、60KB以下",
count(60000 < sent_bytes or null) as "60KB超"
FROM example_table

やっていることは単純です。まず集計期間におけるログの行数を数えて全体の数を求めています。次にreceived/sent_bytesを10KBで区切って、区切りの間にログが何件あるか量を求めます。このようなクエリを書くことで、平均からは読み取ることができないサイズの分布をざっくりと掴むことができます。

一工夫必要なのは、条件に当てはまらないログ行をor nullでカウント対象外にしてやることです。このように書くことでNULLの場合はカウントされなくなります。

単にcountするだけでは、条件に一致する(true)行も一致しない行(false)もカウントしてしまい、求めている結果が得られません。

あとはクエリの結果をCSVでダウンロードして、Excelなどでグラフにしてあげると視覚的に分かりやすくなります。

費用に関する注意点

費用には注意しましょう。Athenaはスキャンしたデータ量に応じた課金体系です。
https://aws.amazon.com/jp/athena/pricing/

ALBに対するリクエスト量によりますが、ALBのアクセスログは大量に出力されます。ライフサイクルポリシーで保管期間を90日など短く設定しているなら、アクセスログを出力しているS3バケットのルート以下をテーブル作成してもスキャンするデータ量はそこまで大きくならないでしょう。

しかし、年単位でアクセスログが溜まっているS3バケットでルート以下をテーブル作成するとデータ量はそこそこ大きくなりそうです。求める結果が得られず、クエリを何度も実行してしまってスキャンしたデータ量が大きくなってしまい、意図せず費用が高くなってしまわないように気をつけましょう。

limitをかけるのも一つの方法ですが、この記事ではテーブル作成時に特定の月のアクセスログを集計対象とするようにしてスキャンするデータ量が大きくなりすぎないように対策しています。上に貼ったALBのアクセスログのドキュメントにアクセスログファイルの形式が記載されています。テーブルを作成する際にbucket[/prefix]/AWSLogs/aws-account-id/elasticloadbalancing/region/yyyy/mm/まで絞り込むとスキャンするデータ量を減らすことができるでしょう。

もう一つ注意するポイントがあります。スキャンするデータが格納されたS3バケットと、Athenaのリージョンは同じにしましょう。リージョンが異なると、リージョン間データ転送料金がかかります。

さらに、Amazon S3、AWS Lambda、AWS Glue、および Amazon SageMaker など、Athena で使用する AWS のサービスの標準レートに基づいた料金が請求されます。例えば、ストレージ、リクエスト、リージョン間データ転送は S3 レートで課金されます。
引用元

GitHubで編集を提案

Discussion