請求額を見たとき、私たちはまず、画面に表示している地図の利用が増えたのだと考えました。ところが、請求明細を分けて確認すると違いました。Google Maps Platformの利用料金は税込557,715円。その税抜費用の約96%を占めていたのは、公開ページの裏側で実行される従量課金APIでした。
約39,424件の公開詳細ページがあり、1ページの表示が最大8回の有料処理へ増幅される実装でした。機械的な大量アクセスが引き金になった可能性は極めて高い一方、当時のアクセスログからUser-AgentやIPを特定できていないため、特定のクローラーが原因だったとは断定していません。
請求の主因は、画面に見える地図ではなかった
税込557,715円のうち、従量課金APIの費用は税抜487,068円、実行回数は99,174回でした。一方、画面に表示する地図の費用は税抜16,707円でした。見た目から原因を推測せず、サービス別・SKU別の利用量と費用に分けたことで、止めるべき処理を特定できました。
費用の約93%は特定の6日間に集中していました。この偏りは、人による通常閲覧よりも、クローラーなどによる機械的な巡回と整合します。ただし、費用の集中だけをもってアクセス主体を決めつけないことも重要です。
1ページ表示が、裏側で8回の有料処理になっていた
利用者から見れば一つの詳細ページでも、サーバー側では複数の条件ごとに外部APIへ問い合わせていました。その結果、一度のページ表示が最大8回の有料処理へ増幅されていました。
99,174回を8回で割ると、約12,397回分の詳細ページ処理に相当します。数万件の公開URLを持つサイトでは、すべてのページを人が閲覧しなくても、機械的な巡回だけで短期間に大きな請求へ到達し得ます。
- 公開URLは何件あるか
- 1ページ表示で外部APIを何回呼ぶか
- 同じページを再訪したとき再課金されるか
- 全URLを一度巡回された場合の最大費用はいくらか
問題は「有料APIを使ったこと」ではなく、公開アクセスと直結したこと
有料APIの利用自体が問題だったわけではありません。公開ページを表示するだけでサーバー側の従量課金APIが実行され、その結果を保存・再利用せず、同じアクセスのたびに再実行する経路が外部へ開かれていたことが問題でした。
IP単位の短時間レート制限はありましたが、複数のIPを合算した日次上限や、Google Cloud側の低いクォータは設定されていませんでした。入口を制限していても、外部API側で何倍に増幅されるかまで見なければ、費用の上限にはなりません。
予算アラートは、費用を自動で止める装置ではない
予算アラートは異常を知らせるための仕組みで、設定額に達した時点で自動的にAPI利用や請求を止めるものではありません。請求情報には遅延もあるため、金額だけを見ていると急増を後から追うことになります。
停止に近い役割を持つのは、APIごとのクォータとアプリ側の回数上限です。予算通知、API利用量の通知、アプリ側の日次上限、Cloud側のクォータを別々の防御として重ねます。
Google Maps Platformの費用管理でも、予算通知とクォータは異なる役割として案内されています。
公開画面から有料処理を切り離す
対策では、公開ページの閲覧を契機に有料APIへ直接問い合わせる構造をやめました。必要なデータは管理された処理で取得し、公開側は保存済みの結果を読む形へ分けます。また、複数回に分かれていた問い合わせを可能な範囲でまとめました。
- 公開画面と外部API通信を分離する
- 取得結果を利用条件の範囲内で保存・再利用する
- 複数条件の問い合わせをまとめ、外部通信回数を減らす
- ブラウザ用とサーバー用のAPIキーを分離する
- キーごとに利用できるAPIと接続元を制限する
- アプリ側とCloud側の両方に分・日単位の上限を設ける
対策後は、Cloud Monitoringの利用量メトリクスで従量課金APIへのリクエスト数を継続的に確認しています。請求データ自体には反映までの遅延があるため、金額よりもリクエスト数そのものを指標にする方が実態を追いやすく、対策前と比べておおむね99%以上少ない件数で推移していること、同種の急増が再発していないことを確かめられます。
費用対策で、サービスの使い勝手まで壊さない
初動では、地図そのものも利用者が操作するまで表示しない方式へ変更しました。しかし主因は地図表示ではなく、その裏側で繰り返される別の有料処理でした。原因ではない機能まで止めると、費用は減ってもサービス価値を損ないます。
そこで、課金の中心となる経路の遮断、キー制限、クォータ、保存済みデータの利用は維持しながら、地図の表示方法は元に戻しました。緊急停止と恒久対策を分け、何を止めれば費用が止まり、何を残せば利用体験を守れるかを確認します。
公開前に、PVではなく最悪費用を試算する
月間PVの予測だけでは、クローラーによる全ページ巡回や、一つの処理内で増幅する外部通信を捉えられません。公開URL数、1ページ当たりの外部API回数、単価を掛け合わせ、想定外ではなく計算可能な最大値として確認します。
- 公開ページ1表示で発生する外部API通信を計測したか
- ループや条件分岐の中で通信回数が増えていないか
- 全公開URLを一度巡回された場合の費用を計算したか
- 同一条件の再取得を避ける仕組みがあるか
- IP単位だけでなくプロジェクト全体の日次上限があるか
- 予算通知とは別にAPIクォータを設定したか
- 異常時に止める処理と、維持する利用者向け機能を分けたか
- 請求・API使用量の急増を複数の連絡経路で検知できるか
発生した費用は、原因と対策を添えて相談する
従量課金APIの費用は通常の利用に対する正当な対価であり、Google側に自動的な返金の仕組みはありません。それでも、原因を特定し、技術的な再発防止策を完了させたうえで依頼すれば、初めてのインシデントについて一時的な費用調整(クレジット)を検討してもらえる余地はあります。相談先は請求先アカウントに紐づく課金サポートで、有料サポートプランに入っていなくても利用できます。
依頼文のサンプル(Google Cloud 課金サポート宛)
同じような急増に直面した方の参考になるよう、実際に使った依頼文の型を、個別の数値を置き換えられる形で残します。件名は「〇〇API 想定外の高額請求に関するクレジット申請」のように、対象APIと目的が分かる形にします。
- 請求先アカウントID・対象プロジェクト名を先頭に記載する
- ■発生した問題:発生期間、対象APIのみの費用、請求先アカウント全体の費用を数字で示す
- ■原因:制限が不十分だったキーや経路が、外部からの機械的なアクセスに晒されていたことを説明する
- ■実施済みの再発防止策:キーの分離、接続元・利用APIの制限、アプリ側とCloud側それぞれの回数上限、公開経路と有料APIの切り離しなど、実際に手を打った内容を箇条書きにする
- ■依頼内容:初めてのインシデントであり技術的な再発防止策を完了していることを添え、対象期間の異常利用分について一時的なクレジット(費用調整)を検討してもらえるよう依頼する
この経験から残す判断基準
公開ページと従量課金APIの間には、クローラーも通常利用者も通る入口があります。アクセス数だけを制限するのではなく、一つのアクセスが外部で何回の課金へ変換されるかを把握し、費用の最終上限をシステムとCloudの両側で持たせます。
従量課金APIを使うWeb制作では、「正常に表示できるか」と同じ重さで、「すべての公開URLを機械的に巡回されても支払えるか」を公開条件に含める必要があります。