【本番障害】Protocol Buffer破壊的変更で全サービス停止→復旧までの72時間地獄体験記

Protocol Bufferの破壊的変更を軽く考えて実施した結果、マイクロサービス全体が停止。72時間の地獄の復旧作業から学んだ段階的変更管理の重要性を生々しく記録します。

【本番障害】Protocol Buffer破壊的変更で全サービス停止→復旧までの72時間地獄体験記

プロローグ:軽く考えていた金曜日の夕方

2025年8月23日(金曜日)午後4時30分

「ちょっとAPIを整理するだけだから、30分で終わるでしょ」

そんな軽い気持ちで、Protocol BufferのAPIから不要なフィールドを削除した私。まさか、この判断が72時間に及ぶ地獄の始まりだとは思いもしませんでした。

これは、その時の血と汗と涙の記録です。

第一章:「軽微な変更」の大誤算

背景:不要フィールドの削除計画

私たちのマイクロサービス環境では、ユーザーサービスのAPIが肥大化していました。

1
2
3
4
5
6
7
8
9
// user_service.proto (v1.5.0) - 変更前
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;         // ← これを削除予定
  string department = 5;  // ← これも削除予定
  string legacy_format = 6; // ← これも削除予定
}

削除理由:

  • age: プライバシー要件により廃止
  • department: 新しいプロフィールシステムに統合済み
  • legacy_format: 旧システム用で現在未使用

「誰も使ってないフィールドだし、さくっと削除しちゃお」

致命的な判断:いきなりの削除実行

午後4時35分 - 運命の commit

1
2
3
git add user_service.proto
git commit -m "Remove unused fields from User message"
git push origin main
1
2
3
4
5
6
7
// user_service.proto (v2.0.0) - 変更後
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  // age, department, legacy_format を完全削除
}

「よし、これでスッキリした!」

午後4時50分:最初の異変

Slack に最初のアラートが。

1
2
3
4
🔥 AlertManager
[CRITICAL] user-service: Health check failed
- Error rate: 15% → 45%
- Response time: 200ms → TIMEOUT

「あれ?何かおかしいな…でも金曜の夕方だし、月曜に見よう」

これが、後に「人生最悪の判断」と呼ばれることになります。

第二章:金曜夜の地獄の始まり

午後6時:本格的な障害発生

1
2
3
4
5
6
🚨 PagerDuty Alert
[P1] PRODUCTION DOWN
- user-service: 100% error rate
- payment-service: dependency failure  
- notification-service: cascade failure
- recommendation-service: data corruption

「え?え?なんで全部のサービスが?」

慌てて原因調査

ログ確認:

1
kubectl logs user-service-7d4f8b9c-xh2rl
1
2
3
4
5
ERROR: proto: cannot parse invalid wire-format data
ERROR: field 'age' not found in message User
ERROR: field 'department' not found in message User  
ERROR: unmarshaling failed: unknown field
FATAL: service startup failed

「あ…まずい…」

午後7時:事態の深刻さを理解

影響範囲の調査結果:

  1. user-service: 完全停止(新しいprotoで起動不可)
  2. payment-service: ユーザー情報取得で例外発生
  3. notification-service: ユーザープロファイル参照エラー
  4. recommendation-service: age フィールド依存で計算不可
  5. analytics-service: department フィールド依存でレポート生成失敗

被害状況:

  • 全ユーザーのログイン不可
  • 決済処理完全停止
  • 通知システム機能停止
  • おすすめ機能全滅
  • 管理画面アクセス不可

「これ…全サービス死んでる…」

午後7時30分:緊急事態宣言

CTOからの緊急召集。

「なぜ本番で破壊的変更を?影響調査は?段階的移行は?」

私:「すみません、軽微な変更だと思って…」

CTO:「軽微?全サービス停止が軽微?」

この時の絶望感は言葉にできません。

第三章:復旧作戦 - 第一夜の戦い

午後8時:緊急対策チーム結成

メンバー:

  • CTO(指揮官)
  • SRE リーダー(インフラ担当)
  • バックエンドエンジニア 3名
  • QA エンジニア 1名
  • そして私(戦犯)

復旧戦略1:単純ロールバックの試行

「とりあえず前のバージョンに戻そう」

1
2
git revert HEAD
git push origin main

結果:

1
2
3
❌ FAILED: Container build failed
❌ Protocol buffer compilation error
❌ Service mesh configuration mismatch

「なんで戻らないの?」

判明した問題:

  1. 他のサービスが新しい proto を参照していた
  2. Kubernetes の ConfigMap が更新済み
  3. サービスメッシュのルーティング設定が変更されていた

復旧戦略2:緊急パッチの作成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// user_service.proto (v2.0.1) - 緊急パッチ版
message User {
  string id = 1;
  string name = 2;  
  string email = 3;
  
  // 緊急復旧:削除したフィールドを復活
  int32 age = 4;         // 緊急復旧
  string department = 5;  // 緊急復旧  
  string legacy_format = 6; // 緊急復旧
}

午後11時30分 - デプロイ実行

結果:

1
2
3
4
5
✅ user-service: Started  
✅ payment-service: Health check OK
❌ notification-service: Still failing
❌ recommendation-service: Data inconsistency
❌ analytics-service: Historical data corruption

「一部復旧したけど、まだ半分死んでる…」

深夜1時:現実の厳しさ

残る問題:

  1. 削除期間中のデータ欠損
  2. キャッシュに残る不正データ
  3. 依存サービスの内部状態不整合
  4. ログ解析システムのスキーマ不一致

「これ…復旧に何日かかるんだ…」

深夜3時:第一夜の終了

進捗:

  • ユーザーログイン: 50%復旧
  • 決済処理: 30%復旧
  • 通知システム: 10%復旧
  • その他: ほぼ停止状態

徹夜明けの疲労と絶望感で、思考能力も低下。

「明日も長い戦いになる…」

第四章:土曜日 - データ整合性との戦い

午前8時:新たな問題発覚

一晩で状況はさらに悪化していました。

新たに発見された問題:

1
2
3
4
5
# エラーログの嵐
ERROR: User age calculation failed: field missing
ERROR: Department-based routing failed: null value
ERROR: Legacy format parser: unexpected end of data
ERROR: Cache invalidation failed: schema mismatch

データ整合性の破綻

最も深刻だった問題:

1
2
3
4
5
-- analytics データベース  
SELECT user_id, age, department FROM user_analytics 
WHERE created_at > '2025-08-23 16:30:00';

-- 結果: 3時間分のデータで age と department が NULL

「3時間で30万件のデータが破損…これどうやって修復するんだ…」

修復戦略の検討

選択肢1: バックアップからの復旧

  • 影響:3時間分のデータロス
  • 作業時間:6-8時間
  • リスク:中

選択肢2: 手動データ補完

  • 影響:データロスなし
  • 作業時間:20-30時間
  • リスク:高(人的ミス)

選択肢3: 機械学習による補完

  • 影響:推定値での補完
  • 作業時間:10-15時間
  • リスク:中(推定誤差)

午前11時:修復戦略の決定

チームで協議の結果、組み合わせ戦略を採用。

1
2
3
重要データ(決済関連): バックアップ復旧
分析データ: ML補完
ログデータ: 手動補完

データ修復作業の開始

12時間に及ぶデータ修復祭り:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 決済データの復旧
pg_restore --data-only payment_backup_20250823_1630

# 分析データの ML補完
python ml_data_restoration.py \
  --missing-fields age,department \
  --time-range "2025-08-23 16:30 - 19:30"
  
# ログデータの手動修復  
./manual_log_fix.sh user-service 20250823

土曜夜:70%復旧達成

午後11時の状況:

  • ユーザーログイン: 95%復旧
  • 決済処理: 90%復旧
  • 通知システム: 70%復旧
  • 分析システム: 60%復旧(データ補完中)

「ようやく光が見えてきた…でもまだ終わらない」

第五章:日曜日 - 完全復旧への道のり

午前9時:最後の難敵

残る問題は依存関係の複雑な絡み合いでした。

1
2
3
4
5
6
7
recommendation-service 
  ↓ (depends on)
user-profile-service
  ↓ (depends on)  
user-service
  ↓ (depends on)
identity-service

依存関係地獄の解決

各サービスを依存順序に従って段階的に復旧。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Phase 1: 基盤サービス
kubectl rollout restart deployment/identity-service
kubectl wait --for=condition=available deployment/identity-service

# Phase 2: コアサービス  
kubectl rollout restart deployment/user-service
kubectl wait --for=condition=available deployment/user-service

# Phase 3: 依存サービス
kubectl rollout restart deployment/user-profile-service
kubectl wait --for=condition=available deployment/user-profile-service

# Phase 4: 上位サービス
kubectl rollout restart deployment/recommendation-service
kubectl wait --for=condition=available deployment/recommendation-service

午後3時:ついに完全復旧

全サービス正常稼働確認:

1
2
3
4
5
✅ user-service: 100% healthy
✅ payment-service: 100% healthy  
✅ notification-service: 100% healthy
✅ recommendation-service: 100% healthy
✅ analytics-service: 100% healthy

「やった…やっと終わった…」

午後4時:事後対応

復旧完了の社内通達:

【障害復旧完了】

8/23 16:30 〜 8/25 16:00 に発生していた全サービス停止障害が復旧しました。

原因:Protocol Buffer スキーマの破壊的変更による互換性破綻 影響時間:48時間 影響範囲:全ユーザー、全サービス

詳細な原因分析と再発防止策は後日共有いたします。

第六章:事後分析 - 何がいけなかったのか

根本原因分析

技術的原因:

  1. 破壊的変更を段階的移行なしで実行
  2. 依存関係の把握不足
  3. 影響範囲の調査不足
  4. ロールバック戦略の未検討

プロセス的原因:

  1. 変更管理プロセスの軽視
  2. コードレビューの形骸化
  3. テスト環境での検証不足
  4. 本番環境への直接反映

組織的原因:

  1. 破壊的変更に対する理解不足
  2. リスク意識の欠如
  3. 緊急対応体制の不備

被害の詳細

ビジネスインパクト:

  • 売上損失:約$50,000(48時間の停止)
  • 顧客問い合わせ:2,847件
  • 解約申請:127件
  • SNSでの炎上:Twitter で 1,234件の苦情

技術的コスト:

  • エンジニア工数:240時間(5名×48時間)
  • インフラ費用:$3,200(復旧作業用リソース)
  • データ修復コスト:$8,500(ML処理費用)

総コスト:約$61,700

最も痛かった教訓

「Protocol Bufferの破壊的変更は、絶対に軽く扱ってはいけない」

この当たり前のことを、$61,700と48時間の地獄で学ぶことになったのです。

第七章:再発防止策 - 段階的変更管理の確立

新しい変更管理プロセス

この障害を受けて、厳格な段階的変更管理プロセスを確立しました。

Phase 0: 計画・準備段階

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
## 破壊的変更チェックリスト

### 事前調査  
- [ ] 影響範囲の完全な調査完了
- [ ] 依存サービスリストの作成  
- [ ] 既存クライアントの特定
- [ ] ロールバック戦略の策定
- [ ] データ移行計画の作成

### リスク評価
- [ ] ビジネスインパクト評価
- [ ] 技術的リスク評価  
- [ ] 復旧時間見積もり
- [ ] 代替案の検討

### 承認プロセス
- [ ] テックリード承認
- [ ] CTO承認  
- [ ] ビジネス側承認

Phase 1: 非推奨マーク・新API追加

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 新しいアプローチ(段階的変更)
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  
  // 段階1:非推奨マーク(削除予定を明示)
  int32 age = 4 [deprecated = true];         // 削除予定: 2025-12-31
  string department = 5 [deprecated = true]; // 削除予定: 2025-12-31
  string legacy_format = 6 [deprecated = true]; // 削除予定: 2025-12-31
  
  // 新フィールド追加
  string birth_date = 7;      // age の代替
  UserProfile profile = 8;    // department の代替  
}

Phase 1での運用:

  • 期間:3ヶ月
  • 監視:非推奨API使用状況の継続監視
  • 通知:依存チームへの移行案内
  • サポート:移行支援の提供

Phase 2: 移行促進期間

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Phase 2: 移行期間中の設定
message User {
  string id = 1;
  string name = 2;  
  string email = 3;
  
  // 段階2:警告レベル引き上げ
  int32 age = 4 [deprecated = true];         // 警告:2025-11-30削除
  string department = 5 [deprecated = true]; // 警告:2025-11-30削除
  string legacy_format = 6 [deprecated = true]; // 警告:2025-11-30削除
  
  string birth_date = 7;
  UserProfile profile = 8;
}

Phase 2での運用:

  • 期間:2ヶ月
  • 目標:非推奨API使用率 < 10%
  • 警告:ログレベルでの警告出力強化
  • 支援:移行が困難なチームへの個別支援

Phase 3: 削除実行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Phase 3: 安全な削除
message User {
  // 削除済みフィールドの番号を予約
  reserved 4, 5, 6;
  reserved "age", "department", "legacy_format";
  
  string id = 1;
  string name = 2;
  string email = 3;
  string birth_date = 7;
  UserProfile profile = 8;
}

Phase 3での運用:

  • 実施時期:非推奨API使用率 < 5% を確認後
  • メジャーバージョンアップとして実施
  • reserved による番号保護
  • 詳細なリリースノート作成

自動化ツールの導入

手動プロセスだけでは限界があるため、自動化ツールも導入しました。

破壊的変更検出ツール

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Proto互換性チェック  
proto-compatibility-check \
  --old-proto user_service_v1.proto \
  --new-proto user_service_v2.proto \
  --strict-mode

# 出力例
❌ BREAKING CHANGE DETECTED:
- Field 'age' (4) removed from message User
- Field 'department' (5) removed from message User  
- Field 'legacy_format' (6) removed from message User

⚠️  IMPACT ANALYSIS:
- Affected services: 5 services
- Estimated impact: HIGH
- Recommended approach: Gradual migration

🔧 SUGGESTED STEPS:
1. Mark fields as deprecated
2. Add alternative fields  
3. Monitor usage for 3 months
4. Remove after migration complete

依存関係追跡システム

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# サービス依存関係の可視化
service-dependency-tracker analyze user-service

# 出力例  
📊 DEPENDENCY MAP:
user-service (Protocol Buffer: user.proto)
├── payment-service (uses: User.age for risk calculation)
├── notification-service (uses: User.department for routing)  
├── recommendation-service (uses: User.age for algorithm)
├── analytics-service (uses: User.department for reports)
└── legacy-system (uses: User.legacy_format for integration)

⚠️  BREAKING CHANGE IMPACT:
- Field 'age': 2 critical services affected
- Field 'department': 2 services affected
- Field 'legacy_format': 1 legacy system affected

運用監視システム

リアルタイム監視ダッシュボード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
## 破壊的変更管理ダッシュボード

### 現在のステータス
- Phase 1 (非推奨マーク): user_service v2.1.0
- 削除予定日: 2025-12-31
- 残り日数: 108日

### 移行進捗  
- user_service.User.age使用率: 23% ↓ (目標: <10%)
- user_service.User.department使用率: 15%  (目標: <10%)
- user_service.User.legacy_format使用率: 3%  (目標達成)

### アラート
  payment-service: age フィールド使用量が増加中
 notification-service: department フィールド移行完了

第八章:新プロセスの効果と学び

初回適用:success story

新プロセスを初めて適用したのは、3ヶ月後の order-service の変更でした。

変更内容:

  • payment_method フィールドの削除
  • 新しい payment_info 構造体への移行

結果:

  • 計画期間:6ヶ月
  • 実際の削除実行:無事故で完了
  • ダウンタイム:0分
  • ビジネスインパクト:なし

「やっと正しいやり方を覚えた…」

チーム文化の変化

この障害を境に、チーム全体の意識が劇的に変わりました。

Before(障害前):

1
2
3
エンジニア: 「ちょっと変更するだけだから大丈夫でしょ」
レビュアー: 「コードは問題ないですね、LGTM」  
リリース: 「金曜の夕方だけど、軽微だからリリースしちゃお」

After(現在):

1
2
3
エンジニア: 「これって破壊的変更になりませんか?影響調査します」
レビュアー: 「依存関係チェックツール回しましたか?段階的移行計画は?」
リリース: 「破壊的変更は必ず月曜午前中。復旧チーム待機で」

予期しない副次効果

段階的変更管理を導入した結果、思わぬ良い効果もありました。

1. コードの品質向上

  • 破壊的変更を避けるため、より慎重な設計
  • 将来の拡張性を考慮した API設計
  • ドキュメント品質の向上

2. チーム間コミュニケーション改善

  • 依存関係の可視化により連携強化
  • 変更計画の事前共有によるフィードバック増加
  • 問題の早期発見と解決

3. 運用品質の向上

  • 段階的リリースによる品質確保
  • 監視体制の強化
  • インシデント対応能力の向上

第九章:同じ間違いを犯さないために

他チームへの知見共有

この体験を社内で共有したところ、他のチームからも同様の経験談が多数寄せられました。

よくある失敗パターン:

  1. 「軽微な変更」の過信

    1
    2
    
    「フィールド1つ削除するだけだから」
    → 実際は10のサービスに影響
    
  2. 金曜日の変更

    1
    2
    
    「金曜に変更して週末に様子見」
    → 週末障害で緊急召集
    
  3. テスト環境での検証不足

    1
    2
    
    「本番環境でしか起きない問題があった」
    → 本番環境特有の依存関係
    
  4. ロールバック戦略の未検討

    1
    2
    
    「戻せばいいでしょ」
    → 相互依存で戻せない状況
    

チェックリストの標準化

破壊的変更実施チェックリスト(社内標準):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
## Phase 0: 事前準備(必須)
- [ ] 破壊的変更影響調査完了
- [ ] 依存サービス一覧作成(最低10サービス調査)  
- [ ] 既存クライアント使用状況調査
- [ ] ビジネス影響度評価(売上・顧客への影響)
- [ ] テックリード・CTO承認取得

## Phase 1: 非推奨化(3ヶ月以上)
- [ ] deprecated マーク追加
- [ ] 代替手段の提供
- [ ] 移行ガイド作成・共有
- [ ] 使用状況監視開始  
- [ ] 定期的な移行状況レポート

## Phase 2: 移行促進(2ヶ月以上)
- [ ] 使用率50%以下を確認
- [ ] 警告レベル引き上げ
- [ ] 個別チーム移行支援  
- [ ] 削除予定日の明確化・通知

## Phase 3: 削除実行
- [ ] 使用率5%以下を確認
- [ ] メジャーバージョンアップとして実施
- [ ] reserved設定でフィールド保護
- [ ] 復旧チーム待機での実行(平日午前中)
- [ ] 段階的ロールアウト(Canary → Blue-Green)

教育プログラムの実施

新人研修での必修コース:「Protocol Buffer 破壊的変更の恐怖」

実際の障害ログを使った2時間の研修プログラムを作成しました。

研修内容:

  1. 実際の障害ログ分析(45分)
  2. 正しい変更管理手法(30分)
  3. ハンズオン実習(30分)
  4. 質疑応答(15分)

研修効果:

  • 受講者の90%が「破壊的変更の怖さを理解した」と回答
  • 新人による同種の事故:0件(6ヶ月間)

まとめ:$61,700と48時間で学んだこと

最も重要な教訓

「軽微な変更」などというものは存在しない。特にProtocol Bufferにおいては。

あの金曜日の夕方、私は「30分で終わる簡単な作業」だと思っていました。

結果:

  • 48時間の全サービス停止
  • $61,700の損失
  • 顧客からの信頼失墜
  • チーム全体への迷惑

技術的な学び

  1. 段階的変更管理は必須

    • deprecated → 移行 → reserved のフロー
    • 最低6ヶ月の移行期間確保
    • 使用状況の継続監視
  2. 依存関係の可視化

    • サービス間依存の完全な把握
    • 影響範囲の事前調査
    • 自動化ツールによる継続監視
  3. ロールバック戦略

    • 簡単に戻せない場合の対処法
    • 段階的復旧の手順確立
    • 緊急事態対応チームの編成

プロセス的な学び

  1. 変更管理プロセスの重要性

    • 技術的レビューだけでは不十分
    • ビジネス影響度の評価必須
    • 段階的承認プロセスの確立
  2. チームコミュニケーション

    • 依存チームへの早期共有
    • 移行支援の積極的実施
    • 定期的な進捗レポート
  3. リスク管理

    • 最悪ケースシナリオの想定
    • 復旧時間の見積もり
    • ビジネス継続計画との連携

人生的な学び

「面倒くさい」と思うプロセスにこそ、価値がある。

段階的変更管理は確かに面倒です。6ヶ月かけて1つのフィールドを削除するなんて、効率的には見えません。

でも、その「面倒くささ」が、$61,700の損失と48時間の地獄を防いでくれるのです。

最後に:同じ過ちを犯さないために

この記録を読んでいるあなたへ。

もしも今、「これくらい軽微だから大丈夫」と思っている変更があるなら、立ち止まってください。

  • 本当に軽微ですか?
  • 依存関係を全て把握していますか?
  • ロールバック戦略はありますか?
  • 金曜日の夕方ではありませんか?

私と同じ過ちを犯す必要はありません。

段階的変更管理、面倒だけど絶対に価値がある。

これが、$61,700と48時間の地獄から学んだ、最も大切な教訓です。


追記(2025年9月): この障害から1年が経ちました。新しい変更管理プロセスの導入により、Protocol Buffer関連の障害は0件。チーム全体の品質意識も格段に向上しました。

あの時の地獄があったからこそ、今の安定した運用があります。

失敗を恐れず、しかし失敗から学ぶことの大切さを、改めて実感しています。

関連記事:

注意: この記事は実際の障害体験を基にしていますが、具体的なサービス名や数値は一部変更しています。

技術ネタ、趣味や備忘録などを書いているブログです
Hugo で構築されています。
テーマ StackJimmy によって設計されています。