# 機能設計書 38-Podスケジューリング

## 概要

本ドキュメントは、Kubernetes kube-schedulerに含まれるPodスケジューリング機能の機能設計を記述する。未スケジュールのPodを適切なノードに割り当てる中核機能を担当する。

### 本機能の処理概要

**業務上の目的・背景**：Kubernetesにおいて、新しく作成されたPodや再スケジュールが必要なPodに対して、クラスター内の最適なノードを選択し割り当てる。スケジューラーはフィルタリング（Filtering）とスコアリング（Scoring）の2段階アルゴリズムで候補ノードを評価し、最終的にBindingを行う。

**機能の利用シーン**：Podの作成時（Deployment、Job、StatefulSet等によるPod作成含む）に自動的に動作する。スケジューリングは連続的なループで実行され、キューからPodを1つずつ取り出して処理する。

**主要な処理内容**：
1. SchedulingQueueから未スケジュールのPodを取得する（NextPod）
2. スケジューリングプロファイルの選択（Pod.spec.schedulerNameに基づく）
3. スケジューリングサイクルの実行
   - PreFilter → Filter → PostFilter → PreScore → Score → Reserve → Permit
4. バインディングサイクルの実行（非同期）
   - PreBind → Bind → PostBind
5. スケジューリング失敗時のハンドリング（Preemption含む）

**関連システム・外部連携**：kube-apiserver、SchedulingQueue、スケジューラーフレームワーク、スケジューラーExtender

**権限による制御**：Pod、Node、PV/PVC、StorageClass等に対するGet、List、Watch権限。Podに対するBind権限。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | kubectl get pods -o wide | 参照画面 | スケジュール結果（NODE列）の確認 |
| - | kubectl describe pod | 参照画面 | スケジューリングイベントの確認 |

## 機能種別

スケジューリングアルゴリズム / ノード割り当て

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| Pod.Spec | v1.PodSpec | Yes | Podの仕様（リソース要求、Affinity等） | 有効なPod仕様 |
| Pod.Spec.SchedulerName | string | No | 使用するスケジューラー名 | デフォルト: default-scheduler |
| percentageOfNodesToScore | int32 | No | スコアリングするノードの割合 | 0-100、0=adaptive(50-nodes/125) |
| parallelism | int32 | No | 並列処理スレッド数 | デフォルト: 16 |

### 入力データソース

- SchedulingQueue: 未スケジュールPodの優先度付きキュー
- Node Informer: ノード情報のキャッシュ
- Pod Informer: Pod情報のキャッシュ（非終了Pod）
- Snapshot: ノード情報のスナップショット

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Binding | v1.Binding | Podのノード割り当て |
| ScheduleResult | ScheduleResult | スケジューリング結果（SuggestedHost, EvaluatedNodes, FeasibleNodes） |
| Events | Event | スケジューリング結果イベント |

### 出力先

- Kubernetes API Server（Binding作成、Pod Status更新、イベント記録）

## 処理フロー

### 処理シーケンス

```
1. ScheduleOne()が1つのPodのスケジューリングを実行
   └─ NextPod()でキューからPopし、Podを取得
   └─ frameworkForPod()でスケジューラープロファイルを選択
   └─ skipPodSchedule()でスキップ判定
2. schedulingCycle()（同期処理）
   a. スナップショットの更新
   b. SchedulePod() → schedulePod()
      ├─ PreFilter: Pod固有の前処理
      ├─ findNodesThatPassFilters(): ノードフィルタリング
      │   └─ 並列でRunFilterPlugins実行
      ├─ findNodesThatPassExtenders(): Extenderによるフィルタリング
      ├─ prioritizeNodes(): ノードスコアリング
      │   ├─ PreScore → Score → NormalizeScore
      │   └─ Extenderによるスコアリング
      └─ selectHost(): 最高スコアノードの選択
   c. Assume: キャッシュにPodを仮割り当て
   d. RunReservePluginsReserve: リソース予約
   e. RunPermitPlugins: 許可チェック（待機可能）
3. bindingCycle()（非同期・goroutine）
   a. WaitOnPermit: Permit待機
   b. RunPreBindPlugins: バインド前処理
   c. RunBindPlugins: 実際のバインド
   d. RunPostBindPlugins: バインド後処理
```

### フローチャート

```mermaid
flowchart TD
    A[NextPod from Queue] --> B[Select Profile]
    B --> C{Skip?}
    C -->|Yes| D[Done]
    C -->|No| E[schedulingCycle]
    E --> F[PreFilter]
    F --> G[Filter - 並列]
    G --> H{Feasible nodes?}
    H -->|0| I[PostFilter/Preemption]
    H -->|Yes| J[PreScore + Score]
    J --> K[selectHost]
    K --> L[Assume + Reserve]
    L --> M[Permit]
    M --> N[bindingCycle - async]
    N --> O[PreBind → Bind → PostBind]
    I --> P[FailureHandler]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | 直列スケジューリング | ScheduleOneはフィルタリング〜Permitまでは直列で実行する | 常時 |
| BR-38-02 | 非同期バインド | バインディングサイクルはgoroutineで非同期実行する | Permitパス後 |
| BR-38-03 | PercentageOfNodesToScore | 全ノードのうち指定割合のみスコアリングする（adaptive: 50-nodes/125） | ノード数が多い場合 |
| BR-38-04 | minFeasibleNodesToFind | 最低100ノード（または全ノードの5%）はフィルタリング対象とする | フィルタリング時 |
| BR-38-05 | Assume | スケジューリング結果をキャッシュに仮反映し、バインド前に次のPodの処理を開始可能にする | Permit後 |
| BR-38-06 | プラグインメトリクスサンプリング | プラグイン実行時間メトリクスは10%の確率でサンプリングする | 各スケジューリングサイクル |

### 計算ロジック

- PercentageOfNodesToScore: `50 - numNodes/125`（adaptive）、最小5%、最低100ノード
- pluginMetricsSamplePercent: 10（10%サンプリング）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Binding | Pod | CREATE (Binding) | Podのノード割り当て |
| NominatedNode | Pod | PATCH (Status) | Preemption時のNominatedNode設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FitError | スケジュール不可 | フィルタリングで全ノード除外 | PostFilter(Preemption)実行 |
| - | Bind失敗 | API Server通信エラー | Assumeキャッシュからの除去、再キューイング |
| - | Permit拒否 | Permitプラグインが拒否 | FailureHandler呼び出し |

### リトライ仕様

- スケジューリング失敗: handleSchedulingFailureでPodをキューに再追加（backoffあり）
- バインド失敗: handleBindingCycleErrorでAssumeキャッシュからPodを除去し再キューイング

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

Assume → Bind の2段階コミット。Assume後にBindが失敗した場合、Assumeキャッシュからの除去（ロールバック相当）を実行し、Podを再キューイングする。

## パフォーマンス要件

- ScheduleOneは0間隔で連続実行（wait.UntilWithContext(ctx, sched.ScheduleOne, 0)）
- 並列フィルタリング: parallelism設定に基づく（デフォルト16スレッド）
- PodInitialBackoff: 1秒
- PodMaxBackoff: 10秒
- PodMaxInUnschedulablePodsDuration: 5分

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

- PodInformerは非終了Podのみ取得（status.phase != Succeeded/Failed）
- managedFieldsを削除してメモリ使用量を削減
- スケジューラーExtenderはHTTPSで通信

## 備考

- Scheduler.Run()はSchedulingQueueとAPIDispatcherの起動後にScheduleOneループを開始する
- SchedulingQueueはQueueingHintsを使用して、イベントに基づく効率的なPod再キューイングを実現する
- AsyncAPICalls（SchedulerAsyncAPICalls Feature Gate）によりBind等のAPI呼び出しを非同期化可能

---

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

### 推奨読解順序

#### Step 1: スケジューラー全体構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | scheduler.go | `pkg/scheduler/scheduler.go` | Scheduler構造体（68-124行目）でフィールド全体を把握 |

**読解のコツ**: Scheduler構造体はCache、Extenders、NextPod、FailureHandler、SchedulePod、SchedulingQueue等の関数フィールドを持ち、テスト可能な設計になっている。

#### Step 2: 初期化フローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | scheduler.go | `pkg/scheduler/scheduler.go` | New関数（276-454行目）で初期化全体を把握 |

**主要処理**:
- **301-303行目**: InTreeRegistryの作成とOutOfTree Registryのマージ
- **316行目**: internalcache.NewEmptySnapshot()
- **356-374行目**: profile.NewMapでスケジューリングプロファイル作成
- **400-413行目**: SchedulingQueue作成（QueueingHints含む）
- **432-447行目**: Scheduler構造体の初期化

#### Step 3: ScheduleOneの処理フローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | schedule_one.go | `pkg/scheduler/schedule_one.go` | ScheduleOne関数（65-136行目）がメインエントリーポイント |

**主要処理**:
- **67-68行目**: NextPodでキューからPop
- **85行目**: frameworkForPodでプロファイル選択
- **103行目**: NewCycleState作成
- **116行目**: schedulingCycle実行
- **123-135行目**: bindingCycleをgoroutineで非同期実行

#### Step 4: Run関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | scheduler.go | `pkg/scheduler/scheduler.go` | Run関数（532-559行目） |

**主要処理**:
- **534行目**: SchedulingQueue.Run
- **546行目**: go wait.UntilWithContext(ctx, sched.ScheduleOne, 0)
- **552行目**: SchedulingQueue.Close

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

```
New (276行目)
    ├─ NewInTreeRegistry
    ├─ profile.NewMap → Framework作成
    ├─ NewSchedulingQueue
    └─ addAllEventHandlers

Run (532行目)
    ├─ SchedulingQueue.Run
    ├─ APIDispatcher.Run (if enabled)
    └─ ScheduleOne (546行目) [goroutine, 連続実行]
        ├─ NextPod (67行目)
        ├─ frameworkForPod (85行目)
        ├─ schedulingCycle (116行目)
        │   ├─ snapshotSharedLister.UpdateSnapshot
        │   ├─ schedulePod
        │   │   ├─ RunPreFilterPlugins
        │   │   ├─ findNodesThatPassFilters (並列)
        │   │   │   └─ RunFilterPlugins
        │   │   ├─ findNodesThatPassExtenders
        │   │   ├─ prioritizeNodes
        │   │   │   ├─ RunPreScorePlugins
        │   │   │   ├─ RunScorePlugins (並列)
        │   │   │   └─ RunExtenderScore
        │   │   └─ selectHost
        │   ├─ assume
        │   ├─ RunReservePluginsReserve
        │   └─ RunPermitPlugins
        └─ bindingCycle (123行目) [goroutine]
            ├─ WaitOnPermit
            ├─ RunPreBindPlugins
            ├─ RunBindPlugins
            └─ RunPostBindPlugins
```

### データフロー図

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

SchedulingQueue  ──▶  ScheduleOne  ──▶  schedulingCycle  ──▶  Binding
(Pop: NextPod)         │                    │                    │
                  frameworkForPod      ┌────────────┐       Pod割り当て
                       │               │ PreFilter  │
Node Snapshot    ──▶   │               │ Filter     │  ──▶  NominatedNode
                       │               │ PostFilter │       (Preemption時)
                       │               │ Score      │
                       │               │ Reserve    │  ──▶  Events
                       │               │ Permit     │
                       │               └────────────┘
                       │
                  bindingCycle (async)
                       ├─ PreBind
                       ├─ Bind        ──▶  API Server
                       └─ PostBind
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| scheduler.go | `pkg/scheduler/scheduler.go` | ソース | スケジューラー本体・初期化 |
| schedule_one.go | `pkg/scheduler/schedule_one.go` | ソース | ScheduleOne - 1Pod処理 |
| eventhandlers.go | `pkg/scheduler/eventhandlers.go` | ソース | Informerイベントハンドラ |
| backend/queue/ | `pkg/scheduler/backend/queue/` | ソース | SchedulingQueue |
| backend/cache/ | `pkg/scheduler/backend/cache/` | ソース | スケジューラーキャッシュ |
| framework/ | `pkg/scheduler/framework/` | ソース | フレームワーク本体 |
| framework/plugins/ | `pkg/scheduler/framework/plugins/` | ソース | ビルトインプラグイン |
| profile/ | `pkg/scheduler/profile/` | ソース | プロファイル管理 |
| metrics/ | `pkg/scheduler/metrics/` | ソース | メトリクス |
