# 機能設計書 42-InterPodAffinityプラグイン

## 概要

本ドキュメントは、Kubernetesスケジューラーの`InterPodAffinity`プラグインの機能設計を記述する。本プラグインはPod間のAffinity（親和性）およびAnti-Affinity（反親和性）ルールに基づいて、Podの配置制御（フィルタリングとスコアリング）を行う。

### 本機能の処理概要

**業務上の目的・背景**：マイクロサービスアーキテクチャにおいて、特定のPod同士を同じトポロジードメイン（ノード、ゾーン等）に配置したい場合（Affinity）や、障害耐性のために分散配置したい場合（Anti-Affinity）がある。本プラグインはPodのSpec.Affinityに定義されたルールを評価し、適切なノードへの配置を制御する。

**機能の利用シーン**：Pod SpecにpodAffinityまたはpodAntiAffinityが定義されている場合に動作する。例えば、Webサーバーとキャッシュサーバーを同一ゾーンに配置するAffinity、同じDeploymentのPodを異なるノードに分散するAnti-Affinity。

**主要な処理内容**：
1. PreFilter: 既存PodのAnti-Affinityカウント、新規PodのAffinity/Anti-Affinityカウントを並列計算
2. Filter: ノード毎にAffinity/Anti-Affinity制約の充足を判定
3. PreScore: Preferred（ソフト）制約のトポロジースコアを並列計算
4. Score: トポロジースコアに基づくノードスコアリング
5. NormalizeScore: スコアの正規化（0-100範囲に変換）

**関連システム・外部連携**：Namespaceセレクターの評価にNamespace Informerを使用。

**権限による制御**：フィーチャーゲート（EnableSchedulingQueueHint）により動作が制御される。IgnorePreferredTermsOfExistingPodsオプションでスコアリング最適化が可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | スケジューラー内部プラグインとして自動実行 |

## 機能種別

フィルタリング / スコアリング / 配置制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| InterPodAffinityArgs | config.InterPodAffinityArgs | Yes | HardPodAffinityWeight等の設定 | ValidateInterPodAffinityArgs |
| Pod | v1.Pod | Yes | スケジュール対象Pod（Affinity定義含む） | - |
| NodeInfo | fwk.NodeInfo | Yes | ノード情報（ラベル、既存Pod含む） | - |

### 入力データソース

- Pod Spec（Affinity.PodAffinity、Affinity.PodAntiAffinity）
- 全ノードの既存Pod情報（SharedLister経由）
- Namespaceラベル情報（NamespaceLister経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| FilterStatus | fwk.Status | フィルタリング結果 |
| Score | int64 | ノードスコア（正規化前は正負あり、正規化後は0-100） |

### 出力先

スケジューラーフレームワークのCycleState

## 処理フロー

### 処理シーケンス

```
1. PreFilter: Affinity/Anti-Affinityカウントの事前計算
   ├─ getExistingAntiAffinityCounts: 既存PodのAnti-Affinityルールと新規Podの照合（並列）
   └─ getIncomingAffinityAntiAffinityCounts: 新規PodのAffinity/Anti-Affinityルールと既存Podの照合（並列）

2. Filter: ノード毎のAffinity充足チェック
   ├─ satisfyPodAffinity: Affinityルールの充足確認
   ├─ satisfyPodAntiAffinity: Anti-Affinityルールの充足確認
   └─ satisfyExistingPodsAntiAffinity: 既存PodのAnti-Affinityとの整合確認

3. PreScore: Preferred制約のトポロジースコア計算（並列）
   └─ processExistingPod: 既存Pod毎にソフトAffinity/Anti-Affinityスコアを積算

4. Score: トポロジーキーに基づくスコア算出

5. NormalizeScore: Min-Max正規化（0-100）
```

### フローチャート

```mermaid
flowchart TD
    A[PreFilter: カウント事前計算] --> B{Required Affinity/AntiAffinity あり?}
    B -->|No| C[Skip]
    B -->|Yes| D[Filter: ノード毎判定]
    D --> E{Affinity充足?}
    E -->|No| F[UnschedulableAndUnresolvable]
    E -->|Yes| G{AntiAffinity充足?}
    G -->|No| H[Unschedulable]
    G -->|Yes| I{既存PodのAntiAffinity充足?}
    I -->|No| J[Unschedulable]
    I -->|Yes| K[PreScore: Preferred制約スコア計算]
    K --> L[Score: トポロジースコア合算]
    L --> M[NormalizeScore: 0-100に正規化]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | Required Affinityの全マッチ | Affinityの全termにマッチする既存Podが同一トポロジーに存在する必要あり | RequiredDuringScheduling |
| BR-02 | セルフAffinity | Affinity対象Podが自身のみの場合、自身がマッチすれば配置可能 | 初回Pod配置時 |
| BR-03 | Required Anti-Affinityの排他 | Anti-Affinityルールにマッチする既存Podが同一トポロジーに存在してはならない | RequiredDuringScheduling |
| BR-04 | 既存PodのAnti-Affinity尊重 | 新規Podが既存PodのAnti-Affinityにマッチする場合は配置不可 | 常時 |
| BR-05 | Namespace制約 | AffinityTermのNamespaceSelectorを展開してNamespaces集合にマージ | NamespaceSelector指定時 |
| BR-06 | HardPodAffinityWeight | 既存PodのRequired Affinityにマッチする場合のスコア重み | HardPodAffinityWeight > 0 |

### 計算ロジック

**Score**: トポロジーキーとトポロジー値の組み合わせ毎にスコアを蓄積。Affinityマッチは加算、Anti-Affinityマッチは減算。
**NormalizeScore**: `score = MaxNodeScore * (score - minScore) / (maxScore - minScore)`

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

本機能はデータベースへの直接操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UnschedulableAndUnresolvable | Affinity不一致 | Affinityルールを満たせない | 別ノードを検索 |
| Unschedulable | Anti-Affinity不一致 | Anti-Affinityルール違反 | プリエンプション可能 |
| Error | 内部エラー | CycleState読み取り失敗 | 再試行 |

### リトライ仕様

プラグイン自体にリトライ機構はない。

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

トランザクションは使用しない。

## パフォーマンス要件

- PreFilter/PreScoreでParallelizerを使用し、全ノードの既存Pod照合を並列実行
- topologyToMatchedTermCountListにより、マージ処理のイテレーション性能を最適化
- IgnorePreferredTermsOfExistingPodsオプションでスコアリング対象Podを限定可能

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

- Namespace境界を越えたAffinity/Anti-AffinityはNamespaceSelector/Namespacesフィールドで制御

## 備考

- Pod Affinity/Anti-Affinityは計算コストが高いため、大規模クラスターでは慎重に使用すべき
- IgnorePreferredTermsOfExistingPodsの使用推奨

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | filtering.go | `pkg/scheduler/framework/plugins/interpodaffinity/filtering.go` | preFilterState、topologyPair、topologyToMatchedTermCount の定義 |
| 1-2 | scoring.go | `pkg/scheduler/framework/plugins/interpodaffinity/scoring.go` | preScoreState、scoreMap の定義 |

**読解のコツ**: `topologyToMatchedTermCount`はトポロジーペア（キー+値）毎のマッチ数を保持するmap。フィルタリングの中心データ構造。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | plugin.go | `pkg/scheduler/framework/plugins/interpodaffinity/plugin.go` | New関数（107-127行目）、InterPodAffinity構造体 |

#### Step 3: フィルタリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | filtering.go | `pkg/scheduler/framework/plugins/interpodaffinity/filtering.go` | PreFilter（274-309行目）、Filter（412-432行目） |

**主要処理フロー**:
- **274-309行目**: PreFilter - 並列でAffinity/Anti-Affinityカウントを計算
- **352-364行目**: satisfyExistingPodsAntiAffinity
- **367-378行目**: satisfyPodAntiAffinity
- **382-408行目**: satisfyPodAffinity（セルフAffinityの特別処理含む）

#### Step 4: スコアリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | scoring.go | `pkg/scheduler/framework/plugins/interpodaffinity/scoring.go` | PreScore（128-221行目）、Score（240-255行目）、NormalizeScore（258-290行目） |

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

```
New (初期化)
    │
    ├─ PreFilter
    │      ├─ getExistingAntiAffinityCounts (並列)
    │      │      └─ appendWithAntiAffinityTerms
    │      └─ getIncomingAffinityAntiAffinityCounts (並列)
    │             ├─ appendWithAffinityTerms
    │             └─ appendWithAntiAffinityTerms
    │
    ├─ Filter
    │      ├─ satisfyPodAffinity
    │      ├─ satisfyPodAntiAffinity
    │      └─ satisfyExistingPodsAntiAffinity
    │
    ├─ PreScore
    │      └─ processExistingPod (並列)
    │             └─ scoreMap.processTerms
    │
    ├─ Score
    │      └─ topologyScore の合算
    │
    └─ NormalizeScore
           └─ Min-Max正規化
```

### データフロー図

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

Pod.Spec.Affinity        ───▶  PreFilter                   ───▶  preFilterState
既存Pod情報                     (並列カウント計算)                     (affinityCounts, antiAffinityCounts)

preFilterState           ───▶  Filter                      ───▶  FilterStatus
NodeInfo.Labels                (Affinity/AntiAffinity判定)

Pod.Spec.Affinity        ───▶  PreScore                    ───▶  preScoreState.topologyScore
既存Pod情報                     (並列スコア計算)

preScoreState            ───▶  Score + NormalizeScore       ───▶  Score (0-100)
Node.Labels                    (合算 + 正規化)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| plugin.go | `pkg/scheduler/framework/plugins/interpodaffinity/plugin.go` | ソース | プラグイン初期化、イベント登録、QueueingHint |
| filtering.go | `pkg/scheduler/framework/plugins/interpodaffinity/filtering.go` | ソース | PreFilter/Filterの実装 |
| scoring.go | `pkg/scheduler/framework/plugins/interpodaffinity/scoring.go` | ソース | PreScore/Score/NormalizeScoreの実装 |
