💨

【AWS】ALB,CloudFrontのログをAthenaで分析する🐱

2023/06/09に公開

概要📝

ALBとCloudFrontのログを有効化してS3に格納し、Athenaで分析してみる方法について記載しています

私はAWSエンジニアとしてまだまだ駆け出しなので、もっと良い分析方法があるかと思いますが、少しでも参考になれば幸いです🙇‍♂️🙇‍♂️

対象読者🔖

  • ALB・CloudFrontのログを見たことがない方
  • ALB・CloudFrontのログを有効化はしてみたがどうすればいいかわからない方

目的、気持ち🔥

  • 想定: わたしは、せんぱいからきほんの監視業務を教えてもらった!🕺

我々のプロダクトでは、
CloudFront(FE用・BE用) + 一般的3層アーキテクチャ + ECS(Fargate) +α
の構成を採用していました🍍

先輩に「日々どんな監視を行なったら良いでしょうか?」と聞いたところ、
その中のひとつとして「CloudFront・ALBで5xxエラーが発生したら、調査してBEに連携して、改善すべきかどうか記録しておくといいですよ」という内容がありました🎯

他にもさまざまな監視項目・方法が考えられると思いますが、今回は上記について記載します
(特にNew Relicのようなツールを使った改善フローも構築してみたいですが、現状は経験がありません🙇‍♂️)

コスト💰

アクセスログを溜め続けることで、S3の料金
Athenaのクエリを叩くことで、Athenaの料金
がかかります

特にAthenaのクエリは対象のデータを読み込むと意外に料金がかかってしまうことがありますので、
・テーブルを作成する際に範囲を限定
・パーティションを使う
といった工夫を、必要に応じて検討した方が良いと思います💸

手順👓

ログをS3に格納⚙️

ALBのログを有効化⚙️

  • Consoleの検索窓>EC2>Load Balancers>設定変更したいLoad Balancer>下タブ Attributes>Edit >MonitoringセクションのAccess losgを有効化


  • S3 URI に、ログを格納したい場所の、S3バケットのプレフィックスのURIを入力
    例えば my-alb-access-logs/access-logs/ のように入力

  • Save Changes で完了

CloudFrontのログを有効化⚙️

  • Consoleの検索窓>CloudFront>Distributions>設定変更したいディストリビューション>タブGeneral>Edit>Standard loggingセクションをOnにする


  • S3 bucketに、ログを格納するS3バケットの名前を入力

例えば my-cloudfront-access-logslog-bucket.s3.amazonaws.com のように入力

  • Log prefixにログを格納するプレフィックスを入力

例えば、access-logs/のように入力

  • Save Changesで完了

完了です

  • (補足)CloudFrontログについて、Lambdaで毎月1日のUTC0時にプレフィックスを変えるものを実装する

CloudFrontログは、ALBログと違って、年月日ごとにプレフィックスが分かれていません
分けたい方は、下記の公式ドキュメントを参考にすると良いと思います

私は、
access-logs/ →この階層に当月分ログがある
archive-logs/year/month/day →この階層に過去月分のログがある
というプレフィックスになるようにしました

Lambdaは月1回、access-logsからarchive-logsに移動する(リネームする)処理を行います

(任意)CloudWatchダッシュボードに、5xxエラーのメトリクスを登録⚙️

  • ConSoleの検索窓>CloudWatch>Dashboards>開きたいダッシュボードまたはCreate Dashboard>+>Lineを選択してNext>Metricsを選択してNext

  • 検索窓にALB>Per AppELB, per AZ Metrics>検索窓に5xx>HTTPCode_Target_5XX_Countにチェックを入れてCreate widget

まだ5xxエラーが1件も発生していない状態だと、検索で出てこない場合があります
そのときは、時間を空けて発生してから試してみると良いと思います

  • +>Lineを選択してNext>Metricsを選択してNext>検索窓の左TokyoからUS East(N. Virginia)us-east-1を選択

  • 検索窓にCloudFront>Per-Distribution Metrics>検索窓に5xx>5xxErrorRateにチェックを入れてCreate widget

  • 定期的にダッシュボードを見て、CloudFrontまたはALBで5xxエラーが発生していたら、Athenaで分析してみる

Athenaでの分析例は後述します

Athenaにログ分析用のテーブルを作成⚙️

テーブル名Locationの部分を、変更して、Athena内でクエリを実行すればOKです
公式ドキュメント内にはパーティションを使用するものもありますが、私はLocationで月ごとのプレフィックスをすることでクエリの対象範囲を絞っています

一度テーブル作成に成功したら、Generate table DDLで同じクエリを使い回して、別のテーブルを作ることができます
Tables>対象テーブルの...を押す>Generate table DDL

ALBログ分析用のテーブル🔧

公式ドキュメントに従って、ALBログ分析用のテーブルを作成します

下記は私が現状使っているテーブルからGenerate table DDLしたものです
公式ドキュメントのものと若干差異があったので、何かエラーが出て調整した後のものかな...と思います
公式も参照しつつうまく調整をいただければ幸いです

CREATE EXTERNAL TABLE `[テーブル名]`(
  `type` string COMMENT '', 
  `time` string COMMENT '', 
  `elb` string COMMENT '', 
  `client_ip` string COMMENT '', 
  `client_port` int COMMENT '', 
  `target_ip` string COMMENT '', 
  `target_port` int COMMENT '', 
  `request_processing_time` double COMMENT '', 
  `target_processing_time` double COMMENT '', 
  `response_processing_time` double COMMENT '', 
  `elb_status_code` int COMMENT '', 
  `target_status_code` string COMMENT '', 
  `received_bytes` bigint COMMENT '', 
  `sent_bytes` bigint COMMENT '', 
  `request_verb` string COMMENT '', 
  `request_url` string COMMENT '', 
  `request_proto` string COMMENT '', 
  `user_agent` string COMMENT '', 
  `ssl_cipher` string COMMENT '', 
  `ssl_protocol` string COMMENT '', 
  `target_group_arn` string COMMENT '', 
  `trace_id` string COMMENT '', 
  `domain_name` string COMMENT '', 
  `chosen_cert_arn` string COMMENT '', 
  `matched_rule_priority` string COMMENT '', 
  `request_creation_time` string COMMENT '', 
  `actions_executed` string COMMENT '', 
  `redirect_url` string COMMENT '', 
  `lambda_error_reason` string COMMENT '', 
  `target_port_list` string COMMENT '', 
  `target_status_code_list` string COMMENT '', 
  `classification` string COMMENT '', 
  `classification_reason` string COMMENT '')
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.serde2.RegexSerDe' 
WITH SERDEPROPERTIES ( 
  '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]+)\" \"([^ ]*)\" \"([^ ]*)\"') 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://[バケット名]/access-logs/AWSLogs/[アカウントID]/elasticloadbalancing/[リージョン]/[年]/[月]'
TBLPROPERTIES (
  'transient_lastDdlTime'='1686285466')

CloudFrontログ分析用のテーブル🔧

公式ドキュメントに従って、CloudFrontログ分析用のテーブルを作成します

下記は私が現状使っているテーブルからGenerate table DDLしたものです
公式ドキュメントのものと若干差異があったので、何かエラーが出て調整した後のものかな...と思います
公式も参照しつつうまく調整をいただければ幸いです

CREATE EXTERNAL TABLE [テーブル名](
  `date` date, 
  `time` string, 
  `location` string, 
  `bytes` bigint, 
  `request_ip` string, 
  `method` string, 
  `host` string, 
  `uri` string, 
  `status` int, 
  `referrer` string, 
  `user_agent` string, 
  `query_string` string, 
  `cookie` string, 
  `result_type` string, 
  `request_id` string, 
  `host_header` string, 
  `request_protocol` string, 
  `request_bytes` bigint, 
  `time_taken` float, 
  `xforwarded_for` string, 
  `ssl_protocol` string, 
  `ssl_cipher` string, 
  `response_result_type` string, 
  `http_version` string, 
  `fle_status` string, 
  `fle_encrypted_fields` int, 
  `c_port` int, 
  `time_to_first_byte` float, 
  `x_edge_detailed_result_type` string, 
  `sc_content_type` string, 
  `sc_content_len` bigint, 
  `sc_range_start` bigint, 
  `sc_range_end` bigint)
ROW FORMAT DELIMITED 
  FIELDS TERMINATED BY '\t' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://[バケット名]/access-logs'
TBLPROPERTIES (
  'skip.header.line.count'='2', 
  'transient_lastDdlTime'='1676855653')

(例)分析用クエリ🔧

先ほどテーブル作成の際に紹介した ALBとCloudFrontの公式ドキュメントにもクエリ例が載っています
Classmethodさんのブログ記事なんかにも、いろんな分析方法が載っていますので、検索してみると良いと思います

CloudFrontログの項目 - 標準ログファイルフィールド
ALBログの項目 - 構文

5xxエラーを検索👓

CloudFront

SELECT *
FROM [テーブル名]
WHERE 1=1
    -- AND uri = ''
    -- AND request_ip = ''
    AND status >= 500
ORDER BY date DESC,time DESC;

コメントになっている部分は最初にクエリを叩いてから以下のように使ったりします

  • uri
    同様のエラーが同じuriでどのくらい起きているかざっと見たい場合

  • request_ip
    エラーになった前後、同じrequest_ipの人が成功しているか、何度も失敗しているか

ALB

SELECT *
FROM [テーブル名]
WHERE 1=1
    -- AND request_url = ''
    -- AND client_ip = ''
    AND elb_status_code >= 500
ORDER BY time DESC;

著しく時間がかかっている処理がないか検索👓

5xxエラーとは別に、こちらもたまに見ています
CloudFront

SELECT uri,max(time_taken) as max_time_taken
FROM [テーブル名]
GROUP BY uri
ORDER BY max_time_taken DESC;

(例)BE担当へ共有して、改善シートに記入📌

他にも様々な監視・分析項目があると思いますが、とりあえず上記を監視して、たとえば5xxエラーが発生した場合は以下のようなスプシを作成してBE担当と原因調査をしていました
もっとスマートな方法があると思うのですが、小規模なシステムで5xxエラーも少なかったので、少しずつ対応していました🐶

年月日_調査詳細 スプシ📌
実際のログなどを貼り付ける、詳細情報があるスプシです

シート名 項目名 備考
調査サマリ インフラorアプリケーションエラーの切り分け ALBログにて、elb_status_code=target_status_codeの場合、ALBはアプリケーションから受け取ったエラーコードを返しているだけ=アプリケーションエラーと考えられる
調査サマリ アクセス元のIP ipinfoなどで調べたアクセス元のIPアドレス情報
調査サマリ リクエストの際に叩かれたURI
調査サマリ 上記URIを叩いた時の処理内容
調査サマリ エラーの発生原因
調査サマリ エラーを再発防止すべきか、静観で良さそうか
CloudFrontログ - CloudFrontのログを必要な範囲で貼り付けて、調査対象に色を塗る
ALBログ - ALBのログを必要な範囲で貼り付けて、調査対象に色を塗る
Appログ - Appのログ(CloudWatchのlog streamなど)を必要な範囲で貼り付けて、調査対象に色を塗る

改善シート スプシ📝
詳細スプシをいちいち見ているとめんどくさいので、こちらにまとめておきます

シート名 項目名 備考
調査記録 エラー発生日時
調査記録 エラー発生場所 どの機能で発生したかなど
調査記録 ステータス 調査中
保留(解決策あり)
保留(原因不明)
解決済
問題なし
などの、ステータス
調査記録 問題(要約)
調査記録 解決策(要約)
調査記録 備考
調査記録 詳細リンク 調査詳細スプシのリンク

編集後記📝

こんな地味な調査方法でいいのかなぁと思いつつも、ちゃんと記録していくことで、少しずつどんなエラーが発生しているかがわかるようになってきました🐝

エラーが複数回発生していても、すでに調査済みの特に直すほどでもないエラーであった場合は安心ですね♻️

また、我々のチームでは、CTO含め5名の少人数ですが、全員でQAを行なっていました🦁🦁
そもそもアプリケーションエラーが発生しないように、しっかりQAする重要性も感じました

New Relicなどのツールでスマートに調査する方法も学んでみたいと思っています...!!🙇‍♂️🙇‍♂️

Discussion