Amazon AthenaでALBのアクセスログを分析する -リクエスト/レスポンスサイズの平均と簡単な分布集計

仕事でAthenaを使う機会がありました。便利だなと思ったので、使い方を忘れないように書き記しておくことにします。初めてAthenaを使う方の参考になれば幸いです。

今回のお題

ある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のドキュメントから確認可能です。

単に平均を求めるだけであれば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はスキャンしたデータ量に応じた課金体系です。

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 レートで課金されます。

https://aws.amazon.com/jp/athena/pricing/

おわりに

ワンライナーもいいですが、分析期間が1週間とか1ヶ月になってくると、ログをダウンロードする手間と分析処理(コマンド)の実行時間が増えるのが困ります。Athenaを使ってささっと分析できるようになりたいですね。

この記事が気に入ったらサポートをしてみませんか?