Skip to content
Published on

冗長化完全ガイド — 99.999%可用性の秘密

Authors
  • Name
    Twitter
Redundancy & HA

はじめに

「サーバーが1台落ちたらサービスも落ちる」— これが単一障害点(SPOF)です。

二重化・三重化は、このSPOFを排除する技術です。銀行、証券、航空管制システムは三重化まで行い、私たちが毎日使うクラウドサービスも二重化以上で運用されています。

可用性(Availability)の数字の意味

可用性 = 正常運用時間 / 全体時間 × 100%
可用性等級年間ダウンタイム月間ダウンタイム
99%Two 9s3.65日7.3時間
99.9%Three 9s8.77時間43.8分
99.99%Four 9s52.6分4.38分
99.999%Five 9s5.26分26.3秒
99.9999%Six 9s31.5秒2.6秒

ポイント: 99.9%から99.99%に上げるのは、8時間から52分への短縮。しかしコストは10倍以上増加!

二重化(Redundancy)パターン

1. Active-Standby(アクティブ・スタンバイ)

正常状態:
  [Activeサーバー] ←── すべてのトラフィック
  [Standbyサーバー]    (待機、同期のみ)

障害発生:
  [Activeサーバー] ✗   障害検知!
  [Standbyサーバー] ←── トラフィック切り替え(Failover)
    新Activeに昇格
# KubernetesでのActive-Standby: StatefulSet + Leader Election
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-ha
spec:
  replicas: 2 # 1 Active + 1 Standby
  selector:
    matchLabels:
      app: redis
  template:
    spec:
      containers:
        - name: redis
          image: redis:7
          ports:
            - containerPort: 6379
        - name: sentinel # 障害検知 + 自動Failover
          image: redis:7
          command: ['redis-sentinel', '/etc/sentinel.conf']

メリット: リソース効率が良い(Standbyは最小リソース) デメリット: Failover時間(数秒〜数十秒のダウンタイム)

2. Active-Active(アクティブ・アクティブ)

正常状態:
  [サーバーA] ←── トラフィック50%
  [サーバーB] ←── トラフィック50%
  Load Balancerが分散

障害発生:
  [サーバーA] ✗   障害!
  [サーバーB] ←── トラフィック100%(自動)
# Kubernetes Service = 自動Active-Active
apiVersion: v1
kind: Service
metadata:
  name: my-api
spec:
  selector:
    app: my-api
  ports:
    - port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
spec:
  replicas: 2 # 両方Active!
  template:
    spec:
      containers:
        - name: api
          image: my-api:latest
          readinessProbe: # 障害検知
            httpGet:
              path: /health
              port: 8080
            periodSeconds: 5
            failureThreshold: 3 # 3回失敗 → トラフィックから除外

メリット: ダウンタイムほぼ0(すでに分散中のため) デメリット: データ同期が複雑(競合解決が必要)

3. N+1 / N+2 二重化

N+1: N台必要なところに1台追加(最小限の余裕)
  [サーバー1] [サーバー2] [サーバー3] [+予備1]
1台落ちてもOK

N+2: N台必要なところに2台追加
  [サーバー1] [サーバー2] [サーバー3] [+予備1] [+予備2]
2台同時に落ちてもOK + 1台メンテナンス可能

2N: 全体を2倍に(最もコスト高)
  [サーバー1] [サーバー2] [サーバー3] [サーバー4] [サーバー5] [サーバー6]
  → 半分が落ちてもOK(ミッションクリティカル)

三重化(Triple Redundancy)

なぜ三重化?

二重化の致命的弱点: スプリットブレイン(Split Brain)

二重化でネットワーク断絶時:
  [サーバーA] ──✗── [サーバーB]
Bが落ちた?自分がActive!」  「Aが落ちた?自分がActive!」
  → 両方Active → データ不整合 → 大惨事!

三重化 + クォーラムで解決:

三重化(3ノードクラスター):
  [ノードA]──[ノードB]──[ノードC]

ネットワーク分割時:
  [ノードA]    [ノードB]──[ノードC]
  1票(少数)     2票(過半数 = クォーラム!)
Aは自主退任
BCがサービスを継続
  → スプリットブレイン防止!

クォーラム公式: 過半数 = (N/2) + 1
  3ノード: 2票必要(1ノード障害許容)
  5ノード: 3票必要(2ノード障害許容)
  7ノード: 4票必要(3ノード障害許容)

etcd(Kubernetesの頭脳)— 三重化必須!

# etcd 3ノードクラスター(Raft合意アルゴリズム)
# 1ノードダウン: クォーラム(2/3)維持 → サービス継続
# 2ノードダウン: クォーラム喪失 → 読み取りのみ可能

# kubeadmデフォルト設定
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
etcd:
  local:
    extraArgs:
      initial-cluster: >-
        etcd-0=https://10.0.0.1:2380,
        etcd-1=https://10.0.0.2:2380,
        etcd-2=https://10.0.0.3:2380
# Raft合意アルゴリズム(疑似コード)
class RaftNode:
    def __init__(self, node_id, peers):
        self.state = "follower"  # follower → candidate → leader
        self.term = 0
        self.voted_for = None
        self.log = []

    def request_vote(self):
        """リーダー選出 — 過半数の投票が必要"""
        self.state = "candidate"
        self.term += 1
        votes = 1  # 自分自身

        for peer in self.peers:
            if peer.grant_vote(self.term, self.log):
                votes += 1

        if votes > len(self.peers) // 2:  # クォーラム!
            self.state = "leader"
            return True
        return False

    def replicate(self, entry):
        """データ複製 — 過半数確認後にコミット"""
        self.log.append(entry)
        acks = 1

        for peer in self.peers:
            if peer.append_entry(entry):
                acks += 1

        if acks > len(self.peers) // 2:  # クォーラム!
            self.commit(entry)  # 過半数確認 → 安全にコミット

レイヤー別の二重化戦略

全体アーキテクチャ

[ユーザー]
[DNS]Route 53ヘルスチェック(マルチリージョン)
[CDN]CloudFront(グローバルエッジ)
[L4 LB]NLB x2(Active-Active、AZ分散)
[L7 LB]ALB x2(Active-Active)
[Webサーバー]Deployment replicas: 3+(Active-Active)
[アプリサーバー]Deployment replicas: 3+(Active-Active)
   ├──▶ [DB Primary] ──同期レプリケーション──▶ [DB Standby](Active-Standby)
   │    └── 非同期レプリケーション ──▶ [DB Read Replica x2]
   ├──▶ [Redis Primary] ── [Redis Replica x2](Sentinel)
   └──▶ [MQ]RabbitMQ 3ノード(クォーラムキュー)

データベースの二重化

[同期レプリケーション](Strong Consistency)
  Primary ──COMMIT──▶ Standby
  「両方に書き込むまで待つ」
  メリット: データ損失ゼロ
  デメリット: 遅い(ネットワークRTT追加)

[非同期レプリケーション](Eventual Consistency)
  Primary ──COMMIT──▶(後で)Standby
  「まずPrimaryだけに書いて応答」
  メリット: 速い
  デメリット: 障害時に最新データ損失の可能性(RPO0より大きい)

[半同期レプリケーション](Semi-Sync)
  Primary ──COMMIT──▶ Standby(1つだけ確認)
  MySQLデフォルト + 金融機関で好まれる
-- PostgreSQL Streaming Replication設定
-- Primary (postgresql.conf)
-- wal_level = replica
-- max_wal_senders = 3
-- synchronous_standby_names = 'standby1'

-- Standby確認
SELECT client_addr, state, sync_state
FROM pg_stat_replication;
-- client_addr | state     | sync_state
-- 10.0.0.2   | streaming | sync
-- 10.0.0.3   | streaming | async

障害復旧指標

RPO (Recovery Point Objective):
  「どれだけのデータを失っても許容できるか?」
  同期レプリケーション: RPO = 0(データ無損失)
  非同期レプリケーション: RPO = 数秒〜数分

RTO (Recovery Time Objective):
  「どれだけ早く復旧しなければならないか?」
  Active-Active: RTO0
  Active-Standby: RTO = 数秒〜数分
  バックアップ復元: RTO = 数時間

MTBF (Mean Time Between Failures):
  障害間の平均時間(長いほど良い)

MTTR (Mean Time To Repair):
  復旧までの平均時間(短いほど良い)

可用性 = MTBF / (MTBF + MTTR)

コスト対可用性

可用性     │ 構成                │ 相対コスト
───────────┼─────────────────────┼──────────
99%        │ 単一サーバー        │ 1x
99.9%      │ 二重化(1リージョン)│ 2〜3x
99.99%     │ 三重化 + 自動復旧    │ 5〜10x
99.999%    │ マルチリージョンActive│ 10〜50x
99.9999%   │ マルチリージョン + DR50〜100x

クイズ — 二重化/三重化(クリックして確認!)

Q1. Active-StandbyとActive-Activeの最大の違いは? ||Active-Standby: 通常時は1台のみトラフィック処理、Failover時に切り替え遅延あり。Active-Active: 通常時からすべてのサーバーでトラフィック分散、1台が落ちても残りが即座に吸収(ほぼ無停止)||

Q2. スプリットブレイン(Split Brain)とは何か、どう防止するか? ||ネットワーク断絶により両側が自分がActiveだと判断する状態。データ不整合が発生する。三重化 + クォーラム(過半数投票)で防止 — 過半数を得られない側が自主退任||

Q3. etcdが3ノードである理由は?4ノードではない理由は? ||3ノード: 1ノード障害許容(クォーラム2/3)。4ノード: 同じく1ノードのみ障害許容(クォーラム3/4)で3ノードと同じだがコスト増加のみ。奇数が効率的||

Q4. RPOとRTOの違いは? ||RPO: 許容可能なデータ損失量(同期レプリケーション = 0、非同期 = 数秒)。RTO: 障害発生からサービス復旧までの許容時間。どちらも短いほど良いがコスト増加||

Q5. 99.99%と99.999%可用性の年間ダウンタイムの差は? ||99.99% = 52.6分/年、99.999% = 5.26分/年。約10倍の差があり、コストは5〜10倍以上増加||

Q6. N+1と2N二重化の違いは? ||N+1: 必要台数(N) + 予備1台 — コスト効率が良く、1台の障害を許容。2N: 全体を2倍 — コストが高いが半分が落ちてもサービス維持、ミッションクリティカルシステム用||

Q7. 同期レプリケーションと非同期レプリケーションはそれぞれどのような状況で使うか? ||同期: 金融取引、決済などデータ無損失必須(RPO=0)。非同期: 読み取り負荷分散、分析用レプリケーションなど多少の遅延を許容できる場合(パフォーマンス優先)||

Q8. Raft合意でのリーダー選出プロセスを説明せよ。 ||FollowerがリーダーのHeartbeatを受信できなくなるとCandidateに移行 → term増加 → 他のノードに投票を要求 → 過半数の得票でLeaderに昇格 → クライアントリクエストの処理を開始||