# 機能設計書 24-PodGCコントローラー

## 概要

本ドキュメントは、KubernetesのPodGCコントローラーの機能設計を記述する。PodGCコントローラーは、削除されたノードに紐づく不要なPodや完了したPodのガベージコレクションを行う。

### 本機能の処理概要

**業務上の目的・背景**：ノード障害やノード削除後に孤立したPod、完了済みPod、サービス停止ノード上で滞留するPodを自動的にクリーンアップし、クラスターリソースの効率的な利用とetcdの肥大化防止を実現する。

**機能の利用シーン**：ノードがクラスターから削除された後に残存するPodの自動削除。terminatedPodThresholdを超過した完了済みPodの自動削除。out-of-serviceノード上のTerminating Podの強制削除。未スケジュールのTerminating Podの強制削除。

**主要な処理内容**：
1. 全Podと全Nodeを定期的にリストする（gcCheckPeriod=20秒間隔）
2. gcTerminated: 完了Pod数がterminatedPodThresholdを超過した分を削除する
3. gcTerminating: out-of-serviceノード上のTerminating Podを削除する
4. gcOrphaned: 存在しないノードに紐づくPodを削除する（quarantine後）
5. gcUnscheduledTerminating: 未スケジュールのTerminating Podを削除する

**関連システム・外部連携**：API Server（Pod/Nodeの取得・削除・パッチ操作）

**権限による制御**：PodGCコントローラーはsystem:controller:pod-garbage-collectorサービスアカウントで動作し、Pod/Nodeのget、list、watch、delete、patch権限を持つ。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本コントローラーに直接関連する画面はない |

## 機能種別

ガベージコレクション（バックグラウンド定期処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| terminatedPodThreshold | int | Yes | 完了Podの保持閾値（0の場合gcTerminatedは無効） | 0以上の整数 |

### 入力データソース

- Pod Lister: 全Podの一覧取得
- Node Lister: 全Nodeの一覧取得
- API Server: ノード存在確認のGET呼び出し（quarantine後）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Podステータスパッチ | PATCH API呼び出し | Phase=Failedへのステータス変更 |
| Pod削除 | DELETE API呼び出し | 不要Podの強制削除（GracePeriod=0） |

### 出力先

- API Server: Podのステータスパッチおよび削除操作
- Prometheusメトリクス: deleting_pods_total、deleting_pods_error_total

## 処理フロー

### 処理シーケンス

```
1. Run()でGCコントローラーを起動する
   └─ キャッシュ同期待機後、gcCheckPeriod(20秒)間隔でgc()を実行
2. gc()で全Podと全Nodeをリストする
   └─ podLister.List(labels.Everything()), nodeLister.List(labels.Everything())
3. gcTerminated(): 完了Pod数がthresholdを超過した分を削除
   └─ Evicted Podを優先、CreationTimestampの古い順にソートして削除
4. gcTerminating(): out-of-serviceノード上のTerminating Podを削除
   └─ ノードがNotReadyかつout-of-service Taintを持つ場合
5. gcOrphaned(): 存在しないノードに紐づくPodを削除
   └─ quarantineTime(40秒)後にAPI Serverで確認し、DisruptionTarget条件を付与
6. gcUnscheduledTerminating(): 未スケジュールのTerminating Podを削除
   └─ DeletionTimestampありかつNodeName空のPod
```

### フローチャート

```mermaid
flowchart TD
    A[gc開始 20秒間隔] --> B[全Pod/Nodeリスト取得]
    B --> C{terminatedPodThreshold > 0?}
    C -->|Yes| D[gcTerminated: 超過分の完了Pod削除]
    C -->|No| E[スキップ]
    D --> F[gcTerminating: OOS上のPod削除]
    E --> F
    F --> G[gcOrphaned: 孤立Pod削除]
    G --> H[gcUnscheduledTerminating: 未スケジュールPod削除]

    D --> D1[Evicted優先+作成日時順ソート]
    D1 --> D2[並行goroutineで削除]

    G --> G1[quarantine 40秒待機]
    G1 --> G2{API Serverでノード確認}
    G2 -->|存在しない| G3[DisruptionTarget条件付与して削除]
    G2 -->|存在する| G4[スキップ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-24-01 | 完了Pod閾値 | terminatedPodThreshold超過分のPodのみ削除。Evicted Pod優先 | gcTerminated |
| BR-24-02 | OOSノードPod削除 | ノードがNotReadyかつnode.kubernetes.io/out-of-service Taintを持つ場合にTerminating Podを削除 | gcTerminating |
| BR-24-03 | 孤立Podのquarantine | 不明ノードは40秒間quarantineし、API Server GETで存在確認後に削除 | gcOrphaned |
| BR-24-04 | DisruptionTarget条件 | 孤立Pod削除時にDisruptionTarget条件（Reason: DeletionByPodGC）を付与 | gcOrphaned |
| BR-24-05 | Phase=Failedパッチ | 削除前にPodのPhaseをFailedに変更（Succeeded/Failed以外の場合） | 全削除操作 |
| BR-24-06 | GracePeriod=0 | 全ての削除はGracePeriodSeconds=0で強制削除 | 全削除操作 |

### 計算ロジック

- **削除数計算**: `deleteCount = terminatedPodCount - terminatedPodThreshold`
- **ソート順**: Evicted Podが先、次にCreationTimestampの昇順、同一の場合はName順

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Pod一覧 | Pod（etcd） | SELECT（List） | 全Podの取得 |
| Node一覧 | Node（etcd） | SELECT（List） | 全Nodeの取得 |
| Node存在確認 | Node（etcd） | SELECT（Get） | quarantine後のノード存在確認 |
| Podステータスパッチ | Pod（etcd） | UPDATE（Patch） | Phase=Failedへの変更 |
| Pod削除 | Pod（etcd） | DELETE | GracePeriod=0での強制削除 |

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

#### Pod（etcd経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| PATCH | status.phase | v1.PodFailed | Succeeded/Failed以外の場合のみ |
| PATCH | status.conditions | DisruptionTarget=True | gcOrphanedの場合のみ |
| DELETE | - | UID一致、GracePeriodSeconds=0 | 強制削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| List失敗 | API応答 | Pod/Node一覧の取得失敗 | ログ出力しgcサイクルをスキップ |
| Get失敗 | API応答 | ノード存在確認のGET失敗 | ログ出力し次回サイクルで再確認 |
| Patch失敗 | API応答 | Podステータスパッチ失敗 | エラーログ出力、メトリクスインクリメント |
| Delete失敗 | API応答 | Pod削除失敗 | エラーログ出力、メトリクスインクリメント |

### リトライ仕様

- gcは20秒間隔の定期実行のため、失敗した場合は次回サイクルで再試行される
- 個別のPod削除失敗はエラーログとメトリクス記録のみで、即時リトライは行わない
- orphanedのquarantineキューはDelayingQueueで管理され、40秒後に処理される

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

Podのステータスパッチと削除は2段階で行われる。パッチ（Phase=Failed）が成功した後に削除を実行する。パッチが失敗した場合は削除も行わない。各Pod削除操作は並行goroutineで実行され、sync.WaitGroupで完了を待機する。

## パフォーマンス要件

- gcCheckPeriod=20秒間隔で全Pod/Nodeをリスト取得
- quarantineTime=40秒でorphaned判定
- 並行goroutineによるPod削除で大量Pod処理時のスループットを確保
- メトリクスによりGC動作の監視が可能

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

- PodGCコントローラーはPodの強制削除権限を持つ
- Phase=Failedへの書き換えにより、Podが適切な終了状態を記録する
- UID一致によるpreconditionで誤削除を防止

## 備考

- gcTerminating(OOSノード処理)はNode Lifecycleコントローラーと連携する
- byEvictionAndCreationTimestampソートにより、Evicted Podを優先削除して通常の完了Podの残存を優先する
- quarantineメカニズムはInformerのキャッシュ遅延に対応するための仕組み

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | PodGCController構造体（54-67行目）。kubeClient、podLister、nodeLister、nodeQueue（DelayingQueue）、terminatedPodThresholdを保持 |
| 1-2 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | 定数: gcCheckPeriod=20秒（48行目）、quarantineTime=40秒（51行目） |

**読解のコツ**: nodeQueueはworkqueue.TypedDelayingInterfaceであり、quarantineTime後に処理されるDelayed Itemを管理する。RateLimitingQueueではなくDelayingQueueである点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | NewPodGCInternal（75-92行目）。各Informerからlister/synced取得、nodeQueue初期化、メトリクス登録 |
| 2-2 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | Run（94-115行目）。キャッシュ同期待機後、gcCheckPeriod間隔でgc()を実行（単一ワーカー） |

**主要処理フロー**:
1. **107-109行目**: キャッシュ同期待機
2. **111-113行目**: gc()をgcCheckPeriod間隔で実行するgoroutine起動

#### Step 3: GCメインループを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | gc（117-134行目）。全Pod/Nodeリスト取得後、4つのサブ関数を順次呼び出し |

**主要処理フロー**:
- **118-127行目**: podLister/nodeListerで全Pod/Nodeを取得
- **128-130行目**: terminatedPodThreshold > 0ならgcTerminated呼び出し
- **131行目**: gcTerminating呼び出し
- **132行目**: gcOrphaned呼び出し
- **133行目**: gcUnscheduledTerminating呼び出し

#### Step 4: 各GCサブ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | gcTerminated（193-226行目）。完了Pod数 - threshold = deleteCount、eviction優先ソート、並行削除 |
| 4-2 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | gcTerminating（148-191行目）。OOSノードのTerminating Pod、IsNodeReady() && TaintKeyExists(out-of-service) |
| 4-3 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | gcOrphaned（229-268行目）。existingNodeNames集合、nodeQueue.AddAfter(quarantineTime)、discoverDeletedNodes |
| 4-4 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | gcUnscheduledTerminating（302-320行目）。DeletionTimestamp != nil && NodeName == "" |

#### Step 5: 共通削除処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | gc_controller.go | `pkg/controller/podgc/gc_controller.go` | markFailedAndDeletePodWithCondition（346-366行目）。Phase=Failed パッチ → GracePeriod=0 で強制削除 |

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

```
PodGCController.Run()
    │
    └─ gc() [20秒間隔]
           │
           ├─ gcTerminated()
           │      ├─ sort.Sort(byEvictionAndCreationTimestamp)
           │      └─ markFailedAndDeletePod() [並行goroutine]
           │
           ├─ gcTerminating()
           │      ├─ nodeutil.IsNodeReady() + taints.TaintKeyExists()
           │      └─ markFailedAndDeletePod() [並行goroutine]
           │
           ├─ gcOrphaned()
           │      ├─ nodeQueue.AddAfter(quarantineTime)
           │      ├─ discoverDeletedNodes()
           │      │      └─ checkIfNodeExists() ── API Server GET
           │      └─ markFailedAndDeletePodWithCondition() ── DisruptionTarget条件付き
           │
           └─ gcUnscheduledTerminating()
                  └─ markFailedAndDeletePod()
```

### データフロー図

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

Pod Lister ─────────▶ gc()
(全Podリスト)              │
                          ├─ gcTerminated ─▶ API Server PATCH+DELETE
Node Lister ────────▶     │                    (Phase=Failed → 強制削除)
(全Nodeリスト)             │
                          ├─ gcTerminating ─▶ API Server PATCH+DELETE
                          │
                          ├─ gcOrphaned ─▶ nodeQueue (quarantine)
                          │       │            ▼
                          │       └─ API Server GET (Node確認)
                          │                    ▼
                          │            API Server PATCH+DELETE
                          │            (DisruptionTarget条件付き)
                          │
                          └─ gcUnscheduledTerminating ─▶ API Server PATCH+DELETE
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| gc_controller.go | `pkg/controller/podgc/gc_controller.go` | ソース | PodGCコントローラーのメインロジック |
| metrics/metrics.go | `pkg/controller/podgc/metrics/metrics.go` | ソース | Prometheusメトリクスの定義 |
| gc_controller_test.go | `pkg/controller/podgc/gc_controller_test.go` | テスト | PodGCの単体テスト |
