CloudFrontのアクセスログをS3シンボリックリンクを利用してAthanaで効率的に解析してみた。(Lambdaレス版)

CloudFrontの標準のアクセスログ、Athenaで効率的な解析を実現するため、CloudShellを利用した簡単な加工を試みてみました。
2023.10.12

CloudFront のアクセスログを S3への保存する設定のみを実施していた環境で、 直近のアクセスログを対象とした、ログ解析を行う必要がありました。

Athenaによるフルスキャンにより発生する S3、Athena費用を抑制するため、 CloudShellを利用して、調査対象のアクセスログを反映したシンボリックファイルを用意。 Athenaのパーティション投影に対応した形式でS3に設置し、解析する機会がありましたので紹介させて頂きます。

S3にフラットに配置してしまったログも大丈夫!シンボリックリンクを利用してスキャン範囲を絞ってAthenaからクエリする

手順

CloudShell

  • CloudFrontのアクセスログ保管S3バケットのファイルリストより、過去一週間の日付に一致するファイルを抽出しました。
  • シンボリックファイル保存用のS3バケット、日付「YYYY/MM/DD」を含む状態で保存しました。
S3BUCKET='<ログ保管S3バケット>'
S3SYMLINK='<シンボリックファイル保管S3バケット>'

TMPDIR="/tmp/$?"
mkdir -p ${TMPDIR}
cd ${TMPDIR}

aws s3 ls s3://${S3BUCKET} --recursive | sed "s/^.* /s3:\/\/${S3BUCKET}\//g" > s3ls.txt

for i in {0..6}
do
  mkdir -p "${TMPDIR}/`date +%Y/%m/%d --date \"${i} day ago\"`"
  SYMLINKFILE="${TMPDIR}/`date +%Y/%m/%d --date \"${i} day ago\"`/symlink.txt"
  tmp=`date +%Y-%m-%d --date "${i} day ago"`
  cat s3ls.txt | grep -e "${tmp}-[0-9].\..*.gz$" > ${SYMLINKFILE}
done

aws s3 sync ${TMPDIR} s3://${S3SYMLINK} --exclude '*' --include '2[0-9][0-9][0-9]/*/symlink.txt'
  • ログ出力先のS3バケット、複数のディストリビューションで共有している場合、aws s3 ls <bucket> --recursive のS3パス指定を調整してご利用ください。
  • アクセスログのファイル数が多く CloudShellではメモリ不足となる場合には、メモリを多く搭載したEC2などの実行環境をご利用ください。

Athena

テーブル作成

INPUTFORMATとして「SymlinkTextInputFormat」。 LOCATION、TBLPROPERTIES は、シンボリックファイル保存先のS3バケットとしたテーブルを作成しました。

CREATE EXTERNAL TABLE IF NOT EXISTS cflogs_parted (
  `date` DATE,
  time STRING,
  location STRING,
  bytes BIGINT,
  requestip STRING,
  method STRING,
  host STRING,
  uri STRING,
  status INT,
  referrer STRING,
  useragent STRING,
  querystring STRING,
  cookie STRING,
  resulttype STRING,
  requestid STRING,
  hostheader STRING,
  requestprotocol STRING,
  requestbytes BIGINT,
  timetaken FLOAT,
  xforwardedfor STRING,
  sslprotocol STRING,
  sslcipher STRING,
  responseresulttype STRING,
  httpversion STRING,
  filestatus STRING,
  encryptedfields INT
)
PARTITIONED BY (day STRING)
ROW FORMAT DELIMITED 
FIELDS TERMINATED BY '\t'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.SymlinkTextInputFormat'
OUTPUTFORMAT  'org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat'
LOCATION 's3://<シンボリックファイル保管S3バケット>/'
TBLPROPERTIES
(
  'skip.header.line.count'='2', 
  "projection.enabled" = "true",
  "projection.day.type" = "date",
  "projection.day.range" = "2022/01/01,NOW",
  "projection.day.format" = "yyyy/MM/dd",
  "projection.day.interval" = "1",
  "projection.day.interval.unit" = "DAYS",
  "storage.location.template" = "s3://<シンボリックファイル保管S3バケット>/${day}"
)

クエリサンプル

スキャン範囲をパーティション「day」に限定したクエリ結果が得られました。

Athena結果サンプル

まとめ

CloudFront の標準アクセスログ、ファイル名にログ生成日時が含まれますが、 Athena のパーティション、パーティション投影で利用できる形式ではないため、Athenaを利用したログ解析時のフルスキャンを避けるためには 事前にLambdaなどを利用したリネームを行う必要がありました。

CloudFront が Amazon S3 バケットに保存する各ログファイルの名前には、次のファイル名形式が使用されます。/.YYYY-MM-DD-HH.unique-ID.gz

Amazon CloudFront デベロッパーガイド :ファイル名の形式

CloudFront のアクセスログ解析を稀に実施する必要があるが、 フルスキャンに伴う S3、Athenaのコスト抑制が望ましい、 Lambdaなど 管理するAWSリソースを増やしたくない場合、準備が容易な今回の手順をお試しください。

頻繁にCloudFrontのログ解析の必要がある場合や、アクセスログの長期保管要件のため シンボリックファイルの作成が難しい場合、 従来のアクセスログファイルのりネームを実施するLambda関数や、 要件によっては Glueの活用もご検討ください。

CloudFrontのアクセスログをパーティション射影を使ってコスパ良くAthenaでクエリーする仕組みをCDKで作った