# 機能設計書 13-ノードモニター

## 概要

本ドキュメントは、Jenkinsにおけるノードモニター（NodeMonitor）機能の設計について記述する。この機能は、ビルドエージェントノードの健全性を定期的に監視し、問題検出時に自動でノードをオフラインにする予防的保守機能を提供する。

### 本機能の処理概要

ノードモニター機能は、Jenkinsに登録されたエージェントノードに対して定期的にヘルスチェックを実行し、ディスク容量、メモリ、応答時間、クロック同期などの状態を監視する。監視結果が設定された閾値を超えた場合、ノードを自動的にオフラインにしてビルド障害を未然に防止する。

**業務上の目的・背景**：CI/CD環境では、ノードの障害がビルド失敗に直結する。例えば、ディスク空き容量不足でビルド成果物が保存できない、メモリ不足でビルドプロセスがOOM Killerにより終了する、時刻ずれでSSL証明書検証が失敗するなどの問題が発生しうる。ノードモニターによりこれらの問題を事前に検出・対処することで、ビルド基盤の安定性を向上させる。

**機能の利用シーン**：
- ノード一覧画面でのディスク容量・メモリ使用量の確認
- 閾値超過時の自動オフライン化による障害予防
- クロック同期状態の監視（分散ビルド環境での時刻整合性確保）
- 応答時間監視によるネットワーク遅延の検出

**主要な処理内容**：
1. AbstractNodeMonitorDescriptor.triggerUpdate()による定期的な監視スレッド起動
2. 各NodeMonitor実装クラスによるノードごとの監視データ収集
3. 閾値判定と必要に応じたノードのオフライン化
4. 監視結果のComputerSet画面への表示
5. nodeMonitors.xmlへの設定永続化

**関連システム・外部連携**：Remotingによるエージェントノードへのコールバック実行、OS/JVMのシステム情報API。

**権限による制御**：Jenkins.MANAGE権限でノードモニター設定の変更が可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | ノード一覧 | 主画面 | 監視結果の列表示 |
| 28 | ノードモニター設定 | 主画面 | モニター設定の編集 |

## 機能種別

データ連携 / 計算処理 / 監視

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| ignored | boolean | No | モニターを無視するか（オフライン化を抑制） | - |
| freeSpaceThreshold | String | No | ディスク空き容量閾値（例："1GB"） | 有効な容量表記 |
| freeSpaceWarningThreshold | String | No | 警告閾値 | 有効な容量表記 |

### 入力データソース

- Jenkins.getComputers(): 監視対象のComputer一覧
- VirtualChannel: エージェントノードへのリモート呼び出し
- OS/JVMシステム情報: ディスク容量、メモリ、時刻など

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| DiskSpace | DiskSpaceMonitorDescriptor.DiskSpace | ディスク空き容量情報 |
| SwapSpace | MemoryUsage/SwapMonitor.MemoryUsage2 | スワップ領域使用状況 |
| ClockDifference | ClockMonitor.ClockDifference | マスターとの時刻差 |
| ResponseTime | ResponseTimeMonitor.Data | 応答時間（ミリ秒） |
| Architecture | String | OS/JVMアーキテクチャ情報 |

### 出力先

- ComputerSet画面: ノード一覧テーブルの列として表示
- REST API: /computer/ 配下のmonitorDataフィールド
- $JENKINS_HOME/nodeMonitors.xml: モニター設定の永続化

## 処理フロー

### 処理シーケンス

```
1. Jenkins起動時のNodeMonitor初期化
   └─ ComputerSet静的初期化ブロックでnodeMonitors.xmlからロード
2. 定期監視タイマー起動（デフォルト60分間隔）
   └─ AbstractNodeMonitorDescriptor.schedule()でTimer登録
3. triggerUpdate()による監視スレッド起動
   └─ 既存監視スレッドがあれば中断
   └─ Recordスレッドを開始
4. monitor()メソッドで全ノードを巡回
   └─ 各ノードのVirtualChannel経由でデータ収集
   └─ MasterToSlaveCallableで実際のOS情報を取得
5. 閾値判定とオフライン処理
   └─ markOffline()でノードを一時オフラインに設定
   └─ MonitorMarkedNodeOffline管理モニターに通知
6. 監視結果の記録
   └─ recordに格納してget()で参照可能に
```

### フローチャート

```mermaid
flowchart TD
    A[Timer発火] --> B[triggerUpdate]
    B --> C{進行中の監視あり?}
    C -->|Yes & タイムアウト| D[既存スレッドを中断]
    C -->|Yes & 実行中| E[既存スレッドを返却]
    C -->|No| F[新Recordスレッド生成]
    D --> F
    F --> G[monitor メソッド実行]
    G --> H{各Computerに対して}
    H --> I{Channel接続あり?}
    I -->|No| J[null記録]
    I -->|Yes| K[MasterToSlaveCallable実行]
    K --> L[監視データ取得]
    L --> M{閾値超過?}
    M -->|No| N[正常値記録]
    M -->|Yes| O{ignored?}
    O -->|Yes| N
    O -->|No| P[markOffline]
    P --> Q[MonitorMarkedNodeOffline通知]
    J --> R[次のComputer]
    N --> R
    Q --> R
    R --> H
    H -->|完了| S[record更新]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-13-01 | デフォルト監視間隔 | 60分間隔で全ノードを監視 | 常時 |
| BR-13-02 | タイムアウト | 各ノードの監視は30秒でタイムアウト | 監視実行時 |
| BR-13-03 | オプトアウト動作 | ignoredフラグfalse（デフォルト）で閾値超過時オフライン化 | 閾値超過時 |
| BR-13-04 | 既存監視中断 | 新規監視開始時、タイムアウト超過した既存監視は中断 | triggerUpdate時 |
| BR-13-05 | ディスク閾値デフォルト | DiskSpaceMonitorのデフォルト閾値は1GB | DiskSpaceMonitor |
| BR-13-06 | 時刻差警告 | ClockMonitorで5秒以上の時刻差は警告 | ClockMonitor |

### 計算ロジック

- 監視間隔: `PERIOD = TimeUnit.MINUTES.toMillis(SystemProperties.getInteger("...periodMinutes", 60))`
- タイムアウト判定: `System.currentTimeMillis() > inProgressStarted + getMonitoringTimeOut() + 1000`
- デフォルトタイムアウト: `getMonitoringTimeOut() = TimeUnit.SECONDS.toMillis(30)`

## データベース操作仕様

### 操作別データベース影響一覧

本機能はデータベースではなく、XMLファイルベースの設定永続化を使用。

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| モニター設定保存 | nodeMonitors.xml | UPDATE | NodeMonitorリストの永続化 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | IOException | エージェント通信失敗 | null記録、次回再試行 |
| - | InterruptedException | 監視中断 | 既存データを維持 |
| - | RuntimeException | 予期せぬエラー | ログ出力、次ノードへ継続 |

### リトライ仕様

- 失敗時は次回の定期実行で再試行
- 手動更新はComputerSet画面の「今すぐ更新」ボタンから可能

## トランザクション仕様

- synchronized(AbstractNodeMonitorDescriptor.this)でinProgressスレッドの排他管理
- volatileによるrecordフィールドの可視性保証
- BulkChangeによるモニター設定変更時の一括保存

## パフォーマンス要件

- デフォルト60分間隔の定期実行
- 各ノード監視のタイムアウト: 30秒
- 同時実行される監視スレッドは1つのみ

## セキュリティ考慮事項

- Jenkins.MANAGE権限によるモニター設定変更制限
- MasterToSlaveCallableによるセキュアなリモート呼び出し

## 備考

- 標準搭載のNodeMonitor実装:
  - DiskSpaceMonitor: ワークスペースディスク空き容量
  - TemporarySpaceMonitor: 一時ディレクトリ空き容量
  - SwapSpaceMonitor: スワップ領域使用状況
  - ClockMonitor: マスターとの時刻差
  - ResponseTimeMonitor: 応答時間
  - ArchitectureMonitor: OS/アーキテクチャ情報

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

NodeMonitorの基底クラスと主要な型を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | NodeMonitor.java | `core/src/main/java/hudson/node_monitors/NodeMonitor.java` | 監視の基底クラス、ExtensionPoint |
| 1-2 | AbstractNodeMonitorDescriptor.java | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | 定期実行とデータ管理 |

**読解のコツ**: NodeMonitorはDescribable<NodeMonitor>を実装しており、各モニター実装はDescriptor経由で監視ロジックを持つ。

#### Step 2: エントリーポイントを理解する

監視の起動と結果取得の起点を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NodeMonitor.triggerUpdate() | `core/src/main/java/hudson/node_monitors/NodeMonitor.java` | 監視開始メソッド（108-110行目） |
| 2-2 | NodeMonitor.data() | `core/src/main/java/hudson/node_monitors/NodeMonitor.java` | 結果取得メソッド（94-96行目） |

**主要処理フロー**:
1. **108-110行目**: triggerUpdate()がgetDescriptor().triggerUpdate()に委譲
2. **94-96行目**: data(Computer)がgetDescriptor().get(c)に委譲

#### Step 3: 定期実行と監視ロジックを理解する

AbstractNodeMonitorDescriptorの内部実装を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | schedule() | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | タイマー登録（116-124行目） |
| 3-2 | triggerUpdate() | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | 監視スレッド起動（271-294行目） |
| 3-3 | monitor() | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | 全ノード巡回（165-182行目） |

**主要処理フロー**:
- **57行目**: PERIOD = 60分（システムプロパティで変更可能）
- **116-124行目**: Timer.get().scheduleAtFixedRate()で定期実行
- **271-294行目**: triggerUpdate()で既存スレッド中断判定、Recordスレッド起動
- **165-182行目**: monitor()でJenkins.getComputers()を巡回

#### Step 4: 閾値判定とオフライン処理を理解する

markOffline/markOnlineの処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | markOffline() | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | オフライン処理（247-257行目） |
| 4-2 | markOnline() | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | オンライン復帰（234-238行目） |

**主要処理フロー**:
- **247-257行目**: markOffline()でc.setTemporaryOfflineCause()呼び出し、MonitorMarkedNodeOffline通知
- **234-238行目**: markOnline()でc.setTemporaryOfflineCause(null)呼び出し

#### Step 5: 具体的なモニター実装を理解する

DiskSpaceMonitorを例に具体実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | DiskSpaceMonitor.java | `core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java` | ディスク監視実装 |
| 5-2 | ClockMonitor.java | `core/src/main/java/hudson/node_monitors/ClockMonitor.java` | 時刻同期監視実装 |

### プログラム呼び出し階層図

```
Timer.get() [60分間隔]
    │
    └─ SafeTimerTask.doRun()
           │
           └─ AbstractNodeMonitorDescriptor.triggerUpdate()
                  │
                  ├─ [既存スレッドチェック]
                  │
                  └─ Record.start()
                         │
                         └─ Record.run()
                                │
                                └─ monitor()
                                       │
                                       ├─ Jenkins.get().getComputers() [対象取得]
                                       │
                                       └─ monitor(Computer c) [各ノード監視]
                                              │
                                              ├─ c.getChannel().call(Callable) [リモート実行]
                                              │
                                              └─ [閾値判定]
                                                     │
                                                     └─ markOffline(c, cause)
                                                            │
                                                            └─ c.setTemporaryOfflineCause()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

Timer発火 ─────────────▶ triggerUpdate() ──────────────▶ Recordスレッド
                                │
                                ▼
Jenkins.getComputers() ───────▶ monitor()
                                │
                                ▼
                         [各Computer]
                                │
VirtualChannel ───────────────▶ monitor(Computer c) ───▶ 監視データ(T)
  (リモート実行)                        │
                                │
                                ▼
                         [閾値判定]
                                │
                        ┌───────┴───────┐
                        ▼               ▼
                   markOffline()    record.data
                        │               │
                        ▼               ▼
Computer.setTemporary   ───────────▶ ComputerSet画面
  OfflineCause()                    monitorData列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NodeMonitor.java | `core/src/main/java/hudson/node_monitors/NodeMonitor.java` | ソース | 監視基底クラス |
| AbstractNodeMonitorDescriptor.java | `core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java` | ソース | 定期実行・データ管理 |
| AbstractAsyncNodeMonitorDescriptor.java | `core/src/main/java/hudson/node_monitors/AbstractAsyncNodeMonitorDescriptor.java` | ソース | 非同期監視基底クラス |
| DiskSpaceMonitor.java | `core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java` | ソース | ディスク空き容量監視 |
| TemporarySpaceMonitor.java | `core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java` | ソース | 一時ディレクトリ監視 |
| SwapSpaceMonitor.java | `core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java` | ソース | スワップ領域監視 |
| ClockMonitor.java | `core/src/main/java/hudson/node_monitors/ClockMonitor.java` | ソース | 時刻同期監視 |
| ResponseTimeMonitor.java | `core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java` | ソース | 応答時間監視 |
| ArchitectureMonitor.java | `core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java` | ソース | アーキテクチャ情報取得 |
| MonitorMarkedNodeOffline.java | `core/src/main/java/hudson/node_monitors/MonitorMarkedNodeOffline.java` | ソース | オフライン通知管理モニター |
| MonitorOfflineCause.java | `core/src/main/java/hudson/node_monitors/MonitorOfflineCause.java` | ソース | モニター起因オフライン理由 |
