FastlyのRealtime Log Streamingで苦労したあれこれ
これはなんですか
- FastlyのRealtime Log Streaming機能を使って試行錯誤した際に知った細かいTipsを書きます。
- 以下のサンプルが広いユースケースをカバーしているので、これを読み解いて必要な要素をロギングすることをお勧めします。
やりたかったこと
- ワイルドカード表記のドメインでCDN Serviceをホストしています。
- ex
*.myendpoint.example.com
- ex
- ワイルドカードに当てはまるサブドメインごとにアクセスログを集計したいと考えています。
- アクセス数
- データ転送量
- Fastly Image Optimizer (IO) リクエスト数
前提
FastlyのLog Streamingによるログの記録はベストエフォートです。あくまで参考値である点に注意しましょう。
About Fastly's real-time log streaming features | Fastly Documentation
BigQueryにFastlyのアクセスログを転送する
基本的にはドキュメントの記載通りに設定すれば問題ありません。
About Fastly's real-time log streaming features | Fastly Documentation
TerraformでIAM Impersonation(アカウントに成り代わる権限を振ることで、キーを使わずに認証を通す方式)方式の設定をする際は、次のようにaccount_name
にサービスアカウント名(サービスアカウントを表すメールアドレスのユーザー名部分)を設定します。
Configuring Google IAM service account impersonation to avoid storing keys on Fastly logging | Fastly Documentation
fastly_service_vcl | Resources | fastly/fastly | Terraform | Terraform Registry
logging_bigquery {
name = "logging_to_bq_sample"
project_id = "your-gcp-project-id"
# 'your-service-account-name@your-gcp-project-id.iam.gserviceaccount.com' のユーザー名部分
account_name = "your-service-account-name"
dataset = "your_bq_dataset_name"
table = "your_bq_table_name"
format = "..."
}
Fastly Image Optimizerによる画像変換の有無を記録する
Fastlyのサポートに問い合わせました。結果、レスポンスヘッダのFastly-Stats
を使用すればよいことがわかりました。
Fastly-Stats | Fastly Documentation
レスポンスヘッダはVCL上でresp.http.{NAME}
という変数でアクセスできるため、次のようにログフォーマットを設定しました。
{""fastly_is_transformed_by_io":"} if(resp.http.Fastly-Stats ~ "io=1", "true", "false") {", "}
データ転送量集計用のメトリクスを記録する
こちらもFastlyのサポートに問い合わせました。Websocketを使わない場合は、次の2つの変数を使って上りと下りのデータ転送量を計算できます。
-
bereq.bytes_written
- バックエンドリクエスト全体のサイズ
-
resp.bytes_written
- クライアントへのレスポンス全体のサイズ
Shielding設定時の注意点
FastlyにはShielding(シールド)という機能があります。
Shielding | Fastly Documentation
FastlyはキャッシュサーバーグループをPOP(Point of Presence)という単位で管理しています。Shieldingでは、指定したPOPがShield POPとなり、世界中のPOP(Edge POP)からのオリジンへのリクエストをShield POPが中継します。これにより、オリジンへのリクエスト回数を減らすことができます。
ただし、Shieldingを有効にするとEdge POPとShield POPの両方で同じVCLが実行されるため、ヘッダの操作やログ設定に注意が必要です。
また、シールド間通信はFastlyの課金対象となるため、ログからデータ転送量を計算する際にはシールド間通信の有無を考慮する必要があります。
ログをEdge POPでのみ記録する
Fastly VCLにはリクエストが通過したキャッシュサーバーの数を記録する変数として、fastly.ff.visits_this_service
があります。
fastly.ff.visits_this_service | Fastly Documentation
この変数を使って、vcl_log
サブルーチンでログを記録する条件を記述します。
vcl_log | Fastly Documentation
if (fastly.ff.visits_this_service == 0) {
log "....";
}
こうすることで、シールド間通信が走った際にEdge POPでのみログを記録できます。
なお、Realtime Log StreamingにはCondition(条件)という設定が存在するので、そちらに条件式を書くことでも同様に設定できます。以下はTerraformの設定例です。
condition {
name = "log_only_on_edge"
statement = "fastly.ff.visits_this_service == 0"
type = "RESPONSE"
}
logging_bigquery {
name = "log_to_bq"
account_name = "{{サービスアカウント名}}"
project_id = "{{Google CloudプロジェクトID}}"
dataset = "{{BQデータセット名}}"
table = "{{BQテーブル名}}"
response_condition = "log_only_on_edge"
format = "{{ログフォーマット}}"
}
クライアントへのレスポンスから特定のヘッダを落とす
Shieldingが有効な場合、VCLはEdge POPとShield POPの両方で動作するため、vcl_deliver
サブルーチンで単純にunset resp.http.{NAME}
と書いてレスポンスヘッダを落とすと、Shield POPの時点でレスポンスヘッダが落ちてしまいます。Edge POPはShield POPのレスポンスを元に処理をするため、resp.http.{NAME}
が使えなくなります。
レスポンスヘッダの値を取り回しつつ、クライアントへのレスポンスからのみ除外するには、fastly.ff.visits_this_service
変数を使ってEdge POPの場合のみレスポンスヘッダを削除します。
# resp.http.log-origin の値を取り回した後にレスポンスから落とす
if (fastly.ff.visits_this_service == 0) {
unset resp.http.log-origin;
}
シールド間通信の有無を記録する
Edge POPとShield POP間のシールド間通信のデータ転送量はFastlyの課金対象です。したがって、ログからデータ転送量を集計する際には、シールド間通信の有無も記録する必要があります。シールド間通信の記録方法は、冒頭で紹介したサンプルがカバーしています。
Copy of Comprehensive logging, one value per line - Fastly Fiddle
このサンプルは様々なログを記録しているため、シールド間通信の記録に必要な要素のみ確認してみます。シールド間通信の有無は fastly_is_shield
フィールドで記録されています。vcl_log
サブルーチン内の該当箇所を抜粋します。
{""fastly_is_shield":"} if(req.http.log-origin:shield == server.datacenter, "true", "false") {", "}
このフィールドは、「リクエストがEdge POPを経由せず、直接Shield POPに到達した」場合にtrue
となります。この判定には、log-origin:shield
というカスタムヘッダとserver.datacenter
変数を使っています。server.datacenter
はそのリクエストを処理するPOPのIDを表現します。
log-origin:shield
カスタムヘッダがどのように定義されるかを追いかけます。vcl_deliver
サブルーチンを見ると、次のようにlog-origin
レスポンスヘッダの値をlog-origin
リクエストヘッダにコピーしています。
if (resp.http.log-origin) {
set req.http.log-origin = resp.http.log-origin;
}
先ほど出てきたlog-origin:shield
という表記は、:
演算子を使ってlog-origin
ヘッダのshield
サブフィールドにアクセスしていることを意味しています。したがって、log-origin
をコピーすればサブフィールドであるshield
の値もコピーできます。
また、Fastly VCLではオリジンへのリクエスト以降、リクエストヘッダ(req.http.{NAME}
)の値は意味を持ちません。その性質を利用して、後続のサブルーチン(vcl_deliver
,vcl_log
)で変数のように使うことが多くあります。
resp.http.log-origin
レスポンスヘッダを追いかけると、vcl_fetch
サブルーチンでberesp.http.log-origin
にアクセスしていることがわかります。
if (req.backend.is_origin) {
set beresp.http.log-origin:shield = server.datacenter;
}
beresp
変数は後続のバックエンドから得たレスポンスを格納する変数です。vcl_fetch
サブルーチンでのみ書き込みが可能で、その結果は後続のvcl_deliver
、vcl_log
サブルーチンでresp
変数として利用できます。
Variables in VCL | Fastly Documentation
vcl_fetch
サブルーチンの処理を見ると、req.backend.is_origin
変数がtrue
の場合にserver.datacenter
変数の値を詰めています。これは、オリジンへのリクエストを行った場合に、VCLを実行しているPOPのIDを記録することを意味しています。
ここまでの内容を踏まえて、シールド間通信がある場合とない場合でfastly_is_shield
の値がどうなるかを考えます。まず、リクエストを直接Shield POPで受け取ってオリジンまでリクエストを行う場合の処理順序を見ます。
- Shield POP上でVCLにしたがってリクエストを処理し、
vcl_fetch
サブルーチンに到達する。 - Shield POPはオリジンをバックエンドに持つため、
if(req.backend.is_origin) {...}
内の処理を実行し、log-origin:shield
ヘッダにserver.datacenter
変数の値を詰める。 -
log-origin
ヘッダは値を持つので、Shield POPはvcl_deliver
サブルーチンのif (resp.http.log-origin) {...}
を実行し、リクエストヘッダにlog-origin
ヘッダを設定する。 -
vcl_log
サブルーチンでlog-origin:shield
リクエストヘッダの値を比較してログとして記録する。Shield POP上でのみ処理を行っているためserver.datacenter
の値は一致し、fastly_is_shield
はtrue
となる。
次に、Edge POPからShield POPを経由してオリジンまでリクエストを行う場合の処理順序を見てみます。
- Edge POP上でVCLにしたがってリクエストを処理する。
- Edge POPからShield POPにリクエストを行う。
- Shield POPでも同様にVCLにしたがってリクエストを処理する。前述の通り、Shield POPでは
resp.http.log-origin
が値を持つので、Edge POPへのレスポンスにはlog-origin
ヘッダが含まれる。
- Shield POPでも同様にVCLにしたがってリクエストを処理する。前述の通り、Shield POPでは
- Edge POP上で
vcl_fetch
サブルーチンに到達する。 - Edge POPのバックエンドはShield POPであるため、Edge POPは
if(req.backend.is_origin) {...}
を実行せず、Shield POPが返したberesp.http.log-origin
の値を保持する。 -
log-origin
ヘッダは値を持つので、Edge POPはvcl_deliver
サブルーチンでif (resp.http.log-origin) {...}
を実行し、リクエストヘッダにlog-origin
ヘッダを設定する。 -
vcl_log
サブルーチンでlog-origin:shield
リクエストヘッダの値を比較してログとして記録する。Edge POPとShield POPは異なるserver.datacenter
の値を持つのでfastly_is_shield
はfalse
となる。
このように、それぞれの場合に応じてfastly_is_shield
が適切な値になることがわかります。
トラブルシュート用のAPIエンドポイント
Log Streamingがうまく動いているかどうかを確認するためのAPIがあります。トラブルシュートする際に便利です。
Setting up remote log streaming | Fastly Documentation
参考
そのほか、調査した中で読んだページを挙げます。
Discussion