45395 - シコウサクゴ -

監視ハーネスでサイレント障害を1時間以内に検知する:5モジュール78テストの設計と実装

2026-04-05
AI駆動開発
AI駆動開発
Claude Code
Python
監視
自動化
パイプライン
Last updated:2026-04-05
12 Minutes
2381 Words

自動売買システムのパイプラインが「exit 0 で終了しているのに、実は何も生成していない」——こうしたサイレント障害に気付くまで8日〜16日かかっていました。この問題を解決するために、5つの監視モジュールと78のテストケースで構成する「監視ハーネス」を設計・実装しました。検知遅延は24時間超から1時間以内に短縮されています。

サイレント障害はなぜ厄介か

一般的な障害はプロセスがクラッシュし、ログにエラーが残ります。ところが個人運用の自動化パイプラインでは、次のような「静かな故障」が起きます。

インシデント検知までの日数表面的な動作
環境変数未設定でスクリプトが即終了16日exit 0、ログ正常
SSD未マウントでパイプライン全停止8日launchdジョブ自体は起動
上流パイプラインの出力消失9日下流は「入力なし」で正常終了
ログは更新されているが entries=0 が連続10営業日exit 0、ログに完了メッセージあり

共通しているのは、プロセスの終了コードは0なのに、生成されるべきファイルやトレード結果が存在しないという点です。exit code を見ているだけでは永遠に気付けません。

設計方針:「生成物の存在」で判断する

監視の基本方針を「プロセスが動いたか」から「期待する生成物が存在するか」に切り替えました。

1
従来: launchd → スクリプト実行 → exit 0 → 正常と判断
2
今回: launchd → スクリプト実行 → 生成物チェック → 鮮度・件数・内容を検証

この方針を5つのモジュールに分解して実装しています。

5つの監視モジュール

全体アーキテクチャ

1
monitoring_harness.py(エントリーポイント)
2
3
├─ infra_monitor (FR-5: インフラ前提条件)
4
│ SSD マウント / symlink / DB接続 / ディスク容量
5
6
├─ pipeline_monitor (FR-1: 生成物監視)
7
│ ディレクトリ存在 / ファイル数 / 鮮度 / ログパターン
8
9
├─ business_monitor (FR-2: 業務メトリクス)
10
│ entries=0 連続日数 / フォールバック検知
11
12
├─ causal_tracker (FR-3: 因果追跡)
13
│ 上流障害 → 下流アラート抑制
14
15
└─ alert_manager (FR-4: 通知制御)
1 collapsed line
16
新規通知 / 復旧通知 / エスカレーション / 状態永続化

FR-5: インフラ前提条件チェック

パイプライン全体が依存する「土台」を最初にチェックします。SSD未マウントやsymlink切れはCRITICALとして即通知・即終了です。

1
infra:
2
ssd_mount_path: "/Volumes/work2"
3
tmp_symlink: "_tmp"
4
db_check_enabled: true
5
disk_warn_gb: 5.0

CRITICALが検出された時点でパイプラインチェックをスキップします。土台が崩れている状態で下流のエラーを数えても意味がないためです。

FR-1: パイプライン生成物監視

各パイプラインをYAMLで定義し、生成物の存在・鮮度・件数をチェックします。

1
pipelines:
2
- name: fx_swing
3
chain:
4
- name: getFXStocks
5
output_dir: _tmp/getFXStocks2calcFXAnalyticsDatas
6
pattern: "*.parquet"
7
min_active: 0 # 下流が即消費するため空が正常
8
max_age_hours: 4
9
weekday_only: true # 土日は +48h 緩和
10
schedule: hourly

ここで重要なのが Consumed Pipeline パターン です。下流ジョブが上流の出力を即座に消費する場合、ディレクトリは空になります。これを「障害」と誤検知しないために、.bak ファイルの鮮度で判定します。

1
Active ファイルあり → 鮮度チェック → OK or ERROR
2
Active ファイルなし → .bak ファイル確認
3
├─ .bak が新鮮 → OK(正常に消費された)
4
└─ .bak が古い or なし → ERROR(パイプライン停止)

FR-2: 業務メトリクス監視

exit 0 でもビジネス上の異常を検知します。具体的には2つのチェックです。

  1. entries=0 連続日数: ログから entries=N を抽出し、N=0 が3日連続で WARNING
  2. フォールバック検知: "SwingTrade.*フォールバック" パターンをログから検出したら ERROR

FR-3: パイプライン因果追跡

上流ジョブが障害を起こすと、下流ジョブも連鎖的にエラーになります。そのまま通知すると「getFXStocks ERROR」「calcFX ERROR」「simFX ERROR」と3件のアラートが飛びます。

因果追跡モジュールは、パイプラインのチェーン定義を使って根本原因だけを通知し、下流のアラートを抑制します。

1
getFXStocks: ERROR ← 根本原因として通知(影響範囲を付記)
2
calcFX: ERROR ← 抑制(上流 'getFXStocks' の障害による影響)
3
simFX: ERROR ← 抑制

FR-4: 通知制御と状態永続化

Slack通知のルールは4つです。

状態遷移通知理由
新規障害する即時対応のため
継続障害しない同じ通知の繰り返しを防ぐ
復旧する対応完了の確認
エスカレーション(WARNING→ERROR)する6時間放置されたWARNINGを昇格

アラート状態はJSONファイルに永続化されます。書き込みは tmp → rename のアトミック操作で、途中クラッシュによる破損を防いでいます。

1
[監視ハーネス] 07:35
2
🚨 [CRITICAL] infra:ssd_mount: /Volumes/work2 未マウント
3
❌ [ERROR] pipeline:fx_swing:getFXStocks: 最新ファイルが28h前 (閾値: 4h)
4
✅ [復旧] pipeline:js:getJQuantsStocks: 正常に復旧
5
⬆️ [エスカレーション WARNING→ERROR] infra:disk_space: 6h超過

4つの実行モードとスケジュール

4つのlaunchdジョブで、時間帯ごとに監視範囲を変えています。

モード実行時刻監視対象
hourly毎時00分インフラ + FXパイプライン(hourlyスケジュールのみ)
post-fx07:35インフラ + FX全ステージ + FX業務メトリクス
post-js09:30インフラ + 日本株パイプライン + JS業務メトリクス
post-us06:15インフラ + 米国株パイプライン + US業務メトリクス

post-* モードは各市場のパイプライン完了後の時刻に設定しています。「結果が出ているはずの時刻に、結果を確認する」というシンプルな考え方です。

共通データ型:CheckResult

すべてのモジュールが返す統一データ型です。

1
@dataclass(frozen=True)
2
class CheckResult:
3
source: str # "pipeline:fx_swing:getFXStocks"
4
severity: Severity # OK / INFO / WARNING / ERROR / CRITICAL
5
message: str # 人間が読むメッセージ
6
pipeline: str = "" # 因果追跡用
7
stage: str = "" # 因果追跡用

frozen=True にすることで、チェック結果が後から書き換えられることを防いでいます。因果追跡で結果を差し替える場合も、新しいインスタンスを生成します。

YAML駆動の設定管理

パイプラインの追加や閾値の変更はYAMLファイルの編集だけで完結します。コードを触る必要はありません。

1
# 新しいパイプラインを追加する場合
2
pipelines:
3
- name: new_pipeline
4
chain:
5
- name: step1
6
output_dir: _tmp/step1_output
7
pattern: "*.csv"
8
min_active: 1
9
max_age_hours: 24
10
schedule: daily

設定ファイルは config_loader.py が frozen dataclass に変換します。型安全性が担保され、設定ミスは起動時に即座にエラーになります。

78テストの設計

テストは8カテゴリ、78ケースです。

カテゴリテスト数主な検証内容
Types3frozen dataclass、デフォルト値
Config Loader8YAML解析、異常系(FileNotFound等)
Infra Monitor7SSD/symlink/DB/ディスク容量
Pipeline Monitor18正常/異常/境界値/Consumedパターン/週末緩和
Business Monitor7entries追跡、フォールバック検知
Causal Tracker7上流障害→下流抑制、独立パイプライン
Alert Manager15状態遷移、永続化、エスカレーション
E2E + Incident13全モード結合テスト + 過去インシデント再現

特に重視したのが過去インシデント再現テストです。「SSD未マウント8日間放置」「exit 0 だがパイプライン停止」といった実際の事故シナリオを再現し、ハーネスが正しく検知できることを保証しています。

過去インシデントのカバレッジ

このハーネスが検知可能な過去の障害をまとめました。

インシデント旧検知遅延今回の検知メカニズム
環境変数未設定で即終了16日インフラチェック
SSD未マウント8日マウント状態確認(CRITICAL)
ログ未更新(symlink書き込み不可)8日ファイル鮮度チェック
exit 0 + パイプライン停止不明生成物カウント確認
ログ正常だが entries=0 連続10営業日業務メトリクス監視
上流停止→下流正常終了9日因果追跡 + 生成物監視

すべてのケースで、検知遅延が1時間以内に短縮されます。

実装で得た知見

1. exit code は信用できない

最も重要な学びです。exit 0 は「プロセスが異常終了しなかった」を意味するだけで、「期待する処理が完了した」を意味しません。監視は必ず「生成物の存在」で判断すべきです。

2. Consumed Pipeline は専用ロジックが必要

下流ジョブが上流の出力を即座に消費するパイプラインでは、ディレクトリが空であることが「正常」です。一律に「ファイルがない=障害」と判定すると誤検知が多発します。.bak ファイルのタイムスタンプで鮮度を判定する設計にしています。

3. 因果追跡でアラート疲れを防ぐ

上流障害で下流も全部アラートになると、通知が大量に飛びます。障害対応中にノイズが増えるのは逆効果です。根本原因だけを通知し、影響範囲を付記する設計にしたことで、1回の障害で1通知に収まります。

4. 週末緩和は市場連動システムに必須

FX市場は土日閉場です。「金曜夜に生成されたファイルが日曜朝に古い」と判定されないよう、weekday_only: true のステージは土日に閾値を +48h 緩和しています。

まとめ

監視ハーネスは、「プロセスが動いたか」ではなく「生成物が意図通りに存在するか」を検証する仕組みです。5モジュール78テストという規模になりましたが、核となる設計方針はシンプルです。パイプラインの期待する出力をYAMLに定義し、実態と突き合わせる。一致しなければ通知する。

個人運用のシステムでは「誰も見ていない間に静かに壊れる」ことが最大のリスクです。このリスクを構造的に潰す仕組みとして、同様の課題を持つ方の参考になれば幸いです。

Article title:監視ハーネスでサイレント障害を1時間以内に検知する:5モジュール78テストの設計と実装
Article author:45395
Release time:2026-04-05

記事へのご質問・ご感想をお聞かせください

フィードバックを送る