Cloud Runでのセッション管理 - 実践的なアプローチ比較とベストプラクティス
はじめに
「Cloud Runでセッション管理するなら、どの方式が最適なんだろう?」
業務でGCPを初めて採用することになり、この疑問にぶつかりました。
Cloud Runは「ステートレスかつリクエスト駆動で自動スケールするマネージド実行環境」という特性があります。そのため、従来のサーバーベースアプリケーションとは異なるセッション管理の考え方が必要です。
筆者はAWS(EC2/Fargate)での設計・運用経験はありますが、Cloud Runは業務利用が初めてです。そこで公式ドキュメントを参考にしながら、要件適合性と運用性の観点で整理してみました。
本記事では、GCP初心者やAWSからの移行を検討している方に向けて、Cloud Runでのセッション管理アプローチを比較し、運用しやすさ・可用性・性能のバランスが取れたベストプラクティスをご紹介します。
本記事は現在進行形の技術検討段階での知見をまとめたものです。実装は含まれていないため、実際の導入時は各サービスの最新ドキュメントを必ずご確認ください。
また、今回は汎用的な会員サイトの構築を想定していますが、要件によっては別のアプローチが最適な場合もあります。
なぜCloud Runではセッション管理を見直す必要があるのか?
Cloud Runの特性を理解しよう
Cloud Runは需要に応じて自動でインスタンスを増減し、アイドル時はゼロまで縮退するステートレス実行環境です。
これまでのサーバーベースの考え方:
- サーバーA、B、Cが常時稼働
- 各サーバーのメモリでセッションを保持
- ユーザーは特定のサーバーに紐づけ(スティッキーセッション)
Cloud Runの場合:
- コンテナA、B、Cが動的に起動・停止
- インスタンス間でリクエストが分散される
- ローカルメモリやファイルでのセッション保持は不可能
外部ストアが必須な理由
リクエストは複数のインスタンス間で負荷分散されるため、セッション情報は全インスタンスから一貫して参照できる外部ストアに保存する必要があります。
この外部化により、Cloud Runの自動スケールと低運用負荷のメリットを活かしつつ、会員サイトの要件(ログイン維持・延長・失効)に対応できます。
セッション管理アプローチの比較表
まずは、主要なアプローチを一覧で比較してみましょう。
アプローチ | 実装の複雑度 | パフォーマンス | コスト | 主な特徴 |
---|---|---|---|---|
Redis/Valkey(Memorystore) | 中 | 高 | 中 | TTL制御が簡単、高速アクセス |
RDB(Cloud SQL) | 中 | 低〜中 | 低〜中 | 永続性に優れるが、セッション用途には不向き |
JWTのみ | 低 | 高 | 低 | 拡張性は高いが、即時失効が困難 |
JWT + Memorystore | 中〜高 | 高 | 中 |
バランスが良く、最も実用的 ![]() |
アプローチ1: Memorystore(Redis/Valkey)を使う
なぜMemorystoreが有効なのか?
Cloud RunからMemorystore(Redis/Valkey)へは、Direct VPC egressでプライベート接続できます。同一リージョンに配置することで、低レイテンシでセッション検証とTTL更新を実現できます。
実装のポイント
セッションの保持と延長
// セッション作成
await redis.hSet(`session:${sid}`, {
userId,
loginTime: Date.now().toString()
});
await redis.expire(`session:${sid}`, 1800); // 30分のTTL設定
// アクティビティに応じた延長(スライディングセッション)
await redis.expire(`session:${sid}`, 1800); // TTLをリセット
// 即時失効(ログアウト)
await redis.del(`session:${sid}`);
Terraform設定例
resource "google_redis_instance" "session_store" {
name = "session-redis"
tier = "STANDARD_HA" # 高可用性構成
memory_size_gb = 4
region = "asia-northeast1"
redis_configs = {
maxmemory-policy = "allkeys-lru" # メモリ不足時の削除戦略
timeout = "0" # 接続タイムアウト
}
}
Memorystore for Valkeyという選択肢
ValkeyはRedis由来のOSSで、Memorystore for Valkeyはクラスターモードやシャード/レプリカ構成をマネージドで提供します。高スループット・高可用性が必要な場合に適しています。(また、Valkey自体はRedisをフォークして参考にしたOSSなので、Redisよりコストが下がる可能性が高いです。)
アプローチ2: Cloud SQL(RDB)を使う
RDBの特徴と限界
Cloud SQLは永続性とトランザクション整合性に優れていますが、セッション管理においては以下の課題があります:
- 短寿命・高頻度アクセスに最適化されていない
- TTL制御がRedisほど直感的でない
- セッション用途には相対的にオーバースペック
推奨される使い分け
会員プロファイル(永続データ) → Cloud SQL
セッション状態(短寿命データ) → Memorystore
この分離により、それぞれの特性を活かした設計が可能です。
アプローチ3: JWTのみ
メリットとデメリット
メリット:
- Cloud Runのステートレス性と高い親和性
- 外部ストア不要で実装がシンプル
- 高いスケーラビリティ
デメリット:
- 即座のログアウト(失効)が困難
- 権限変更の即時反映に外部台帳が必要
- 結局は外部ストアとの併用が必要になりがち
これらの特性により、単体では即時的な権限の取り消し等が不可能に近いです。
アプローチ4: ハイブリッド(JWT + Memorystore)⭐ おすすめ
なぜハイブリッドが最適なのか?
役割分担が明確:
- JWT: 「会員証」として最小限の情報(ユーザーID、セッションID)を保持
- Memorystore: セッションの有効性、TTL、ブラックリストを集中管理
この組み合わせにより、Cloud Runのステートレス性と集中制御のしやすさを両立できます。
実装フロー
1. ブラウザがJWTを送信
2. Cloud Run(API)でJWT署名を検証
3. Direct VPC egressでMemorystoreに接続
4. session:{sid}の存在確認とTTL延長
5. 有効なら処理続行、無効なら401エラー
実装例
// JWT検証 + セッション確認
const payload = jwt.verify(token, secretKey);
const sessionExists = await redis.exists(`session:${payload.sid}`);
if (!sessionExists) {
return res.status(401).json({ error: 'Session expired' });
}
// アクティビティに応じてTTL延長
await redis.expire(`session:${payload.sid}`, 1800);
ベストプラクティス
1. 設計原則
- ステートレス前提: インスタンスローカルの状態に依存しない
- 役割分離: 永続データはRDB、セッションはMemorystore
- プライベート接続: Direct VPC egressで安全な通信
2. セッション設計
// セッションキーの構造例
const sessionKey = `session:${sessionId}`;
const sessionData = {
userId: '12345',
loginTime: Date.now(),
lastActivity: Date.now(),
// 必要最小限の情報のみ
};
3. セキュリティ対策
- JWT署名鍵の適切な保護
- アクセストークンの短命化
- サーバー側でのセッション有効性二重チェック
4. 運用・監視
主要な監視指標:
- Cloud Runのレイテンシ・エラー率
- Memorystoreの応答時間・失敗率
- セッション作成/延長/失効の頻度
まとめ
汎用的な会員サイトにおけるCloud Runでのセッション管理では、JWT + Memorystore のハイブリッド構成が最もバランスの取れた解決策と思われます。
この構成の利点
✅ 実装の容易性: 標準的なパターンで実装可能
✅ 高い可用性: GCPマネージドサービスの恩恵
✅ 優秀なパフォーマンス: Redisの高速性を活用
✅ 運用しやすさ: Cloud Runの特性を損なわない設計
皆さんの環境や要件に応じて、ぜひ参考にしていただければと思います!