# 通知設計書 76-FailedScheduling

## 概要

本ドキュメントは、KubernetesクラスタにおいてPodのスケジューリングに失敗した際に発行されるFailedScheduling通知の設計を定義する。

### 本通知の処理概要

FailedScheduling通知は、kube-schedulerがPodに対するスケジューリングを試みたが、適切なノードが見つからない、またはスケジューリングエラーが発生した場合にWarningイベントとして発行される。この通知はスケジューリング失敗の原因を特定するための重要な情報を提供する。また、削除中のPodがスケジュール対象となった場合にもスキップ通知として発行される。

**業務上の目的・背景**：Podがスケジュールできない状態はアプリケーションの可用性に直接影響する。FailedScheduling通知により、リソース不足、ノードのアフィニティ/テイント、PodのPVC依存関係等、スケジュール失敗の原因を管理者が迅速に特定し対応できるようにする。

**通知の送信タイミング**：(1) handleSchedulingFailure()内で、schedulingCycleが失敗した場合に発行される。(2) skipPodSchedule()内で、削除中のPodがスケジュール対象になった場合に発行される。

**通知の受信者**：Kubernetes Event APIを通じてPodオブジェクトに紐付くイベントとして記録される。kubectl describe podやイベント監視ツールを通じてPod所有者やクラスタ管理者が受信する。

**通知内容の概要**：スケジューリング失敗の原因を示すメッセージ（例: ノードのフィルタリング結果、リソース不足の詳細等）が通知される。メッセージはNoteLengthLimitで切り詰められる場合がある。

**期待されるアクション**：クラスタ管理者はメッセージの内容に基づいてリソースの追加、ノードの追加、Pod仕様の修正（リソース要求の調整、アフィニティの修正等）を行う。

## 通知種別

Kubernetes Event（Warning）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（handleSchedulingFailure処理内で発行） |
| 優先度 | 高（Podが起動できない状態） |
| リトライ | Podはスケジューリングキューに再投入され再スケジュールが試行される |

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

スケジュール失敗したPodオブジェクトを送信先として使用する。

## 通知テンプレート

### Kubernetes Eventの場合

| 項目 | 内容 |
|-----|------|
| 送信元コンポーネント | scheduler |
| EventType | Warning |
| Reason | FailedScheduling |
| Action | Scheduling |
| 対象オブジェクト | Pod |

### 本文テンプレート（通常のスケジュール失敗）

```
{truncated error message}
```

### 本文テンプレート（削除中Pod）

```
skip schedule deleting pod: {namespace}/{name}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| error message | スケジューリング失敗の詳細メッセージ | status.Message() (truncateMessageで切り詰め) | Yes |
| namespace | Podのnamespace | pod.Namespace | Yes（削除中Pod） |
| name | Podの名前 | pod.Name | Yes（削除中Pod） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| スケジューリング失敗 | schedulingCycle()の失敗 | status.IsSuccess() == false | handleSchedulingFailure内で発行 |
| Podスキップ | skipPodSchedule() | Pod.DeletionTimestamp != nil | 削除中のPodのスケジュールスキップ |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| スケジューリング成功 | Podが正常にスケジュールされた場合はScheduledイベントが発行される |
| err == nil | テストのみが到達可能なパス |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ScheduleOne開始] --> B[skipPodSchedule?]
    B -->|削除中| C[FailedScheduling Event - skip]
    B -->|No| D[schedulingCycle実行]
    D --> E{成功?}
    E -->|Yes| F[bindingCycleへ]
    E -->|No| G[handleSchedulingFailure]
    G --> H[FailedScheduling Event発行]
    H --> I[PodCondition更新]
    I --> J[スケジューリングキューに再投入]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| Pods (API Server) | Pod情報の参照 | informer cache |
| Nodes (API Server) | ノード情報の参照 | フィルタリング・スコアリング |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Events (etcd) | INSERT | Kubernetes Eventオブジェクトが作成される |
| Pods (etcd) | UPDATE | PodCondition (PodScheduled=False) が更新される |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ErrNoNodesAvailable | クラスタにノードが登録されていない | ノードの追加を待機 |
| FitError | 全ノードでフィルタリング失敗 | PostFilterPlugins（プリエンプション）を試行 |
| スケジューラエラー | 内部エラー | エラーハンドリング後に再スケジュール |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無制限（バックオフ付き） |
| リトライ間隔 | スケジューリングキューのバックオフに基づく |
| リトライ対象エラー | 全てのスケジューリング失敗 |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし

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

- エラーメッセージにPodのリソース要求やノードの状態情報が含まれる場合がある
- NoteLengthLimitで切り詰めが行われるため、過度に長いメッセージは表示されない

## 備考

- メッセージはtruncateMessage()関数でNoteLengthLimitの長さに切り詰められる（行1129-1137）
- PodConditionも同時に更新され、PodScheduled=False, Reason=Unschedulable または SchedulerError が設定される
- NominatingInfoによりプリエンプション候補のノード情報がキューに保存される
- 2つの発行箇所がある: handleSchedulingFailure() と skipPodSchedule()

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schedule_one.go | `pkg/scheduler/schedule_one.go` | 行49-62: スケジューリング定数（minFeasibleNodesToFind等） |

**読解のコツ**: kube-schedulerのFailedSchedulingは定数として定義されておらず、文字列リテラル "FailedScheduling" が直接使用されている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | schedule_one.go | `pkg/scheduler/schedule_one.go` | 行65-136: ScheduleOne()メソッド |
| 2-2 | schedule_one.go | `pkg/scheduler/schedule_one.go` | 行404-410: skipPodSchedule() - 削除中Podのスキップ |

**主要処理フロー**:
1. **行67**: NextPod()でPod取得
2. **行93**: skipPodSchedule()で削除中Podチェック
3. **行407**: 削除中の場合FailedSchedulingイベント発行
4. **行116**: schedulingCycle()実行
5. **行117-119**: 失敗時FailureHandler呼び出し

#### Step 3: handleSchedulingFailureを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | schedule_one.go | `pkg/scheduler/schedule_one.go` | 行1035-1127: handleSchedulingFailure()メソッド |

**主要処理フロー**:
- **行1047-1050**: reason判定（Unschedulable vs SchedulerError）
- **行1069-1077**: エラー種別に応じたログ出力
- **行1096**: スケジューリングキューに再投入
- **行1116-1117**: truncateMessageとFailedSchedulingイベント発行
- **行1118-1126**: PodCondition更新

#### Step 4: truncateMessageを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | schedule_one.go | `pkg/scheduler/schedule_one.go` | 行1129-1137: truncateMessage()関数 |

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

```
kube-scheduler
    |
    +-- Scheduler.ScheduleOne()  [行65]
           |
           +-- skipPodSchedule()  [行93]
           |       +-- EventRecorder().Eventf("FailedScheduling")  [行407] (削除中Pod)
           |
           +-- schedulingCycle()  [行116]
           |       +-- SchedulePod()
           |       +-- RunPostFilterPlugins() (失敗時)
           |
           +-- FailureHandler = handleSchedulingFailure  [行118]
                  |
                  +-- truncateMessage()  [行1116]
                  +-- EventRecorder().Eventf("FailedScheduling")  [行1117]
                  +-- updatePod() (PodCondition更新)  [行1118]
                  +-- AddUnschedulableIfNotPresent()  [行1096]
```

### データフロー図

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

SchedulingQueue            ScheduleOne()
(Pod) ------------------> schedulingCycle()
                           |
                           +-- 失敗時:
                           |   handleSchedulingFailure()
                           |   +---------> FailedScheduling Event
                           |               (Warning, Pod, "Scheduling")
                           |   +---------> PodCondition更新
                           |               (PodScheduled=False)
                           |   +---------> キューに再投入
                           |
                           +-- 削除中Pod:
                               skipPodSchedule()
                               +---------> FailedScheduling Event
                                           (Warning, "skip schedule deleting pod")
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| schedule_one.go | `pkg/scheduler/schedule_one.go` | ソース | スケジューリング処理の主要実装（通知発行元） |
| schedule_one_test.go | `pkg/scheduler/schedule_one_test.go` | テスト | スケジューリングのテスト |
