Cloudflare の HTTP リクエストログを Amazon S3 に出力して Amazon Athena で分析してみた

Cloudflare のログを Amazon S3 経由で Amazon Athena で分析してみました。

ウィスキー、シガー、パイプをこよなく愛する大栗です。

最近 Cloudflare を触っています。運用を考えるとアクセス状況の把握が必要になります。標準で分析ダッシュボードも有るのですが、詳細ログを分析したくなることがあります。今回は HTTP リクエストのログを S3 へ出力して Amazon Athena で分析してみます。

Cloudflare Logpush

Cloudflare はログを確認するための手段を複数用意しています。今回はこの中の Logpush に焦点を当てます。なお Logpush は Enterprise Plan での提供となっています。

  • Logpush: 外部のストレージサービスへバッチでログをプッシュします
  • Logpull: REST API でリクエストログを取得します
  • Instant Logs: ダッシュボードかコマンドラインからトラフィックのライブストリームにアクセスできます

Logpush はログの各バッチを改行区切り JSON(いわゆる NDJSON)を gzip ファイルとして、通常 1 分以内に配信します。このバッチはファイルごとに 100,000 レコード以下になります。Logpush は 1 分に複数ファイルを配信する場合もあります。1

配信先としては外部のストレージやモニタリング、SIEM 等へ出力できます。標準では以下のサービスに対応しています。

  • Amazon S3
  • S3-compatible endpoints
  • Datadog
  • Google Cloud Storage
  • Microsoft Azure Blob Storage
  • New Relic
  • Splunk
  • Sumo Logic

上記以外に Google BigQuery へ出力するツールや、API を介してそれ以外を構成することが可能です。

やってみる

今回は Logpush で HTTP リクエストのログを Amazon S3 へ配信して、結果を Amazon Athena で分析してみようと思います。ざっくりと下図の様になります。Cloudflare から Logpush で Amazon S3 へ配信して、Amazon Athena を使って SQL で分析します。

Cloudflare Logpush

手順の前に Amazon S3 に保存用のバケットを準備しておきます。今回は東京リージョン(ap-northeast-1)に作成しました。

Cloudflare Logpush を構成する

まずは Cloudflare で Logpush の設定を行います。

各 Web サイトのページで、左のメニューから [Analytics]-[Logs] を開きます。

Cloudflare Logs

今回は HTTP リクエストのログを対象にするため、HTTP requestsを選択してNextをクリックします。

配信するログのフィールドを選択します。今回はデフォルトのフィールド2を使用します。そのままNextをクリックします。必要に応じてフィールドを追加しましょう。

ログの配信先を選択します。今回は Amazon S3 を選択して、Nextをクリックします。

Select a destination

配信先の情報を記載します。

  • Bucket path: / 形式で入力
  • Daily subfolders: Yes, automatically organize logs in daily subfolders クエリを楽にするためにログを毎日のサブフォルダに自動的に整理します
  • Encryption constraint in bucket policy: No

Enter destination information

情報を入力するとGrant Cloudflare access to upload files to your bucketの項に必要なバケットポリシーが表示されるのでコピーします。一番下のAmazon S3 bucket permissionsのリンクから Amazon S3 のコンソールを開きます。

Grant Cloudflare access to upload files to your bucket

Amazon S3 のコンソールでPermissions(アクセス許可)タブを開き、Bucket policy(バケットポリシー)のEdit(編集)をクリックします。

Amazon S3

Logpush で表示されたバケットポリシーを Policy の欄にペーストして、Save changesをクリックします。バケットポリシーが既存で必要なものがある場合は、内容をマージしてください。

Bucket Policy

Cloudflare の画面に戻り、Validate accessをクリックします。

S3 バケットの所有権を確認します。Logpush で書き込んだトークンの内容を確認するために、リンクにある S3 オブジェクトにアクセスします。

Openをクリックして内容を確認し、トークンの内容をコピーします。

S3 Object

コピーしたトークンの内容をOwnership tokenの欄にペーストして、Pushをクリックします。

Prove ownership

Amazon S3 への Logpush が構成されました。

サイトへアクセスするとログファイルが配信されます。

ファイルの中身な以下のようになっています。

{"ClientIP":"2001:0db8:0001:0002:0003:0004:0005:1234","ClientRequestHost":"cf.example.net","ClientRequestMethod":"GET","ClientRequestURI":"/","EdgeEndTimestamp":"2022-04-05T00:24:29Z","EdgeResponseBytes":4200,"EdgeResponseStatus":403,"EdgeStartTimestamp":"2022-04-05T00:24:29Z","RayID":"1234241e4a89ef86"}
{"ClientIP":"2001:0db8:0001:0002:0003:0004:0005:1234","ClientRequestHost":"cf.example.net","ClientRequestMethod":"GET","ClientRequestURI":"/cdn-cgi/bm/cv/669835187/api.js","EdgeEndTimestamp":"2022-04-05T00:24:29Z","EdgeResponseBytes":9690,"EdgeResponseStatus":200,"EdgeStartTimestamp":"2022-04-05T00:24:29Z","RayID":"1234241f9b4aef86"}
{"ClientIP":"2001:0db8:0001:0002:0003:0004:0005:1234","ClientRequestHost":"cf.example.net","ClientRequestMethod":"GET","ClientRequestURI":"/favicon.ico","EdgeEndTimestamp":"2022-04-05T00:24:29Z","EdgeResponseBytes":3858,"EdgeResponseStatus":403,"EdgeStartTimestamp":"2022-04-05T00:24:29Z","RayID":"123424219c95ef86"}
{"ClientIP":"2001:0db8:0001:0002:0003:0004:0005:1234","ClientRequestHost":"cf.example.net","ClientRequestMethod":"POST","ClientRequestURI":"/cdn-cgi/bm/cv/result?req_id=6f6e249ef86","EdgeEndTimestamp":"2022-04-05T00:24:29Z","EdgeResponseBytes":666,"EdgeResponseStatus":204,"EdgeStartTimestamp":"2022-04-05T00:24:29Z","RayID":"123424217c88ef86"}

Amazon Athena でテーブルを作成する

次に Amazon S3 のデータを Amazon Athena で分析できるようにテーブルを作成します。

Amazon Athena のコンソールでExplore the query editor(クエリエディタを詳しく確認する)をクリックします。

Amazon Athena

初回の利用でクエリ結果の保存場所を設定していない場合にはView settings(設定を表示)をクリックします。

result location

Manage(管理)をクリックします。

Query result and encryption settings

 クエリ結果を保存する S3 のパスを入力してます。ここでは Athena と同じリージョンのバケットを選択してください。そしてSave(保存)をクリックします。

この様にクエリ結果の場所が設定されました。次はEditor(エディタ)タブに遷移してテーブルを作成します。

Query result and encryption settings

まずデータベースを作成します。以下の SQL を実行して、cloudflare_logpushというデータベースを作成します。クエリエディタに SQL を入力したらRunをクリックします。

CREATE DATABASE IF NOT EXISTS cloudflare_logpush
  COMMENT 'Logs from Cloudflare logpush';

実行するとCompletedになり、Query successful.と表示されます。

テーブルを作成します。以下の SQL を実行して、cloudflare_http_request_logsというテーブルを作成します。クエリエディタに SQL を入力したらRunをクリックします。なお、SQL 中の <bucket_name> と <file_to_path> は Cloudflare の Logpush で設定した配信先の内容に置き換えて実行してください。ここでは Partition Projection を使用して、保存フォルダでパーティショニングしています。

CREATE EXTERNAL TABLE IF NOT EXISTS `cloudflare_logpush`.`cloudflare_http_request_logs` (
  `clientip` string,
  `clientrequesthost` string,
  `clientrequestmethod` string,
  `clientrequesturi` string,
  `edgeendtimestamp` string,
  `edgeresponsebytes` int,
  `edgeresponsestatus` string,
  `edgestarttimestamp` string,
  `rayid` string
)
PARTITIONED BY ( 
  `requestdate` string)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://<bucket_name>/<file_to_path>'
TBLPROPERTIES (
  'projection.enabled' = 'true',
  'projection.requestdate.type' = 'date',
  'projection.requestdate.range' = '19920101,NOW',
  'projection.requestdate.format' = 'yyyyMMdd',
  'projection.requestdate.interval' = '1',
  'projection.requestdate.interval.unit' = 'DAYS',
  'storage.location.template' = 's3://<bucket_name>/<file_to_path>/${requestdate}',
  'classification'='json', 
  'compressionType'='gzip', 
  'typeOfData'='file');

実行するとCompletedになり、Query successful.と表示されます。

作成したテーブルで問題なくデータを参照できるか確認します。Tablescloudflare_logpushの右にあるメニューからPreview Tableをクリックします。

テーブルが問題なく作成されていればプレビュー結果が表示されます。

Amazon Athena でログを分析する

ログを分析してみます。分析内容はドキュメントの内容を抜粋して jq から SQL に変更したもので実施してみます。

フィールドの集約

jq では以下のようにClientRequestURIが一致したものの数をカウントしてソートしています。

$ jq -r .ClientRequestURI logs.json | sort -n | uniq -c | sort -n | tail
2 /nginx-logo.png
2 /poweredby.png
2 /testagain
3 /favicon.ico
3 /testing
3 /testing123
6 /test
7 /testing1234
10 /cdn-cgi/nexp/dok3v=1613a3a185/cloudflare/rocket.js
54 /


SQL で同様の分析を行う場合は、以下のようになります。



<img src="https://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2022/04/24049bdcae252f1592dcdd8cb942e6d4.png" alt="" width="850" height="696" class="alignnone size-full wp-image-862026" />

#### フィールドのフィルタリング

jq では以下のように`ClientRequestURI`が一致したものの数をカウントしてソートしています。


$ jq 'select(.OriginResponseStatus == 502) | .ClientRequestURI' logs.json | sort -n | uniq -c | sort -n | tail
1 "/favicon.ico"
1 "/testing"
3 "/testing123"
6 "/test"
6 "/testing1234"
18 "/"

SQL で同様の分析を行う場合は、以下のようになります。実データではステータス502のデータが少なかったため 302で置き換えています。

SELECT COUNT("clientrequesturi") as count, "clientrequesturi"
FROM "cloudflare_http_request_logs"
WHERE "cloudflare_http_request_logs"."edgeresponsestatus" = '302'
GROUP BY "clientrequesturi"
ORDER BY count;

さいごに

Cloudflare のログは別のプラットフォームを使用して詳細に分析が可能になっています。今回は Amazon S3 にログを配信して、Amazon Athena で分析をしてみました。Amazon S3 の logpush はアクセスキーを発行する必要がなくセキュアに手軽に連携できます。

今回は Athena で分析してみましたが S3 にログがあればデータ処理は簡単なので、もうひと手間入れるだけで可視化ができるので Cloudflare の稼働状況ダッシュボードも作成できます。Cloudflare の本格的な運用を行うためには Logpush は必須の機能と言えるのでお試し頂ければと思います。


  1. 2020年前半以前は 5 分ごとにログを配信(Logpush v1)していました。現在は Logpush v2 がデフォルトの設定になっていますが、古い設定の場合は Logpush API を使用して v2 にアップグレードします。 
  2. HTTP requests のログのフィールドの詳細はHTTP requestsを参照してください。