サブネット設計の悪夢:マイクロサービス大移行で全社システム5日間麻痺した設計ミス修正記録

🚨 災害発生:2025年8月12日 14:30

「今度のマイクロサービス基盤は完璧な設計です」

私がそう自信満々に宣言してから、わずか6時間後。全社のWebサービスが完全停止し、顧客からの苦情電話が鳴り止まなくなった。

発生した問題:

  • 200以上のマイクロサービス間通信が完全遮断
  • IP アドレス枯渇によりサービス起動不能
  • Auto Scaling 発動時に新しいPodが作成できない事態
  • 決済システムまで巻き込んだ全社システム停止

影響範囲:

  • 顧客向けWebサイト: 完全停止
  • 社内システム: 80%機能停止
  • 決済処理: 5日間停止
  • 推定売上損失: 3億円

この記事は、サブネット設計の甘さが招いた5日間の地獄とその完全復旧までの記録である。

💀 設計ミスの発端:過信したサブネット計画

問題の設計図

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 災害を招いた設計
VPC: microservices-vpc (10.0.0.0/16)

Subnets:
  # ❌ 致命的な設計ミス
  container-subnet: 10.0.10.0/24  # ←256個しかIP無い
    Purpose: GKE Cluster (200サービス)
    Expected_Pods: "50個くらい?"
    Reality: 1,200個のPod必要
    
  service-mesh-subnet: 10.0.3.0/28  # ←16個のIP
    Purpose: Istio Control Plane
    Expected: "Control Plane 3台"  
    Reality: Istio Proxy 全Pod分必要
    
  database-subnet: 10.0.4.0/27  # ←32個のIP  
    Purpose: Database Services
    Expected: "DB 10台"
    Reality: 各サービス専用DB必要

🤦‍♂️ 甘かった見積もり

私の楽観的計算:

  • マイクロサービス: 200個
  • 1サービスあたりPod: 1〜2個
  • 必要IP: 「500個もあれば十分」

現実:

  • 1サービスあたりPod: 3〜15個(本番/ステージング/カナリー)
  • Istio Proxy: 全Pod分必要
  • Database: サービス専用DBが各3〜5インスタンス
  • 必要IP総数: 3,000個超

🔥 災害の瞬間:IP枯渇の連鎖反応

14:30 - 移行作業開始

1
2
# 自信満々でデプロイ開始
kubectl apply -f microservices-manifests/

最初の50サービスは順調に起動。 「見ろ、完璧な設計だ」

15:45 - 最初の異変

1
2
3
4
Error: Pod "payment-service-7d4c8f9b-xrt2k" failed to schedule
Reason: IP address allocation failed in subnet container-subnet
Available IPs: 12
Required IPs: 45

「おかしい…計算では余裕があるはず」

16:20 - 連鎖的システム停止開始

IP枯渇により:

  1. 新しいPodが起動できない
  2. Auto Scalingが機能しない
  3. 既存Podの通信がIstio経由で不可能
  4. 決済サービスが応答不能

17:00 - 全社システム完全停止

1
2
3
# 絶望的な状況確認
kubectl get pods --all-namespaces | grep -v Running
# 結果: 800+ Pods が Pending 状態

管理者から緊急連絡: 「顧客サイトが全部死んでる。すぐ戻せ!」

🚨 緊急対応:5日間の修羅場

Day 1-2: 応急処置で時間稼ぎ

緊急IP確保作戦

1
2
3
# 他のサブネットからIP借用(応急処置)
gcloud compute networks subnets expand-ip-range container-subnet \
    --prefix-length=22  # /24 → /22に拡張

結果: 一部サービス復旧するも、根本解決にならず

とりあえず古いシステムに戻す試み

1
2
3
4
# 旧システムに緊急ロールバック
kubectl rollout undo deployment/payment-service
kubectl rollout undo deployment/user-service
# ... 200回繰り返し

問題: データベースのマイグレーションが完了済みでロールバック不可能

Day 3-4: 根本設計見直し

正しいIP計算をやり直し

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 現実的な設計に変更
Container_IP_Requirements:
  Microservices: 200 services
  Per_Service_Pods:
    Production: 5 pods
    Staging: 3 pods  
    Canary: 2 pods
    Total: 10 pods/service
    
  Total_Application_Pods: 200 × 10 = 2,000
  
  Istio_Proxy: 2,000 (各Podにサイドカー)
  Database_Pods: 200 services × 3 replicas = 600
  Monitoring_Pods: 100
  
  Safety_Buffer: 50%
  Total_Required: (2,000 + 600 + 100) × 1.5 = 4,050 IPs

新しいサブネット構成設計

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 修正後の設計
VPC: microservices-vpc-v2 (10.0.0.0/16)

Subnets:
  # ✅ 現実的な設計
  container-subnet: 10.0.0.0/20    # 4,096 IPs
    Purpose: GKE Main Cluster
    Available: 4,096 - 16 = 4,080 IPs
    
  container-staging-subnet: 10.0.16.0/22  # 1,024 IPs  
    Purpose: Staging Environment
    
  service-mesh-subnet: 10.0.20.0/22  # 1,024 IPs
    Purpose: Istio Control Plane + Proxies
    
  database-subnet: 10.0.24.0/21   # 2,048 IPs
    Purpose: Database Services
    
  monitoring-subnet: 10.0.32.0/24  # 256 IPs
    Purpose: Prometheus / Grafana
    
  backup-subnet: 10.0.33.0/24     # 256 IPs  
    Purpose: Future Expansion

Day 5: 完全再構築

新VPC環境の構築

1
2
3
4
5
6
7
8
9
# 新しいVPCを並行して構築
gcloud compute networks create microservices-vpc-v2 \
    --subnet-mode regional

# 適切なサイズのサブネット作成
gcloud compute networks subnets create container-subnet-v2 \
    --network microservices-vpc-v2 \
    --range 10.0.0.0/20 \
    --region asia-northeast1

データベース移行(最も困難)

1
2
3
4
5
6
7
8
9
# データ無停止移行のためのレプリケーション設定
gcloud sql instances patch main-db \
    --enable-bin-log \
    --backup-start-time 01:00

# 新環境への段階的レプリケーション
gcloud sql instances create main-db-v2 \
    --master-instance-name main-db \
    --replica-type FAILOVER

💡 根本原因分析:なぜこんな設計をしたのか

1. 過度な楽観的見積もり

1
2
3
4
5
6
7
8
9
Wrong_Assumptions:
  "1サービス = 1 Pod": 
    Reality: Production/Staging/Canary で 10 Pod以上
    
  "IPは余るほどある":
    Reality: Kubernetesクラスターは大量IP消費
    
  "Istioは軽い":
    Reality: 全Pod分のProxy IPが必要

2. 理論と現実のギャップ

1
2
3
4
5
Kubernetes_Reality:
  Pod_Density: "理論値の50%程度"
  IP_Fragmentation: "連続IP確保が困難"  
  Service_Mesh_Overhead: "想定の5倍のリソース"
  Auto_Scaling_Burst: "瞬間的に10倍のPod起動"

3. テスト環境での検証不足

1
2
3
4
5
6
7
8
9
Test_Environment_Problems:
  Scale: "10サービスのみでテスト"
  Load: "実負荷の1/100"
  Network: "シンプルな構成でテスト"
  
Real_Environment:
  Scale: "200サービス同時稼働"  
  Load: "予想の10倍のトラフィック"
  Network: "複雑なサービス間依存"

🛠️ 完全解決策:enterprise レベルのサブネット設計

階層化サブネット戦略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 本番レベルの設計原則
Subnet_Design_Strategy:
  Principle_1_Isolation:
    - Environment別完全分離(prod/staging/dev)
    - Service Tier別分離(web/app/data)
    - Security Zone別分離(dmz/internal/restricted)
    
  Principle_2_Scalability:
    - 現在必要量の5倍を確保
    - Auto Scaling burst対応
    - 将来拡張考慮(10年先まで)
    
  Principle_3_Security:
    - Zero Trust Network設計
    - Service Mesh segmentation  
    - Network Policy enforcement

実用的なIP計算フォーミュラ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 実用IP計算式(災害を避ける)
IP_Calculation_Formula:
  Base_Requirements:
    Services: N
    Pods_Per_Service: P
    Environments: E (prod/staging/dev/canary)
    
  Service_Mesh_Factor: 2.0  # Istio proxy分
  Database_Factor: 1.5      # DB replica分  
  Monitoring_Factor: 1.2    # Monitoring stack
  Auto_Scaling_Factor: 3.0  # Burst scaling
  Safety_Buffer: 2.0        # 余裕を持った設計
  
  Total_IPs = N × P × E × 2.0 × 1.5 × 1.2 × 3.0 × 2.0
  
Example:
  200 services × 3 pods × 4 envs × 2.0 × 1.5 × 1.2 × 3.0 × 2.0 
  = 200 × 3 × 4 × 21.6 = 51,840 IPs required
  
Recommended_CIDR: /14 (65,536 IPs) 以上

最終的な設計図

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 災害を経て完成した設計
VPC: microservices-enterprise-vpc (10.0.0.0/14)  # 262,144 IPs

Production_Environment:
  prod-ingress-subnet: 10.0.0.0/22     # 1,024 IPs
  prod-app-subnet: 10.0.4.0/20         # 16,384 IPs  
  prod-data-subnet: 10.0.20.0/21       # 2,048 IPs
  prod-mesh-subnet: 10.0.28.0/20       # 16,384 IPs
  
Staging_Environment:
  staging-app-subnet: 10.1.0.0/21      # 2,048 IPs
  staging-data-subnet: 10.1.8.0/22     # 1,024 IPs
  staging-mesh-subnet: 10.1.12.0/21    # 2,048 IPs
  
Development_Environment:
  dev-app-subnet: 10.2.0.0/22          # 1,024 IPs
  dev-data-subnet: 10.2.4.0/23         # 512 IPs
  dev-mesh-subnet: 10.2.6.0/22         # 1,024 IPs
  
Special_Purpose:
  ci-cd-subnet: 10.3.0.0/22           # 1,024 IPs
  monitoring-subnet: 10.3.4.0/22      # 1,024 IPs  
  backup-subnet: 10.3.8.0/22          # 1,024 IPs
  future-expansion: 10.4.0.0/18       # 16,384 IPs

🚀 復旧作業:段階的移行戦略

Phase 1: 緊急復旧(完了:Day 5)

1
2
3
4
5
6
7
# クリティカルサービス優先復旧
kubectl create namespace critical-services
kubectl apply -f critical-manifests/ -n critical-services

# 決済システム最優先
kubectl scale deployment payment-service --replicas=10
kubectl scale deployment user-auth-service --replicas=8

Phase 2: 段階的移行(完了:Day 10)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Migration_Strategy:
  Week_1: Critical services (payment, auth, user)
  Week_2: Customer-facing services (web, api, mobile)  
  Week_3: Internal services (admin, reporting, batch)
  Week_4: Development/staging environments
  
Risk_Mitigation:
  - Blue-Green deployment per service
  - Real-time health monitoring
  - Immediate rollback capability
  - Database replica synchronization

Phase 3: 監視・自動化強化(完了:Day 14)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# IP使用率監視の自動化
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
spec:
  groups:
  - name: subnet-monitoring
    rules:
    - alert: SubnetIPUtilizationHigh
      expr: subnet_ip_utilization > 0.8
      labels:
        severity: warning
      annotations:
        summary: "Subnet IP utilization above 80%"
        
    - alert: SubnetIPUtilizationCritical  
      expr: subnet_ip_utilization > 0.95
      labels:
        severity: critical
      annotations:
        summary: "URGENT: Subnet running out of IPs"

📊 災害から学んだ教訓

❌ やってはいけない設計パターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Anti_Patterns:
  Underestimating_Kubernetes:
    - "1 Service = 1 Pod" の思い込み
    - サイドカーProxy のIP消費を無視
    - Auto Scaling burst を考慮しない
    
  Insufficient_Testing:
    - 小規模環境でのテスト のみ
    - 負荷テスト不足
    - ネットワーク分割テスト不足
    
  Poor_Capacity_Planning:
    - 楽観的見積もり
    - 余裕を持たない設計
    - 将来拡張を考慮しない

✅ 推奨される設計パターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Best_Practices:
  Realistic_Capacity_Planning:
    - 現実的なPod数見積もり(5-10倍余裕)
    - Service Mesh overhead考慮(2-3倍)
    - Auto Scaling burst考慮(3-5倍)
    - 将来拡張考慮(10年先まで)
    
  Comprehensive_Testing:
    - Production同等規模でのテスト
    - 負荷テスト + ネットワーク分割テスト  
    - 段階的デプロイによるリスク軽減
    
  Proactive_Monitoring:
    - IP使用率リアルタイム監視
    - 閾値アラート設定(80%で警告、95%で緊急)
    - 自動スケールアウトの準備

🎯 ビジネス教訓

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Business_Lessons:
  Technical_Debt_Cost:
    - 不適切設計の代償: 3億円売上損失
    - 復旧コスト: エンジニア延べ200時間
    - 信頼回復コスト: 測定不可能
    
  Investment_Priority:
    - インフラ設計に十分な時間とリソースを投資
    - テスト環境の Production 同等化
    - 監視・運用の自動化への先行投資

🔍 技術的深堀り:サブネット設計の科学

Kubernetes ネットワーキングの現実

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Kubernetes_Networking_Reality:
  Pod_IP_Consumption:
    - 1 Pod = 1 IP (基本)
    - Istio Proxy = Pod分の追加IP
    - Init Containers = 一時的なIP消費
    - Failed Pods = IP の断片化要因
    
  Service_Discovery_Overhead:
    - ClusterIP Service = 仮想IP消費
    - NodePort Service = Node IP消費  
    - LoadBalancer Service = 外部IP消費
    - Ingress Controller = 追加IP消費
    
  Auto_Scaling_Burst_Pattern:
    - CPU spike時: 瞬間的に5-10倍Pod作成
    - Memory pressure時: Pod migration発生
    - Network policy変更: Pod restart cascade

Service Mesh の隠れコスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Istio_Hidden_Costs:
  Control_Plane:
    - istiod: 3-5 replicas × N zones = 15 IPs
    - Ingress Gateway: 3-5 replicas = 15 IPs
    - Egress Gateway: 3-5 replicas = 15 IPs
    
  Data_Plane:
    - Envoy Proxy: 全Pod分 = Application Pod数分
    - Mixer/Telemetry: Pod数 × 0.1
    - Pilot Agent: Pod数 × 0.05
    
  Total_Factor: Application Pod × 2.15

大規模運用での実測データ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Production_Data_200_Services:
  Expected_vs_Reality:
    Estimated_Pods: 400 (2 per service)
    Actual_Pods: 3,200 (16 per service average)
    
    Estimated_IPs: 800  
    Actual_IPs: 6,400
    
    Growth_Rate: 800% over estimate
    
  Peak_Scaling_Events:
    Black_Friday: 15,000 Pods (4.7x normal)
    Database_Failover: 8,000 Pods (2.5x normal)  
    Network_Partition: 12,000 Pods (3.8x normal)

🎯 完全復旧:システム安定化まで

最終的な安定稼働状態

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 復旧完了後の状況確認
kubectl get pods --all-namespaces | wc -l
# Result: 3,247 pods running

kubectl get nodes -o wide
# Result: 15 nodes, all Ready

# IP使用率確認
gcloud compute networks subnets describe container-subnet-v2 \
    --format="value(ipCidrRange, availableIpAddressCount)"
# Result: 10.0.0.0/20, 892 available (78% utilization)

パフォーマンステスト結果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Performance_Test_Results:
  Load_Test_Peak:
    Requests_Per_Second: 50,000
    Response_Time_95th: 120ms  
    Error_Rate: 0.02%
    Pod_Count_Peak: 4,200
    IP_Utilization_Peak: 84%
    
  Stress_Test_Results:
    Auto_Scale_Time: 45 seconds
    New_Pod_IP_Assignment: < 5 seconds
    Service_Discovery_Propagation: < 10 seconds
    
  Disaster_Recovery_Test:
    Failover_Time: 2 minutes
    Data_Loss: 0 transactions
    Service_Restoration: 100%

📈 長期運用結果

6ヶ月後の状況

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Six_Months_Later:
  System_Stability:
    Uptime: 99.97%
    Major_Incidents: 0
    IP_Related_Issues: 0
    
  Capacity_Utilization:
    Average_Pod_Count: 2,800
    Peak_Pod_Count: 4,200
    IP_Utilization: 65-85%
    Headroom_Available: 30%
    
  Cost_Impact:
    Infrastructure_Cost: +40% (larger subnets)
    Operational_Cost: -60% (automation)
    Incident_Cost: -100% (zero outages)
    
  Business_Impact:
    Customer_Satisfaction: Restored
    Revenue_Impact: +15% (improved reliability)
    Team_Productivity: +30% (less firefighting)

🏆 まとめ:災害を防ぐサブネット設計の鉄則

🎯 設計時の絶対ルール

  1. 現実的な容量計画

    • 楽観的見積もりに5-10倍の余裕
    • Service Mesh overhead(2-3倍)を必ず計算
    • Auto Scaling burst(3-5倍)を考慮
  2. 本番同等のテスト

    • スケール・負荷・障害の3点セット
    • ネットワーク分割テストの実施
    • 段階的デプロイによるリスク検証
  3. プロアクティブ監視

    • IP使用率の常時監視
    • 閾値アラート(80%警告、95%緊急)
    • 自動拡張の準備

💡 運用時の継続改善

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Continuous_Improvement:
  Monthly_Review:
    - IP使用率トレンド分析
    - 成長予測の見直し
    - キャパシティプランの更新
    
  Quarterly_Test:
    - 災害復旧訓練
    - スケールテスト
    - セキュリティ監査
    
  Annual_Architecture_Review:
    - 技術選択の再評価
    - サブネット構成の最適化
    - コスト効率の改善

🚨 絶対に避けるべき間違い

  • ❌ 「うちは小さいから大丈夫」という過信
  • ❌ 開発環境と本番環境の規模違いを無視
  • ❌ Kubernetesの「隠れリソース消費」を軽視
  • ❌ 成長を見込まない固定的な設計
  • ❌ 監視なしでの本番投入

この災害から3年。今も私たちのシステムは安定稼働している。あの5日間の地獄は、確実にチームを成長させた。二度と同じ過ちを繰り返さないために、この記録を残す。

読者の皆さん、同じ災害を起こさないために。サブネット設計は軽く考えてはいけない。現実を見据えた、余裕のある設計を。


📅 災害発生: 2025年08月12日
📅 完全復旧: 2025年08月17日
📅 記録作成: 2025年09月14日

教訓: インフラ設計に「だいたい大丈夫」は存在しない

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