はじめに
Cloud Run で GCS を扱う API を実装していたところ、同時アクセスが増えたタイミングで 429 エラーが頻発する事象に遭遇しました。
- Cloud Run は落ちていない
- CPU 使用率も低い
- それでも一部リクエストが 429 で失敗
結論から言うと、
Cloud Run の最大同時リクエスト数(concurrency)を 1 にしたら解消
しました。
本記事では、
- なぜ 429 が発生したのか
- なぜ concurrency=1 で直ったのか
- Cloud Run × GCS 構成での設計指針
を、実体験ベースでまとめます。
発生していた事象
構成(簡略)
- Cloud Run(Python)
- 各リクエストで GCS からファイル取得
- concurrency:デフォルト(80)
症状
- 同時アクセスが増えると 429 エラー
- 一部リクエストは成功、一部は失敗
- Cloud Run自体は正常稼働
ログを見ると、GCS アクセス部分で429 Too Many Requestsが発生していました。
最初に勘違いしていたこと
最初は、Cloud Run の制限?インスタンス数が足りない?CPU / メモリ不足?
を疑いました。
しかし実際には、制限に引っかかっていたのは Cloud Run ではなく GCS 側でした。
何が起きていたのか(原因)
Cloud Run の concurrency は
「1インスタンスが同時に処理するリクエスト数」 です。
当時の状態
- concurrency = 80
- 1インスタンスで最大 80 リクエスト同時処理
- 各リクエストが GCS にアクセス
つまり、
1インスタンスから同時に最大 80 件の GCS リクエスト
が飛ぶ構成でした。
その結果、
- GCS 側でスロットリング
-
429(Too Many Requests)発生
という流れです。
concurrency=1 にしたらなぜ直ったのか
concurrency を 1 に変更すると:
- 1インスタンス = 1リクエスト
- GCS アクセスが直列化
- 同時 GCS リクエスト数が大幅に減少
結果として、
- GCS 側のスロットリングが発生しなくなった
- 429 エラーが完全に解消
しました。
ポイント
Cloud Run の concurrency は、
下流サービス(GCS など)への負荷を調整するためのレバー
でもあります。
「CPU が空いているのに遅い」理由
このときのメトリクスは以下の状態でした。
- CPU 使用率:低い
- レイテンシ:高い
- エラー率:増加
これは典型的な
I/O バウンド × 高 concurrency の症状です。
GCS アクセスは:
- ネットワーク I/O
- レイテンシが不安定
- 同時数に弱い
ため、CPU 使用率は指標になりません。
concurrency=1 は悪なのか?
よくある誤解ですが、
concurrency=1= 非効率
ではありません。
Cloud Run はスケールアウトが速いため、
- concurrency を下げる
- インスタンス数で捌く
という設計は 正攻法 です。
特に以下のような処理では合理的です。
- GCS ファイル取得
- ファイル変換・加工
- 一時的なバッチ処理
同じ構成を作る人への設計指針
-
GCS を触るなら concurrency は必ず疑う
デフォルト(80)のままは危険です。
まず 1〜5 から始める
問題なければ徐々に上げる -
429 が出たら「下流」を疑う
429 は必ずしも
「自分のアプリが悪い」わけではありません。- GCS
- BigQuery
- 外部 API
下流サービスの同時接続制限が原因のことが多いです。
-
リトライより先に同時数を減らす
高 concurrency のままリトライ
→ さらに負荷増加になりがちです。
まずは 同時リクエスト数を減らす 方が効果的なケースがあります。
まとめ
Cloud Run × GCS で 429 エラーが発生
原因は 高 concurrency による同時 GCS アクセス
concurrency=1にしたら解消
Cloud Run は「設定を考えなくていい」サービスですが、
concurrency だけは例外だと感じました。
同じ構成でハマる人の参考になれば幸いです。