0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ninda 22日目: 障害検知と復旧戦略

Last updated at Posted at 2025-12-21

今日のゴール

分散システムにおける障害検知と復旧の仕組みを理解し、可用性の高いシステムを構築する方法を学びます。


はじめに

分散システムでは、障害は「起こるかもしれないもの」ではなく「必ず起こるもの」として設計する必要があります。ネットワークの断絶、サーバーのクラッシュ、ディスク障害など、様々な障害が日常的に発生します。

nindaは、これらの障害に対して自動的に検知・復旧する機能を提供しています。本日は、障害に強いシステムを構築するための戦略を学びましょう。


障害の種類

分散システムで発生する主な障害は3種類あります。

障害タイプの比較

障害タイプ 検知難易度 nindaの対応
クラッシュ障害 プロセス停止、マシンダウン 容易 ハートビート監視
ネットワーク障害 分断、遅延、パケットロス 中程度 タイムアウト、クォーラム
ビザンチン障害 データ改ざん、不正応答 困難 対象外(信頼ネットワーク前提)

なぜビザンチン障害に対応しないのか

ビザンチン障害への対応は複雑で、通常3f+1ノード(fは障害ノード数)が必要になります。nindaは信頼できる内部ネットワークでの利用を想定しており、クラッシュ障害とネットワーク障害への対応に集中しています。


Supervisor による障害管理

nindaのSupervisorモジュールは、Erlang/OTPスタイルの再起動戦略を提供します。

再起動戦略の選択

戦略 動作 ユースケース
rsOneForOne 障害プロセスのみ再起動 独立したワーカー群
rsOneForAll 全子プロセスを再起動 相互依存するプロセス
rsRestForOne 障害以降のプロセスを再起動 順序依存のパイプライン

Supervisorの使い方

let supervisor = newSupervisor(
  strategy = rsOneForOne,
  intensity = RestartIntensity(maxRestarts: 3, withinMs: 60_000)
)

ポイント: intensityは再起動の頻度を制限します。60秒間に3回以上再起動すると、無限ループを防ぐためにSupervisor自体が停止します。


タプルスペースによる障害検知

nindaの特徴は、タプルスペース自体を監視の基盤として使うことです。

ハートビート監視の仕組み

核心となるコード

# ワーカー側: ハートビート送信
let now = getTime().toUnix() * 1000
await heartbeats.writeAsync(
  toTuple(strVal("heartbeat"), strVal(workerId), intVal(now))
)

# モニター側: タイムアウト検知
for hb in await heartbeats.readAllAsync(pattern):
  if now - hb[2].intVal > TIMEOUT_MS:
    echo "Worker ", hb[1].strVal, " is dead!"

このアプローチの利点:

  1. タプルスペースが監視の基盤 → 分散環境で自然に動作
  2. ハートビートがタプルとして可視化 → デバッグが容易
  3. カスタム監視ロジックの追加が簡単

SupervisedSystem: 統合された監視

SupervisedSystemは、SupervisorとMonitorを統合した使いやすいAPIです。

使い方

let sys = newSupervisedSystem(manager, defaultSupervisedConfig())

sys.addWorker(SupervisedWorker(
  id: "worker-1",
  task: myWorkerProc,
  restart: rpAlways
))

await sys.start()

ハートビート送信は自動化されるため、ワーカーはビジネスロジックに集中できます。


ネットワーク障害への対応

スプリットブレインとは

ネットワーク分断により、クラスタが2つ以上のグループに分かれ、それぞれが独立してリーダーを選出してしまう状態です。

対策: クォーラム(定足数)

過半数の合意がなければ書き込みを拒否するというシンプルなルールで、スプリットブレインを防ぎます。

ノード数 クォーラムサイズ 許容障害数
3 2 1
5 3 2
7 4 3

💡 なぜ奇数ノード? 偶数だと分断時に同サイズのパーティションができ、どちらもクォーラムを満たせなくなる可能性があります。


障害復旧パターン

分散システムでよく使われる3つの復旧パターンを紹介します。

1. サーキットブレーカー

連続した失敗を検知すると、一時的にリクエストを遮断して障害の連鎖を防ぎます。

いつ使う? 外部APIやデータベースへの呼び出しを保護する場合

2. 指数バックオフ

失敗するたびに待機時間を指数的に増やします。

試行1: 100ms 待機
試行2: 200ms 待機
試行3: 400ms 待機
試行4: 800ms 待機
...

ポイント: ジッター(ランダム要素)を加えることで、複数クライアントが同時にリトライする「雷撃群問題」を避けられます。

3. バルクヘッド(隔壁)

リソースを分離して、1つのサービスの障害が全体に波及するのを防ぎます。

var bulkheads = {
  "database": newBulkhead(maxConcurrent = 10),
  "external-api": newBulkhead(maxConcurrent = 5)
}.toTable

船の隔壁(bulkhead)のように、一区画が浸水しても船全体は沈まないようにする設計です。


グレースフルデグラデーション

障害時に完全停止するのではなく、機能を縮退させてサービスを継続する戦略です。

縮退レベルの設計例

レベル 状態 無効化される機能
Normal 通常運用 なし
Partial 一部制限 分析、通知
Minimal 最小限 検索、バルク操作も無効
Maintenance メンテナンス 読み取り専用

フォールバック戦略

フルテキスト検索 → 失敗
    ↓
簡易検索 → 失敗
    ↓
キャッシュから返す → 失敗
    ↓
空の結果を返す(エラーではない)

重要: ユーザーにとって「遅い」や「機能制限」は許容できても、「エラー画面」は許容できないことが多いです。


設計原則

障害に強いシステムを設計するための原則をまとめます。

1. 障害を前提とした設計

❌ 悪い考え方: 「障害が起きたらどうしよう」
✅ 良い考え方: 「障害は必ず起きる。どう対処するか」

2. 検知・隔離・復旧のサイクル

このサイクルを自動化することが重要です。

3. 部分障害でもサービス継続

全体の1%のリクエストが失敗しても、99%は成功させる。これが分散システムの基本姿勢です。


本日のまとめ

学んだこと

トピック ポイント
障害の種類 クラッシュ、ネットワーク、ビザンチンの3種類
Supervisor 再起動戦略と強度制限
ハートビート監視 タプルスペースを監視基盤として活用
スプリットブレイン対策 クォーラムによる過半数合意
復旧パターン サーキットブレーカー、指数バックオフ、バルクヘッド
グレースフルデグラデーション 機能縮退によるサービス継続

設計原則

  • 障害は必ず起こるものとして設計する
  • 検知・隔離・復旧のサイクルを自動化する
  • 部分障害でもサービスを継続できるようにする
  • 監視とアラートで早期発見を心がける

演習問題

問題22-1: サーキットブレーカー

サーキットブレーカーを実装し、外部APIへの呼び出しを保護するシステムを作成してください。状態遷移(Closed → Open → HalfOpen)を正しく実装しましょう。

問題22-2: ヘルスチェックAPI

タプルスペースの状態を監視するヘルスチェックAPIを実装してください。応答時間、エラー率、ディスク使用量などを確認できるようにしましょう。

問題22-3: 自動フェイルオーバー

プライマリノードが障害を起こした際に、自動的にセカンダリへフェイルオーバーするシステムを構築してください。

💡 完全な実装例は GitHubリポジトリsrc/ninda/actor/ を参照してください。


次回は、パフォーマンスチューニングについて学びます。システムの性能を最大限に引き出す方法を探りましょう。

前回: パーティショニングとスケールアウト | 目次 | 次回: パフォーマンスチューニング

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?