ブログ一覧に戻る
DR運用設計SaaSβ リリース

β リリース前にこそ DR drill を完成させる — 「SLA なし宣言」と「責任放棄」は違う

はじめに

β リリース直前のメンテナンスモード設計を詰めている最中、「エッジ層 (Cloudflare Worker による停止) は β 中はオーバーキルではないか」と自問しました。SLA を規約で適用しないと宣言済みなのに、なぜそこまで作り込むのか。

たどり着いた結論は「β 前にしかできない drill があるからこそ、リリース前に完成させる」でした。本記事はその思考プロセスの記録です。

状況設定

私たちのメンテナンスモード設計は エッジ層 + アプリ層の 2 段防御 で組みました:

  • アプリ層: NestJS の Global Guard で feature flag テーブルを read し、READ-ONLY / kill-switch / FULL_STOP を判定
  • エッジ層: Cloudflare Worker + KV でエッジレベルの即停止。DB / Cloud Run が落ちていても 503 を返せる

アプリ層だけで対応できないシナリオは限定的です:

  • リージョン全停止: クラウドのリージョンが丸ごと停止 (極低確率)
  • Central DB 停止: 中央データベースの停止 (低確率)
  • セキュリティインシデント: 低確率、しかし発生時は致命的

β 期間 (1 年) で発生する期待値だけ見ると、アプリ層のみで十分そうに見えます。エッジ層を作らない選択肢は十分に合理的でした。

「SLA なし」と「責任放棄」は違う

利用規約で 「β 期間中は SLA を適用しません」 と定めました。これは法務的には「停止しても規約上の責任なし」を意味しますが、運用思想としては別問題です。

たとえばセキュリティインシデントを考えます:

  • 情報漏洩中、エッジレベルで全停止できないと 証跡記録だけが流れ続ける 状態になります
  • 規約で「責任なし」と定めていても、個人情報保護法 / GDPR の通知義務 は別途存在します
  • 「ベータだから止められません」では法的にも商業的にも擁護できません

ここで気づくのは、「規約で SLA なし宣言」=「停止経路を作らなくていい」ではない ということです。両者は別レイヤーの話で、規約が緩いほど技術的な責任は逆に重くなるとすら言えます。

β 前にしか実施できないドリル

もう一つの観点が、ドリル実施タイミングの非可逆性 です。

エッジ層が想定するシナリオ — DB 全停止のドリルを正しく検証するには、

# STG の Cloud SQL を意図的に止める
gcloud sql instances patch your-stg-central --activation-policy=NEVER

を実行して、本番に近い障害を発生させる必要があります。これを β リリース後にやると、次のような事態になります:

  • 在席している顧客のデータが失われる (RTO/RPO の検証中はバックアップ復旧経路を試している)
  • 顧客から問い合わせが殺到する
  • 復旧時間中の損失が発生する

β 前に同じ drill をやれば、次のように進められます:

  • 顧客は誰もいない (= 損失ゼロ)
  • 復旧ミスをしても誰にも迷惑がかからない
  • 「何分で復旧できるか」「手順書のどこが詰まったか」を時計を見ながら計測できる
  • 運用手順書に結果を追記する余裕がある

「失敗しても安全な環境で失敗してみる」のが drill の本質 であり、それができるのは β 前だけです。

DR drill の 4 種類

設計ドキュメントには 4 種の drill を運用手順書化しました:

#drill想定シナリオ所要時間
1アプリ層 READ-ONLY計画 migration30 分
2アプリ層 kill-switchendpoint 暴走20 分
3エッジ層 Cloudflare WorkerDB 全停止45 分
4完全 dark scenarioセキュリティ事案60 分

特に #3 (エッジ層の drill) は 本番でやれば即インシデント なので、STG で意図的に DB を停止する drill を入れています。本番でしか出ない挙動 (CF Worker の KV キャッシュ伝播 30-60 秒、エッジ別の挙動差) を 想定通り 503 が出るか で確認できるのは STG / 本番に近い構成だけです。

「コスト < 期待ダメージ」では判断しない

製品開発で機能採否を判断するとき、しばしば 「実装コスト < 発生確率 × ダメージ」 で評価します。エッジ層の場合、この計算式だと次のようになります:

  • 実装コスト: Cloudflare Worker 1-2 日
  • 発生確率: DB 全停止 (年 < 1 回) + セキュリティインシデント (年 < 1 回)
  • ダメージ (セキュリティインシデント): 法令対応 + 顧客信頼喪失 (定量化困難)

確率ベースでセキュリティインシデントは「年 1 回未満」だとしても、発生時のダメージが法的影響を伴うため期待値計算では弾けません。「ダメージが法的責任 / 顧客信頼に及ぶケースは確率にかかわらず作る」 という判断軸が必要です。

これは保険の発想に近いものです。火災の発生確率は年 < 1% でも、火災保険には多くの人が入ります。発生時のダメージが定量化可能なリスク許容範囲を超えるからです。

β リリース前完成スコープに含めた理由

最終的に β リリース前完成必須として以下を入れました:

  • ✅ エッジ層 (CF Worker + KV + Access)
  • ✅ アプリ層 (NestJS Guard + feature flag テーブル)
  • ✅ 管理コンソール UI (フラグ切替 + kill-switch 管理)
  • ✅ 3 アプリ メンテバナー (ローカルファースト準拠)
  • ✅ 監査ログ + 運用通知メール
  • 🟡 STG / PRD デプロイ + DR drill 4 種実施 ← これが本記事の焦点

drill そのものは数時間で終わりますが、その経験が運用手順書の精度を決めます。書いたきりの手順書は障害時に役に立ちません。実際に時計を見ながら手順を踏んで、「ここで詰まった」「コマンドが違った」を手順書に書き戻すまでが drill です。

まとめ

観点結論
規約で SLA なし宣言済それでもエッジ層の停止経路は作ります (法令 / 信頼 / 商業性の論理)
ドリル実施タイミングβ 前が唯一です (顧客在席後は drill 自体がリスクになります)
実装コスト判断軸期待値計算で弾けない法的リスクは確率に関わらず作ります
運用手順書の精度drill 実施後に「詰まった所」を書き戻すまでが完成です

「β なので最低限でいいよね」という判断は、規約の話と運用の話を混同しがちです。β は機能要件を絞る期間であって、運用堅牢性を犠牲にする期間ではありません。むしろ顧客が少ない今が、運用堅牢性を仕込む最後のチャンスです。

私たちは来週から STG drill を始めます。所要時間と詰まった所を運用手順書に書き戻したら、また続編を書こうと思います。