# 機能設計書 25-Disruption Budgetコントローラー

## 概要

本ドキュメントは、KubernetesのDisruption Budgetコントローラーの機能設計を記述する。本コントローラーはPodDisruptionBudget（PDB）の状態を維持し、自発的な中断時の可用性保証を管理する。

### 本機能の処理概要

**業務上の目的・背景**：ノードメンテナンスやクラスターアップグレード時に、アプリケーションの可用性を保証するため、同時に中断されるPod数を制限する必要がある。PDBコントローラーはPodの健全性を常時監視し、DisruptionsAllowed（許可される中断数）を算出・更新する。

**機能の利用シーン**：kubectl drainによるノードメンテナンス時にPDBの制約に従ってPodを順次退避する。Eviction APIがPDBのDisruptionsAllowedを参照して中断可否を判定する。

**主要な処理内容**：
1. PodDisruptionBudgetオブジェクトの変更を監視する
2. 関連するPodのReadiness状態を集計する
3. ExpectedPods、DesiredHealthy、CurrentHealthy、DisruptionsAllowedを算出する
4. DisruptedPodsの古いエントリを自動クリーンアップする（DeletionTimeout=2分）
5. stalePodDisruptionのDisruptionTarget条件をFalseに戻す

**関連システム・外部連携**：API Server（PDB/Pod/ReplicaSet/Deployment/StatefulSetの取得・更新）、Scale API（期待レプリカ数の取得）、Eviction API（DisruptionsAllowedの参照元）

**権限による制御**：system:controller:disruption-controllerサービスアカウントで動作。PDB、Pod、ReplicaSet、Deployment、StatefulSet、ReplicationControllerの読み取りおよびPDBステータス更新権限を持つ。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | kubectl drain | API連携 | ドレイン時にPDBの制約に従って退避が行われる |

## 機能種別

計算処理 / 状態管理（バックグラウンド自動処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| workers | int | Yes | 同時実行ワーカー数 | 正の整数（デフォルト: Disruption用ワーカー数） |

### 入力データソース

- PDB Informer: PodDisruptionBudgetのadd/update/deleteイベント
- Pod Informer: Podのadd/update/deleteイベント
- ReplicaSet/Deployment/StatefulSet/ReplicationController Informer: ワークロードのレプリカ数取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| PDB.Status.ExpectedPods | int32 | 期待されるPod数（Scale APIからのレプリカ数） |
| PDB.Status.DesiredHealthy | int32 | 必要な健全Pod数（MinAvailableまたはMaxUnavailableから算出） |
| PDB.Status.CurrentHealthy | int32 | 現在の健全Pod数 |
| PDB.Status.DisruptionsAllowed | int32 | 許可される中断数 |
| PDB.Status.DisruptedPods | map[string]metav1.Time | 中断されたPodの追跡マップ |

### 出力先

- API Server: PDBステータス更新
- Kubernetes Events: PDB関連イベント

## 処理フロー

### 処理シーケンス

```
1. Run()で3種類のワーカーを起動する
   └─ queue, recheckQueue, stalePodDisruptionQueue
2. PDB/Podの変更イベントで関連PDBをキューに投入する
3. trySync()でPDBのステータスを計算する
   └─ Pod一覧取得、Scale API経由で期待レプリカ数取得
4. DesiredHealthy/CurrentHealthy/DisruptionsAllowedを算出する
   └─ MinAvailableまたはMaxUnavailableから逆算
5. DisruptedPodsのクリーンアップを行う
   └─ DeletionTimeout(2分)を超過したエントリを除去
6. stalePodDisruptionの条件をクリアする
   └─ stalePodDisruptionTimeout(2分)超過のDisruptionTarget条件をFalseに
```

### フローチャート

```mermaid
flowchart TD
    A[PDB/Pod変更] --> B[関連PDBをqueueに投入]
    B --> C[trySync]
    C --> D[ラベルセレクタでPod一覧取得]
    D --> E[Scale APIで期待レプリカ数取得]
    E --> F{MinAvailable or MaxUnavailable?}
    F -->|MinAvailable| G[desiredHealthy = minAvailable]
    F -->|MaxUnavailable| H[desiredHealthy = expectedCount - maxUnavailable]
    G --> I[currentHealthy = Ready Podカウント]
    H --> I
    I --> J[disruptionsAllowed = max 0, currentHealthy - desiredHealthy]
    J --> K[DisruptedPodsクリーンアップ]
    K --> L[PDBステータス更新]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-25-01 | MinAvailable | DesiredHealthy = MinAvailable（数値またはパーセンテージ） | MinAvailable指定時 |
| BR-25-02 | MaxUnavailable | DesiredHealthy = ExpectedPods - MaxUnavailable | MaxUnavailable指定時 |
| BR-25-03 | DisruptionsAllowed | max(0, CurrentHealthy - DesiredHealthy) | 常時 |
| BR-25-04 | DeletionTimeout | DisruptedPodsエントリは2分後にタイムアウト | DisruptedPods管理時 |
| BR-25-05 | StalePodDisruption | DisruptionTarget条件が2分以上staleの場合Falseに設定 | Pod条件管理時 |
| BR-25-06 | フェイルセーフ | エラー発生時はDisruptionsAllowed=0に設定 | trySync失敗時 |

### 計算ロジック

- **DisruptionsAllowed**: `max(0, currentHealthy - desiredHealthy)`
- **MinAvailable(%)**: `desiredHealthy = ceil(expectedCount * minAvailable%)`
- **MaxUnavailable(%)**: `desiredHealthy = expectedCount - floor(expectedCount * maxUnavailable%)`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| PDB取得 | PDB（etcd） | SELECT | PDBオブジェクトの取得 |
| Pod取得 | Pod（etcd） | SELECT | ラベルセレクタによるPod一覧 |
| Scale取得 | Scale（etcd） | SELECT | 期待レプリカ数の取得 |
| PDBステータス更新 | PDB（etcd） | UPDATE | Status全フィールドの更新 |
| Pod条件パッチ | Pod（etcd） | UPDATE（Patch） | DisruptionTarget条件の更新 |

### テーブル別操作詳細

#### PodDisruptionBudget（etcd経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | status.expectedPods | Scale APIのレプリカ数 | UpdateStatusサブリソース |
| UPDATE | status.desiredHealthy | MinAvailable/MaxUnavailableから算出 | UpdateStatusサブリソース |
| UPDATE | status.currentHealthy | Ready Podカウント | UpdateStatusサブリソース |
| UPDATE | status.disruptionsAllowed | max(0, currentHealthy - desiredHealthy) | UpdateStatusサブリソース |
| UPDATE | status.disruptedPods | タイムアウトエントリ除去後のマップ | UpdateStatusサブリソース |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NotFound | API応答 | PDBが削除済み | ログ出力し正常終了 |
| Scale API失敗 | API応答 | 期待レプリカ数の取得失敗 | フェイルセーフ（DisruptionsAllowed=0） |
| ステータス更新失敗 | API応答 | PDBステータス更新の競合 | RateLimitingキューでリトライ |

### リトライ仕様

- syncHandler失敗時: RateLimitingキューでリトライ
- recheckQueue: DeletionTimeout(2分)後にPDBを再チェック
- stalePodDisruptionQueue: stalePodDisruptionTimeout(2分)後にPod条件を再チェック
- フェイルセーフ: エラー時はDisruptionsAllowed=0を設定（安全側に倒す）

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

PDBステータスの更新はUpdateStatusサブリソースAPIで行われ、ResourceVersionによる楽観的並行性制御が適用される。Pod条件のパッチも同様。3種類のキュー（queue、recheckQueue、stalePodDisruptionQueue）が独立して動作する。

## パフォーマンス要件

- PDBの同期はイベント駆動で実行される
- recheckQueueはDeletionTimeout後のみ処理
- 各PDB同期ではラベルセレクタによるPod検索が必要なため、大規模Namespace時にはレイテンシが増加する可能性がある

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

- PDBコントローラーはPDBステータスの更新のみを行い、Podの削除は行わない
- Eviction APIがDisruptionsAllowedを参照して中断可否を判定するため、正確なステータス維持が重要
- フェイルセーフ機能により、エラー時は安全側（中断禁止）に倒す

## 備考

- DeletionTimeout=2分はAPI Server-コントローラー間のレイテンシを考慮した値
- stalePodDisruptionTimeout=2分でstaleなDisruptionTarget条件を自動クリーンアップ
- 時刻の正確性が重要なため、クラスター内のNTP同期が推奨される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | disruption.go | `pkg/controller/disruption/disruption.go` | DisruptionController構造体（81-120行目）。3つのキュー（queue、recheckQueue、stalePodDisruptionQueue）、各種Lister、clock |
| 1-2 | disruption.go | `pkg/controller/disruption/disruption.go` | 定数: DeletionTimeout=2分（69行目）、stalePodDisruptionTimeout=2分（76行目） |

**読解のコツ**: recheckQueueはDelayingQueueであり、DeletionTimeout後にPDBを再処理するために使用される。stalePodDisruptionQueueはPodのstale条件クリーンアップ用。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | disruption.go | `pkg/controller/disruption/disruption.go` | Run()でキャッシュ同期後、3種類のワーカーを起動。queue用、recheckQueue用、stalePodDisruptionQueue用 |

#### Step 3: 同期処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | disruption.go | `pkg/controller/disruption/disruption.go` | trySync()でPDBステータスの算出。expectedCount、desiredHealthy、currentHealthy、disruptionsAllowedの計算 |

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

```
DisruptionController.Run()
    │
    ├─ worker(queue)
    │      └─ trySync()
    │             ├─ Pod一覧取得（ラベルセレクタ）
    │             ├─ Scale API Get（期待レプリカ数）
    │             ├─ DisruptionsAllowed算出
    │             └─ PDBステータス更新
    │
    ├─ worker(recheckQueue)
    │      └─ trySync() ── DeletionTimeout後の再チェック
    │
    └─ worker(stalePodDisruptionQueue)
           └─ Pod DisruptionTarget条件のクリア
```

### データフロー図

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

PDB/Podイベント ────────▶ trySync()
(Informer)                  │
                           ├─ Pod一覧 + Scale API ──▶ 可用性計算
                           │
                           └─▶ API Server UpdateStatus ──▶ PDBステータス
                                (DisruptionsAllowed等)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| disruption.go | `pkg/controller/disruption/disruption.go` | ソース | Disruptionコントローラーのメインロジック |
