# 通知設計書 130-SystemOOM

## 概要

本ドキュメントは、kubelet の OOM Watcher が発行する `SystemOOM` イベント通知の設計を記述する。Linux ノードでシステムレベルの OOM（Out of Memory）イベントが検出された際に発行される警告イベントである。

### 本通知の処理概要

本通知は、kubelet の OOM Watcher が cAdvisor の OOM パーサーを通じてカーネルの OOM イベントを監視し、システムレベルの OOM が発生した際にイベントを記録する処理である。

**業務上の目的・背景**：システムレベルの OOM は、ノード上のメモリリソースが枯渇し、カーネルの OOM Killer がプロセスを強制終了する重大なイベントである。この通知により管理者はノードのメモリ不足を早期に検知し、リソース計画の見直しや問題プロセスの特定を行える。

**通知の送信タイミング**：OOM Watcher が cAdvisor の OomParser から OOM イベントをストリーミング受信し、VictimContainerName が "/" （ルートcgroup = システムレベル）のイベントを検出した時点で発行される。

**通知の受信者**：Kubernetes Event として Node オブジェクト（ref引数で渡されるObjectReference）に対して記録される。クラスタ管理者が参照する。

**通知内容の概要**：「System OOM encountered」のメッセージと、可能であれば被害プロセス名とPIDが記録される。

**期待されるアクション**：クラスタ管理者はノードのメモリ使用状況を確認し、メモリリソースの増加、Pod のリソースリミット設定の見直し、またはノードの追加を検討する。

## 通知種別

Kubernetes Event（Warning イベント）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（goroutine でOOMストリームを監視） |
| 優先度 | 高（Warning - システムリソース枯渇） |
| リトライ | なし |

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

Node オブジェクト（Start関数の ref 引数で渡される ObjectReference）に対してイベントが記録される。

## 通知テンプレート

### メール通知の場合

本通知はKubernetes Eventであり、メール送信は行わない。

### 本文テンプレート

```
# プロセス情報あり
System OOM encountered, victim process: <ProcessName>, pid: <Pid>

# プロセス情報なし
System OOM encountered
```

### 添付ファイル

該当なし。

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| ProcessName | OOM Killer によって終了されたプロセス名 | event.ProcessName | No |
| Pid | 終了されたプロセスのPID | event.Pid | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| カーネルイベント | OOM Killer発動 | event.VictimContainerName == "/" | システムレベルのOOM（ルートcgroup） |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| VictimContainerName != "/" | コンテナレベルのOOMはこのイベントでは報告されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[kubelet起動] --> B[OOM Watcher Start]
    B --> C[goroutine起動]
    C --> D[oomStreamer.StreamOoms - チャネル監視]
    D --> E{OOMイベント受信}
    E --> F{VictimContainerName == /}
    F -->|Yes| G{プロセス情報あり?}
    F -->|No| H[スキップ]
    G -->|Yes| I[メッセージにプロセス名とPID追加]
    G -->|No| J[基本メッセージのみ]
    I --> K[Warning Eventf SystemOOM]
    J --> K
    H --> D
    K --> D
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| カーネルOOMログ | OOMイベントの検出 | cAdvisor OomParser経由で/dev/kmsgを監視 |

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

#### OomInstance（cAdvisor）

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| VictimContainerName | システムレベルOOMの判定 | "/" と一致するか |
| ProcessName | 被害プロセス名 | イベントメッセージに使用 |
| Pid | 被害プロセスPID | イベントメッセージに使用 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Event（API リソース） | INSERT | Warning イベントの記録 |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event | Warning / SystemOOM | Kubernetes Event APIを通じて記録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| OOMストリーム停止 | outStreamチャネルが閉じられた | エラーログ出力（92行目） |
| OomParser初期化失敗 | oomparser.New()がエラーを返す | NewWatcher関数がエラーを返却 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（ストリーム監視は継続） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Kubernetes Event APIのデフォルトレート制限に従う |
| 1日あたり上限 | 制限なし |

### 配信時間帯

特に制限なし。OOMイベント発生時に即座に記録される。

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

本イベントにはプロセス名とPIDが含まれる。ノード上で実行中のプロセス情報が露出するが、Kubernetes RBAC によりイベントの閲覧権限が制御される。

## 備考

- Linux専用機能（`//go:build linux` ビルドタグ指定、1行目）
- cAdvisor の OomParser を使用してカーネルの /dev/kmsg からOOMイベントを検出する
- recordEventContainerName = "/" は、ルートcgroup（システム全体）を表す
- FakeRecorder使用時はnilを返す（テスト用考慮、50-53行目）
- outStreamチャネルのバッファサイズは10（75行目）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | oom_watcher_linux.go | `pkg/kubelet/oom/oom_watcher_linux.go` | realWatcher構造体（39-42行目）、streamerインターフェース（33-35行目） |
| 1-2 | oom_watcher_linux.go | `pkg/kubelet/oom/oom_watcher_linux.go` | systemOOMEvent定数（69行目）とrecordEventContainerName定数（70行目） |

**読解のコツ**: cAdvisor の `oomparser.OomInstance` 構造体がOOMイベントのデータを運ぶ。VictimContainerName が "/" の場合にシステムレベルOOMと判定される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | oom_watcher_linux.go | `pkg/kubelet/oom/oom_watcher_linux.go` | NewWatcher関数（48-66行目）でOomParserの初期化 |
| 2-2 | oom_watcher_linux.go | `pkg/kubelet/oom/oom_watcher_linux.go` | Start関数（74-95行目）でOOMストリーム監視の開始 |

**主要処理フロー**:
1. **48行目**: NewWatcher でOomParser を初期化
2. **74行目**: Start関数でgoroutineを2つ起動
3. **76行目**: oomStreamer.StreamOoms(outStream) でOOMストリーム開始
4. **82行目**: outStream からイベントを受信するループ
5. **83行目**: event.VictimContainerName == recordEventContainerName のチェック
6. **85-88行目**: メッセージ構築（プロセス情報の有無で分岐）
7. **89行目**: recorder.Eventf(ref, Warning, "SystemOOM", eventMsg) でイベント発行

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

```
kubelet起動
    |
    +-- NewWatcher (48行目)
    |      +-- oomparser.New()
    |
    +-- realWatcher.Start (74行目)
           |
           +-- [goroutine 1] oomStreamer.StreamOoms(outStream)
           |
           +-- [goroutine 2] イベント受信ループ
                  |
                  +-- event.VictimContainerName == "/" チェック (83行目)
                  +-- recorder.Eventf("SystemOOM") (89行目)
```

### データフロー図

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

/dev/kmsg ───> cAdvisor OomParser ───> StreamOoms ───> Event記録
(カーネルログ)   (OOMイベント検出)       (チャネル送信)    (SystemOOM)
                                           |
                                           v
                                    VictimContainerName
                                    == "/" フィルタ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| oom_watcher_linux.go | `pkg/kubelet/oom/oom_watcher_linux.go` | ソース | realWatcher, Start, SystemOOMイベント発行 |
| oom_watcher_unsupported.go | `pkg/kubelet/oom/oom_watcher_unsupported.go` | ソース | 非Linux環境用のスタブ |
| oom_watcher_linux_test.go | `pkg/kubelet/oom/oom_watcher_linux_test.go` | テスト | OOM Watcherのテスト |
