# 通知設計書 6-Preempting

## 概要

本ドキュメントは、Kubernetesのkubeletがコンテナをプリエンプト（優先退避）する際に発行する「Preempting」イベント通知の設計を記述する。

### 本通知の処理概要

kubeletのCriticalPodAdmissionHandlerが、クリティカルPodの受け入れのためにリソースを確保する必要がある場合に、優先度の低いPodをエビクト（退避）する。このプリエンプション処理の開始時に、対象PodにWarningイベントとして「Preempting」を記録する。

**業務上の目的・背景**：Kubernetesではシステムクリティカルなポッド（kube-system等）がノードに配置される際、リソースが不足している場合に優先度の低いPodを退避させるプリエンプション機構がある。この通知により、運用者はどのPodがプリエンプトされたか、なぜプリエンプトされたかを把握でき、リソース計画の見直しやPodの優先度設定の調整に活用できる。

**通知の送信タイミング**：kubeletのCriticalPodAdmissionHandler内の`evictPodsToFreeRequests`関数で、プリエンプト対象Podリストの各Podに対してkillPodFunc呼び出しの直前に発行される（`preemption.go` 102行目）。

**通知の受信者**：Kubernetes Event APIを通じて記録されるため、kubectl eventsやモニタリングツールを利用するクラスタ管理者が受信者となる。Warning Eventであるため監視アラート対象となる。

**通知内容の概要**：イベントにはEventType（Warning）、Reason（Preempting）、およびプリエンプションの理由メッセージが含まれる。メッセージにはリソース不足の詳細情報が含まれる。

**期待されるアクション**：運用者はノードのリソース状況を確認し、必要に応じてノードの追加やPodの優先度設定の見直しを行う。プリエンプトされたPodは他のノードに再スケジュールされる可能性がある。

## 通知種別

Kubernetes Eventオブジェクト（API内部通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（Event APIへの書き込み） |
| 優先度 | 高（Warning Event） |
| リトライ | Event API書き込み失敗時はkubelet内部でリトライ |

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

プリエンプト対象のPodに対してEventfを呼び出し、Kubernetes Event APIサーバへ送信する。Podオブジェクトを直接参照として使用する。

## 通知テンプレート

### メール通知の場合

本通知はKubernetes Event APIオブジェクトとして発行されるため、メール通知は該当しない。

### 本文テンプレート

```
{プリエンプション理由メッセージ（リソース不足の詳細）}
```

### 添付ファイル

該当なし。

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| Pod名 | プリエンプト対象Podの名前 | Pod.ObjectMeta.Name | Yes |
| Namespace | 対象PodのNamespace | Pod.ObjectMeta.Namespace | Yes |
| 不足リソース | リソース不足の詳細情報 | insufficientResources.toString() | Yes |
| 要求Pod | リソースを要求しているクリティカルPod | admitPod | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| kubelet内部 | クリティカルPodアドミッション | リソース不足でプリエンプションが必要 | クリティカルPodの受け入れ時にリソースが不足している場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 非リソース理由の拒否 | リソース以外の理由でアドミッション拒否された場合はプリエンプションは行われない |
| プリエンプト対象Podなし | getPodsToPreemptが空リストを返した場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[クリティカルPodアドミッション] --> B{リソース不足?}
    B -->|いいえ| C[アドミッション成功]
    B -->|はい| D[evictPodsToFreeRequests]
    D --> E[getPodsToPreempt]
    E --> F[プリエンプト対象Pod選出]
    F --> G[各Podに対して]
    G --> H[recorder.Eventf: Preempting]
    H --> I[killPodFunc呼び出し]
    I --> J{Kill成功?}
    J -->|成功| K[PodStatus更新: Failed/Preempting]
    J -->|失敗| L[エラーログ出力]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| etcd（Podオブジェクト） | Podリストの取得・優先度判定 | kube-apiserver経由 |
| etcd（Nodeオブジェクト） | ノードリソース情報 | Allocatable等 |

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

#### Podオブジェクト

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| Spec.Priority | Pod優先度の判定 | プリエンプト対象選出時 |
| Spec.Containers[].Resources | リソース要求量 | プリエンプト対象選出時 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| etcd（Eventオブジェクト） | INSERT | Preemptingイベントオブジェクトの作成 |
| etcd（Podオブジェクト） | UPDATE | PodStatus.Phase=Failed, Reason=Preempting |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event.Reason | "Preempting" | イベント理由 |
| INSERT | Event.Type | "Warning" | 警告イベント |
| INSERT | Event.Message | プリエンプション理由 | リソース不足の詳細 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| プリエンプト対象選出失敗 | getPodsToPreemptがエラー | エラーログ出力、アドミッション拒否 |
| Pod Kill失敗 | killPodFuncがエラーを返す | エラーログ出力、次回syncPodで再試行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Pod Kill失敗時は次回のsyncPodループで再試行 |
| リトライ間隔 | Pod同期間隔に依存 |
| リトライ対象エラー | killPodFuncの失敗 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | EventRecorderのバースト制限に依存 |
| 1日あたり上限 | 明示的な上限なし |

### 配信時間帯

制限なし。

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

- プリエンプションメッセージにはリソース不足の詳細が含まれるが、Pod名やリソース量は運用情報であり機密性は低い。
- プリエンプト後のPodStatusにDisruptionTarget条件が設定され、Reason=PodReasonTerminationByKubeletが記録される。

## 備考

- 本イベントはkubelet内の`CriticalPodAdmissionHandler.evictPodsToFreeRequests`関数（`preemption.go` 102行目）で発行される。
- イベント定数は`pkg/kubelet/events/event.go` 26行目で`PreemptContainer = "Preempting"`として定義されている。
- プリエンプト後、PodStatusにPhase=Failed、Reason=Preemptingが設定される（107-108行目）。
- DisruptionTarget条件も設定され、kubeletによる強制終了であることが記録される（109-115行目）。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/kubelet/events/event.go` | 26行目: `PreemptContainer = "Preempting"` の定数定義 |

**読解のコツ**: PreemptContainerはContainer event reason listに含まれるが、実際にはPod単位で発行される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | preemption.go | `pkg/kubelet/preemption/preemption.go` | 94行目: `evictPodsToFreeRequests`関数 |

**主要処理フロー**:
1. **96行目**: getPodsToPreemptでプリエンプト対象Pod選出
2. **100行目**: 対象Podのループ開始
3. **102行目**: recorder.Eventfでプリエンプションイベント発行
4. **105行目**: killPodFuncでPod停止
5. **106-108行目**: PodStatusをFailed/Preemptingに更新
6. **109-115行目**: DisruptionTarget条件設定

#### Step 3: アドミッションハンドラからの呼び出しを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | preemption.go | `pkg/kubelet/preemption/preemption.go` | 86行目: HandleメソッドからevictPodsToFreeRequestsへの呼び出し |

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

```
kubelet.HandlePodAdditions() / kubelet.HandlePodUpdates()
    |
    +-- kubelet.admitPod()
           |
           +-- CriticalPodAdmissionHandler.HandleAdmit()
                  |
                  +-- evictPodsToFreeRequests()         [preemption.go:94]
                         |
                         +-- getPodsToPreempt()
                         +-- recorder.Eventf(Preempting)  [行102]
                         +-- killPodFunc()
                         +-- PodStatus更新(Failed/Preempting)
```

### データフロー図

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

クリティカルPod    ------>  CriticalPodAdmissionHandler   ------>  Kubernetes Event
(アドミッション要求)          |                                      (Type: Warning,
                             +-- リソース不足判定                     Reason: Preempting,
既存Podリスト      ------>   +-- プリエンプト対象選出                  Message: リソース不足詳細)
                             +-- recorder.Eventf()                     |
                             +-- killPodFunc()                   ------>  PodStatus更新
                                                                         (Phase: Failed)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/kubelet/events/event.go` | ソース | イベント理由定数の定義 |
| preemption.go | `pkg/kubelet/preemption/preemption.go` | ソース | プリエンプション処理およびPreemptingイベント記録 |
| kubelet.go | `pkg/kubelet/kubelet.go` | ソース | Podアドミッション処理のトリガー |
