# 通知設計書 31-Rebooted

## 概要

本ドキュメントは、Kubernetesクラスタにおいてノードが再起動された際に発行されるRebootedイベント通知の設計を記述する。kubeletがノードステータス更新時にBootIDの変化を検知し、ノード再起動を通知する仕組みについて詳述する。

### 本通知の処理概要

Rebooted通知は、kubeletがノードのステータスを定期的に更新する過程で、前回記録されたBootIDと現在のBootIDが異なることを検知した場合に発行されるWarningイベントである。この通知は1回のみ発行され、重複発行を防止するためsync.Onceメカニズムが使用される。

**業務上の目的・背景**：ノードが再起動された事実をKubernetesクラスタの運用者に通知することが目的である。ノード再起動はPodの再スケジューリングやデータ損失の可能性を伴うため、運用上重要なイベントとして記録される。予期しない再起動の検出やインシデント対応のトリガーとして活用される。

**通知の送信タイミング**：kubeletがノードステータスの更新処理（MachineInfo取得）を実行する際、前回のBootIDと現在のBootIDを比較し、変化が検知された時点で1回のみ送信される。具体的にはkubeletの起動直後のノードステータス同期処理で検知される。

**通知の受信者**：Kubernetes APIサーバを通じてイベントが記録され、ノードオブジェクトに紐づけられる。クラスタ管理者、モニタリングシステム、およびkubectl get eventsを実行するユーザが受信者となる。

**通知内容の概要**：イベントにはノード名と新しいBootIDが含まれる。メッセージ形式は「Node {ノード名} has been rebooted, boot id: {新しいBootID}」である。

**期待されるアクション**：クラスタ管理者は再起動の原因を調査し、予期しない再起動であればインシデント対応を開始する。また、ノード上で稼働していたPodの状態を確認し、必要に応じて手動でのリカバリ操作を行う。

## 通知種別

Kubernetes Event（Warning）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（ノードステータス更新処理内で発行） |
| 優先度 | 高（Warning EventType） |
| リトライ | なし（sync.Onceにより1回のみ発行） |

### 送信先決定ロジック

イベントはkubeletが管理するノードオブジェクト（nodeRef）に対して記録される。送信先はKubernetes APIサーバであり、イベントオブジェクトとして永続化される。受信者は明示的に指定されず、APIサーバ経由でイベントを監視するすべてのコンポーネント（モニタリングツール、kubectl等）が取得可能である。

## 通知テンプレート

### メール通知の場合

本通知はKubernetes Eventとして発行されるため、メール通知は直接行わない。メール通知が必要な場合は外部の監視ツール（Alertmanager等）を介して設定する。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（Kubernetes Event） |
| 送信元名称 | kubelet |
| 件名 | N/A |
| 形式 | Kubernetes Event Object |

### 本文テンプレート

```
Node %s has been rebooted, boot id: %s
```

- 第1引数: ノード名（nodeName）
- 第2引数: 新しいBootID（info.BootID）

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | 添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| nodeName | ノード名 | kubelet設定（Kubelet.nodeName） | Yes |
| info.BootID | 新しいBootID | cAdvisor MachineInfo.BootID | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ノードステータス更新 | MachineInfo取得時のBootID比較 | 前回BootIDと現在BootIDが異なる場合 | kubeletのノードステータス定期更新処理で検知 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| sync.Once制御 | 同一kubeletプロセスのライフサイクル内で1回のみ発行される。rebootEvent変数（sync.Once型）により制御 |
| 初回BootIDが空 | node.Status.NodeInfo.BootIDが空文字列の場合（ノードの初回ステータス設定時）は発行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[kubelet ノードステータス更新処理開始] --> B[cAdvisor MachineInfo取得]
    B --> C{前回BootIDが空でないか?}
    C -->|空| D[BootIDを記録して終了]
    C -->|空でない| E{前回BootID != 現在BootID?}
    E -->|同一| F[変更なし、終了]
    E -->|異なる| G[rebootEvent.Do実行]
    G --> H{sync.Onceで既に実行済み?}
    H -->|未実行| I[Warning Eventを記録]
    H -->|実行済み| J[スキップ]
    I --> K[BootIDをnode.Status.NodeInfo.BootIDに記録]
    J --> K
    D --> L[終了]
    F --> L
    K --> L
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| Node.Status.NodeInfo | 前回のBootID取得 | Kubernetes APIオブジェクト |
| cAdvisor MachineInfo | 現在のBootID取得 | ローカルシステム情報 |

### テーブル別参照項目詳細

#### Node.Status.NodeInfo

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| BootID | 前回記録されたBootIDとの比較 | ノードステータス更新時に自動参照 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Node.Status.NodeInfo.BootID | UPDATE | 新しいBootIDで更新 |
| Events | INSERT | Rebootedイベントの記録 |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event.Reason | "Rebooted" | Kubernetes Eventオブジェクト |
| INSERT | Event.Type | "Warning" | EventTypeWarning |
| INSERT | Event.Message | "Node {name} has been rebooted, boot id: {id}" | フォーマット済みメッセージ |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| MachineInfo取得失敗 | cAdvisorからの情報取得エラー | エラーログ出力、BootIDの比較処理はスキップ |
| Event記録失敗 | APIサーバへのイベント記録エラー | EventRecorderの内部キューによりリトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | EventRecorderの内部キュー機構に依存 |
| リトライ間隔 | EventRecorderの設定に依存 |
| リトライ対象エラー | APIサーバへのイベント書き込み失敗 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 1件（sync.Onceにより生涯1回のみ） |
| 1日あたり上限 | 1件 |

### 配信時間帯

時間帯の制限なし。BootIDの変化が検知された時点で即時発行される。

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

- BootIDはシステム固有の識別子であるが、機密情報には分類されない
- イベントはKubernetes RBACにより閲覧権限が制御される
- ノード再起動情報はクラスタセキュリティ監査に有用である

## 備考

- sync.Onceによる制御のため、kubeletプロセスが再起動しない限り同じBootID変化に対して複数回のイベントは発行されない
- BootIDは /proc/sys/kernel/random/boot_id から取得される（Linux環境）
- この通知はNodeオブジェクトに対して発行される（Podオブジェクトではない）

---

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

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

### 推奨読解順序

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

まず、イベント定数の定義とノードステータスに含まれるBootIDフィールドの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/kubelet/events/event.go` | 74行目: `NodeRebooted = "Rebooted"` 定数定義を確認 |
| 1-2 | types.go（v1.NodeSystemInfo） | `staging/src/k8s.io/api/core/v1/types.go` | NodeSystemInfo構造体のBootIDフィールドを確認 |

**読解のコツ**: Kubernetesのイベント定数はpkg/kubelet/events/event.goに集約されている。イベント名の文字列値がそのままKubernetes Eventのreasonフィールドに使用される。

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

BootID比較とイベント発行のロジックはnodestatus/setters.goのMachineInfo Setterに実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | setters.go | `pkg/kubelet/nodestatus/setters.go` | 243-254行目: BootID比較とRebootedイベント発行ロジック |

**主要処理フロー**:
1. **243行目**: `if node.Status.NodeInfo.BootID != "" && node.Status.NodeInfo.BootID != info.BootID` - 前回のBootIDが空でなく、かつ変化があるかを判定
2. **249行目**: `rebootEvent.Do(func() {` - sync.Onceにより1回のみ実行を保証
3. **250行目**: `recordEventFunc(v1.EventTypeWarning, events.NodeRebooted, ...)` - Warningイベントを記録
4. **254行目**: `node.Status.NodeInfo.BootID = info.BootID` - 新しいBootIDで更新

#### Step 3: sync.Once制御を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | setters.go | `pkg/kubelet/nodestatus/setters.go` | 61行目: `var rebootEvent sync.Once` - パッケージレベルのsync.Once変数 |

**主要処理フロー**:
- **61行目**: rebootEvent変数はパッケージレベルで定義され、kubeletプロセスのライフサイクル全体で1回のみイベント発行を保証する

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

```
kubelet.syncNodeStatus()
    |
    +-- nodestatus.MachineInfo() (Setter関数を返す)
           |
           +-- machineInfoFunc() [cAdvisor.MachineInfo()]
           |       |
           |       +-- BootID取得 (/proc/sys/kernel/random/boot_id)
           |
           +-- BootID比較
           |       |
           |       +-- rebootEvent.Do()
           |               |
           |               +-- recordEventFunc(Warning, "Rebooted", message)
           |                       |
           |                       +-- EventRecorder.Eventf()
           |
           +-- node.Status.NodeInfo.BootID = info.BootID
```

### データフロー図

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

/proc/sys/kernel/          nodestatus.MachineInfo              Kubernetes Event
random/boot_id    ----->   Setter (setters.go:243-254)  ----> (Reason: "Rebooted",
                           BootID比較 + sync.Once制御          Type: Warning)
                                                         |
node.Status.NodeInfo       BootID更新                    +--> node.Status.NodeInfo
.BootID (前回値)   ----->   (setters.go:254)             ----> .BootID (更新後)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/kubelet/events/event.go` | ソース | イベント定数定義（NodeRebooted） |
| setters.go | `pkg/kubelet/nodestatus/setters.go` | ソース | BootID比較とイベント発行ロジック |
| kubelet_node_status.go | `pkg/kubelet/kubelet_node_status.go` | ソース | ノードステータス同期の呼び出し元 |
| setters_test.go | `pkg/kubelet/nodestatus/setters_test.go` | テスト | Rebooted通知のテストケース |
