本記事は 2026年8月時点 の公式ドキュメントに基づいて執筆しています。 Cloud Run の仕様・料金体系は更新されるため、 実際に設定を選ぶ際は必ず Cloud Run 公式ドキュメント と 料金ページ で最新情報をご確認ください。 本記事に出てくる金額はすべて概算であり、 単価は変動します。
Cloud Run でインメモリキャッシュを持つサービスを作るとき、 課金モードをどう設定していますか。
自分は先日、 題材として ある SaaS の API を中継するサーバーを組んでいて、 「キャッシュをメモリに保持し続けたいから instance-based(常時CPU割り当て)にする」 という判断をしました。
数週間後、 コストを見直していて気づきました。 この理由づけは誤りでした。 キャッシュがメモリに残るかどうかと、 課金モードは関係がありません。 キャッシュの保持を担保しているのは最小インスタンス数(min instances)のほうで、 課金モードを既定に戻してもキャッシュは消えません。
自分の構成では、 この誤解を抱えたまま月 40 ドル前後を払い続けていました。 しかも 「これは必須の費用だ」と思い込んでいたので、 削れる可能性を検討したことすらありませんでした。 金額そのものは大きくありませんが、 理由が間違っている状態で運用に入るのが怖い と感じました。 同じ勘違いをしている人がいるかもしれないと思うので、 何をどう取り違えていたのかを整理します。
Cloud Run には「別々の設定」が2つある
まず結論から書きます。 混同されやすいこの2つは、 担保しているものがまったく違います。
| 設定 | 何を担保するか | 既定値 |
|---|---|---|
| 最小インスタンス数(min instances) | インスタンスが起動したままになる。 つまり プロセスのメモリが保持される | 0 |
| 課金モード(billing settings) | リクエストを処理していない間も CPU が割り当てられる かどうか | request-based |
言い換えると、 最小インスタンス数は「箱が残るか」を決め、 課金モードは「その箱の中でコードが動き続けるか」を決めています。
比喩で言えば、 部屋を借り続けること(最小インスタンス数) と、 その部屋の電気をつけっぱなしにすること(課金モード) の違いだと思っています。 電気を消しても、 部屋に置いた荷物(キャッシュ)は消えません。 消えるのは「部屋の中で作業が進む」ことだけです。
自分は「荷物を置いておきたいから電気をつけっぱなしにする」という、 かみ合っていない理由づけをしていました。
公式ドキュメントの記述はこうなっています。
Request-based billing: CPU is only allocated during request processing
Instance-based billing: CPU is allocated for the entire container instance lifecycle
書いてあるのは CPU の割り当て(allocated) についてだけです。 メモリの保持については何も言っていません。 メモリはインスタンスが生きている限り保持されるので、 そちらは最小インスタンス数の管轄です。
自分が書いていた設計
上流の SaaS API にはレート制限(1時間あたり 5,000 リクエスト)があり、 中継サーバーはリクエストのたびに全ユーザー情報を引く必要がありました。 毎回引いていたら即座に使い切ってしまうので、 起動時に全件を取得してメモリに持ち、 setInterval で 30 分ごとに取り直す設計にしました。
そして設計書の課金モードの欄に、 こう書きました。
課金モード: instance-based(常時CPU割り当て)
理由: User キャッシュを保持するため / 定期チェックをリクエスト外でも動かすため
理由を2つ挙げていますが、 「User キャッシュを保持するため」のほうが誤りです。 キャッシュの保持は最小インスタンス数の管轄で、 課金モードは関係ありません。
整理すると、 自分の頭の中はこうなっていました。
キャッシュをメモリに保持したい
→ instance-based にする ← ここが誤り
正しくはこうです。
キャッシュをメモリに保持したい
→ 最小インスタンス数を 1 にする
リクエストが来ていない間もコードを動かしたい
→ instance-based にする
誤解の本当の害は、 見直しの入口をふさぐこと
そもそも、 この設計自体が最善ではありませんでした。 更新処理をサービスの外に出してしまえば、 定期タイマーは要らなくなり、 instance-based も要らなくなります。
ところが自分は 「キャッシュを持っている以上 instance-based は必須」 だと思い込んでいました。 必須だと思っている設定は、 コスト削減の候補リストにそもそも載りません。 だから設計を見直すという発想が、 そもそも出てきませんでした。
この誤解の害は、 間違った設定をさせることではなく、 設定を見直す入口をふさぐこと でした。
request-based にすると何が失われるのか
失われないもの:
- インスタンスの常駐(最小インスタンス数が 1 なら常駐したまま)
- プロセスのメモリの中身(キャッシュは消えない)
- コールドスタートの回避
失われるもの:
- リクエストを処理していない間に進むコード
-
setInterval/setTimeoutのコールバック - レスポンスを返したあとに続く非同期処理(いわゆる fire-and-forget)
- バックグラウンドのポーリング
-
このうち、 レスポンス返却後の非同期処理が止まる点は見落としやすいポイントでした。 「古いキャッシュで即座に応答しつつ、 裏で取り直す」という stale-while-revalidate のような実装は、 まさにこのパターンに当てはまります。 request-based では、 レスポンスを返した瞬間に裏の処理が進まなくなる可能性があります。
どちらを選ぶかの判断手順
整理してみて、判断手順を考えてみました。 2つの設定を分けて、 順に考えます。
1: 最小インスタンス数を決める
| 状況 | 設定 |
|---|---|
| コールドスタートを避けたい / メモリ上の状態を保ちたい | min = 1 以上 |
| リクエストが散発的で、 起動の遅さを許容できる | min = 0 |
キャッシュを持ちたいかどうかは、 ここで決まります。 課金モードの出番ではありません。
2: リクエスト外で動かす処理があるか確認する
コードを見て、 次のものがあるか探します。
-
setInterval/setTimeoutによる定期処理 - レスポンス返却後も続く非同期処理
- バックグラウンドのポーリングやキュー消費
1つもなければ、 課金モードは既定(request-based)のままでよいです。
あった場合は、 次を考えます。
3: その処理は本当にリクエスト外である必要があるか
自分の場合、 定期タイマーがやっていたのは「キャッシュを最新に保つ」ことでした。 これは以下のような代替手段があります。
| 代替案 | 内容 |
|---|---|
| リクエストのタイミングで更新する | リクエストが来たときにTTLを確認し、 古ければ取り直す |
| 外部のスケジューラを使う | Cloud Scheduler から Cloud Run Job を起動し、 別プロセスで更新する |
つまり 「定期実行が必要」=「instance-based が必要」ではありません。 Cloud Run のサービス内で定期実行しようとするから instance-based が要るのであって、 外に出せば要らなくなります。
自分は最終的に、 Cloud Scheduler と Cloud Run Job を使って更新処理をサービスの外へ出す設計に変えました。 この話は別記事に書きます。
コストの話
金額の感覚も書いておきます。 自分の構成(1 vCPU / 512 MiB / 最小インスタンス数 1 / 東京リージョン)を 料金計算ツール で試算したところ、 instance-based と request-based で月 40 ドル前後の差 になりました。
差が出る理由はシンプルで、 instance-based は「インスタンスが生きている間ずっと」CPU の課金対象になるのに対し、 request-based はリクエストを処理している時間が中心になるからです。 リクエストが1日に数十回しか来ないようなサービスだと、 この差はそのまま無駄になります。
逆に言えば、 トラフィックが常時ある程度あるサービスでは、 instance-based のほうが安くなることもあります。 リクエスト単位の課金がなく、 コンピュート単価も抑えられているためです。 公式のベストプラクティスにも、 トラフィックの性質によって使い分ける旨が書かれています。
- トラフィックが安定して継続的にある → instance-based が有利になりうる
- トラフィックが散発的・バースト的 → request-based が有利
自分のケースは完全に後者でした。 1日に数十リクエスト、 しかも朝の時間帯に集中するバッチ的な使われ方です。 それなのに 24 時間 CPU を確保していました。
まとめ
| 項目 | 結論 |
|---|---|
| キャッシュをメモリに保持したい | 最小インスタンス数を1以上にする。 課金モードは関係ない |
| リクエスト外でコードを動かしたい | instance-based が必要 |
| 定期実行がしたい | Cloud Run のサービス内でやるなら instance-based。 外部スケジューラに出せば不要 |
| どちらが安いか | トラフィック次第。 散発的なら request-based、 常時安定なら instance-based が有利になりうる |
自分の教訓としては、 設定を選ぶ前に「メモリの保持」と「CPUの割り当て」を分けて考える こと。 この2つを一緒くたにすると、 必要のない課金を「必要だ」と思い込んだまま運用に入ってしまいます。
そしてもう1つ。 突き詰めると 「なぜその設定にしたのか」を正確に言語化できていなかった のが根っこの原因でした。 自分の設計書やコードのコメントを見返して、 「この理由、 本当に合っているだろうか」と疑ってみると、 似たようなものが見つかるかもしれません。