Spannerオートスケーラーの極意:静的プロビジョニングの呪縛を断ち切り、真の弾力性を手に入れる方法
こんにちは。チーフアーキテクトの私だ。
これまでのコードレビューや設計レビューで、こんな議論をしたことはないか?
- 「ブラックフライデーのトラフィック急増に備えて、処理ユニット(Processing Units)を多めに常時プロビジョニングしておこう」
- 「いや、コスト効率が悪すぎる。夜間のバッチ処理が終わるタイミングで手動スケールダウンする仕組みを作ろう」
……待ってほしい。Cloud Spannerを「ただのリレーショナルデータベース」として扱い、人間の手やガバガバなスクリプトでキャパシティをいじっているうらやましいチームは、今すぐその手を止めたまえ。
Cloud Spannerの本質は、無制限のスケールと強整合性を両立させた「分散システムの最高峰」にある。そして、そのポテンシャルを極限まで引き出す鍵が 「Spannerオートスケーラー」 だ。
今回は、実務の現場で頭を悩ませるオートスケーリングの設計思想、推奨アーキテクチャ、そして踏み込みがちな「アンチパターン」について、容赦なくロジカルに解説しよう。
—
1. なぜ「公式マネージド機能」と「オートスケーラー」を混同してはいけないのか
まず大前提として、Cloud Spannerにはストレージの自動拡張(Storage Auto-Rebalancing)という偉大な機能が標準備わっている。データ量が増えれば、スプリットが自動的に分割され、ストレージ容量は勝手にスケールする。
しかし、「コンピューティング(処理ユニット / ノード)」 の自動スケーリングは、Google Cloudが提供するオープンソースの「Spanner Autoscaler(通常、Cloud FunctionsやCloud Runで動くリファレンス実装)」などを組み合わせて構築する必要がある。
なぜマネージドで最初からCPUの自動スケールが入っていないのか?
理由はシンプルだ。「データベースのスケールアップ・ダウンは、レイテンシとスループットに不可逆な影響を与える重い処理であり、ビジネスロジックに応じた戦略が求められるから」 だ。
これを理解せず、ただCPU使用率だけで機械的にスケールさせると、本番障害のトリガーになり得る。ここから先で、その「正しい設計と実務知見」を叩き込む。
—
2. 推奨アーキテクチャ:イベント駆動型オートスケーラーの全貌
実務で耐えうる堅牢なオートスケーラーは、以下のコンポーネントで構成されるべきだ。
[Cloud Spanner Metrics]
│ (High CPU / Storage)
▼
[Cloud Monitoring Alert / Pub/Sub]
│
▼
[Cloud Run / Cloud Functions (Autoscaler Engine)]
│
├─► [Spanner Admin API (Set Processing Units)]
└─► [Cloud Logging / BigQuery (Audit & Metrics)]
アーキテクチャの急所:スケールアップの「俊敏性」とスケールダウンの「慎重さ」
オートスケーラーを実装する際、エンジニアが最も陥りやすい罠が 「スケールアップとスケールダウンに同じ閾値とクールダウンを適用すること」 だ。これは致命傷になる。
実務で正解とされるポリシーは以下の通りだ。
1. スケールアップ(迅速・アグレッシブ)
- トリガー: 高負荷(例: 過去5分間の平均CPU使用率が 65% を超えた場合)。
- アクション: 即座に処理ユニットを追加する。
- 理由: スパイク時にモタモタしていると、プッシュバック(`RESOURCE_EXHAUSTED`)が発生し、上流のアプリケーションが雪崩式に死ぬ。
2. スケールダウン(遅延・保守的)
- トリガー: 低負荷(例: 過去30分間の平均CPU使用率が 30% を下回った場合)。
- アクション: 段階的に処理ユニットを減らす。
- 理由: 夜間の一時的なトラフィック低下で即座にスケールダウンすると、次の瞬間きたバッチや微小なスパイクで再びキャパシティ不足に陥り、「スケールアップとダウンの無限ループ(フラッピング)」 を引き起こす。これはSpannerの内部ノードにとっても負荷が高い。
—
3. 実装の急所:コードレビューでチェックすべき3つのポイント
世の中に転がっているサンプルコードをそのまま本番に投入するな。プロのエンジニアなら、以下の3点を作り込むこと。
① 処理ユニットの「刻み(Step)」を意識せよ
Spannerの処理ユニットは、100 PU未満の場合は10 PU刻み、100〜1,000 PUの間は100 PU刻み、1,000 PU以上は1,000 PU刻みで変更する必要がある。この制約を無視したAPIリクエストは容赦なく弾かれる。
処理ユニットの増減ロジックの例(概念コード)
def calculate_next_units(current_pu: int, target_pu: int) -> int:
# 適切なステップサイズに丸める関数
if current_pu < 100:
# 10刻み
return max(10, min(100, round(target_pu / 10) 10))
elif current_pu < 1000:
# 100刻み
return round(target_pu / 100) 100
else:
# 1000刻み
return round(target_pu / 1000) 1000
② スケジュール連携(プリスケーリング)の導入
オートスケーラーを「リアクティブ(事後対応)」だけに頼るな。
「毎朝9時のバッチ処理開始」「毎晩20時のキャンペーン告知」など、ビジネスイベントがあらかじめ分かっている場合は、Cloud Schedulerから事前にインスタンスをスケールアップ(プリスケーリング)させろ。
負荷が上がってからオートスケーラーが反応するまでの数分間の遅延すら、超高負荷システムでは致命傷になり得る。
③ 「高優先度CPU使用率(High Priority CPU)」を見ろ
Spannerのモニタリングには「CPU使用率」のほかに「高優先度CPU使用率(High Priority CPU Utilization)」が存在する。
オートスケーラーが監視すべきは、通常のクエリ処理だけでなくトランザクションのコミットや読み取り/書き込みの調整に直結する「高優先度CPU使用率」だ。ここが70%を超えたら、どんな手を使ってもスケールアップさせなければならない。
—
4. パフォーマンスとコストのトレードオフ:チーフアーキテクトからの戒め
最後に、コストの話をしよう。
「オートスケーラーを導入すれば、夜間はミニマムにしてコストが激減するはずだ」――甘い。
Spannerをスケールダウンさせることには、隠れた代償がある。
インスタンスの処理ユニットを削減すると、内部のスプリット(データの分割単位)の再配置や、キャッシュサイズの縮小が発生する。特に大規模なインスタンスで頻繁なスケールダウン・アップを繰り返すと、内部のキャッシュヒット率が低下し、一時的にレイテンシがスパイクすることがある。
【私の設計指針】
- 開発・ステージング環境: コスト最適化を最優先。夜間は最小構成(10 PU)まで落とし、休日は停止(または極小)にするオートスケーラーを入れろ。
- 本番環境: スプレッドシートの数ペンスのコスト削減のために、本番のレイテンシを人質にとるな。本番のオートスケーラーは「ボトムライン(下限値)」を高めに設定し、急激なトラフィック増に対する「保険」として機能させよ。下限を絞りすぎるな。
—
結び
Spannerオートスケーラーは、単なる「便利ツール」ではない。それは、クラウドネイティブなデータベース運用における「攻めと守りの境界線」だ。
インフラのキャパシティプランニングという不毛な夜間作業からエンジニアを解放し、ビジネスの成長曲線とデータベースのスケールを完全に一致させる。これこそが、我々テクニカルリードが目指すべきモダンなアーキテクチャである。
さあ、君の設計書にある静的なインスタンス定義を今すぐ書き換えに行こう。
コメント