# 画面設計書 17-kubectl drain

## 概要

本ドキュメントは、Kubernetes CLIツール `kubectl drain` コマンドの画面設計書である。メンテナンスのためにノード上のPodを安全に退避（evict/delete）し、ノードをワークロード不在の状態にするコマンドの仕様を記載する。

### 本画面の処理概要

`kubectl drain` コマンドは、指定ノードをまずスケジュール不可（cordon）に設定した後、そのノード上で稼働している全Podを退避させる。Eviction APIが利用可能な場合はeviction（退去）を使用し、PodDisruptionBudget（PDB）の制約を尊重する。Eviction APIが利用不可の場合はDELETE操作でPodを削除する。

**業務上の目的・背景**：ノードのOSアップデート、カーネルパッチ、ハードウェア交換などのメンテナンスを安全に実施するため、稼働中のPodを他のノードに移動させる必要がある。drainコマンドはPDBを尊重しながらPodを退避させ、グレースフルなメンテナンスを実現する。

**画面へのアクセス方法**：ターミナル上で `kubectl drain <ノード名>` を実行する。

**主要な操作・処理内容**：
1. 対象ノードをcordon（スケジュール不可）に設定する
2. ノード上のPod一覧を取得し、退避対象を選別する
3. 各Podに対してEviction/DELETEを実行する
4. 全Podの退避完了を待機する
5. 結果を標準出力に表示する

**画面遷移**：メンテナンスフローの中核ステップ。`kubectl cordon` -> `kubectl drain` -> メンテナンス -> `kubectl uncordon`。

**権限による表示制御**：nodeリソースのget/patch権限、podリソースのlist/get/delete権限、pods/evictionのcreate権限が必要。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 93 | ドレイン（drain） | 主機能 | メンテナンスのためノードからPodを退避する主処理 |
| 25 | Disruption Budgetコントローラー | API連携 | ドレイン時にPodDisruptionBudgetの制約に従って退避が行われる |
| 18 | Node Lifecycleコントローラー | API連携 | ノードの状態変更に伴いNode Lifecycleコントローラーが連動する |

## 画面種別

CLI コマンド（リソース更新・削除）

## URL/ルーティング

```
kubectl drain NODE
```

## 入出力項目

### 入力項目

| 項目名 | フラグ | 型 | 必須 | デフォルト | 説明 |
|--------|--------|------|------|-----------|------|
| ノード名 | 位置引数 | string | Yes(*) | - | drain対象のノード名 |
| ラベルセレクタ | -l, --selector | string | No | "" | ラベルによるノード選択 |
| 強制実行 | --force | bool | No | false | コントローラー管理外のPodも強制削除 |
| DaemonSet無視 | --ignore-daemonsets | bool | No | false | DaemonSet管理Podを無視 |
| emptyDir削除 | --delete-emptydir-data | bool | No | false | emptyDir使用Podも退避 |
| グレース期間 | --grace-period | int | No | -1 | Pod終了の猶予時間（秒）。負数はPodデフォルト使用 |
| タイムアウト | --timeout | duration | No | 0 | 待機タイムアウト。0は無制限 |
| Podセレクタ | --pod-selector | string | No | "" | 退避対象Podをラベルでフィルタ |
| Eviction無効化 | --disable-eviction | bool | No | false | Eviction APIを使用せずDELETEを使用（PDB無視） |
| 待機スキップ | --skip-wait-for-delete-timeout | int | No | 0 | 指定秒以上経過したPodの待機をスキップ |
| チャンクサイズ | --chunk-size | int | No | 500 | リスト取得時のチャンクサイズ |
| Dry Run | --dry-run | string | No | "none" | "none" / "client" / "server" |

(*) ノード名または--selectorのいずれかが必須

### 出力項目

| 項目名 | 説明 |
|--------|------|
| cordon結果 | `node/<name> cordoned` |
| Pod退避結果 | 各Podの `evicted` / `deleted` 状態 |
| drain完了 | `node/<name> drained` |

## 表示項目

| 項目 | 形式 | 説明 |
|------|------|------|
| cordon結果 | `node/<name> cordoned` | ノードのcordon完了 |
| Pod退避開始（V2） | `pod/<name> eviction started` / `deletion started` | klog V(2)有効時のみ |
| Pod退避結果 | `pod/<name> evicted` / `deleted` | 各Podの退避完了 |
| drain完了 | `node/<name> drained` | ノードのdrain完了 |

## イベント仕様

### 1-drain実行

1. コマンドライン引数を解析しDrainCmdOptionsを初期化する（`Complete`、L243-322）
2. 対象ノードをcordonに設定する（`RunDrain`、L325-328）
3. 各ノードについて退避対象Pod一覧を取得する（`deleteOrEvictPodsSimple`、L366-398）
4. 退避対象Podの警告がある場合は表示する（L371-373）
5. dry-run=clientの場合はPod名を表示して終了（L374-379）
6. Podのeviction/deleteを実行する（`drainer.DeleteOrEvictPods`、L381）
7. 退避中にエラーが発生した場合は残留Podを表示する（L382-394）
8. 全ノードの処理結果を集約して報告する（L330-363）

### 2-Pod退避の詳細フロー

1. EvictionAPI利用可能かつ--disable-evictionでない場合：Eviction APIを使用
2. それ以外：通常のDELETE操作を使用
3. 各Podの退避開始時・完了時にコールバック関数が呼ばれる（L167-209）
4. Eviction失敗時は5秒（EvictErrorRetryDelay）後にリトライ

## データベース更新仕様

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

| 操作（イベント） | 対象リソース | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| drain実行（cordon） | Node | PATCH | spec.unschedulableをtrueに設定 |
| drain実行（Pod退避） | Pod | DELETE/EVICTION | 対象PodをEviction/DELETEで退避 |

### テーブル別更新項目詳細

#### Node

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| PATCH | spec.unschedulable | true | drain開始時にcordon |

#### Pod

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| EVICTION/DELETE | - | 対象Podの退避・削除 | PDB制約を尊重（Eviction時） |

## メッセージ仕様

| 種別 | メッセージ | 条件 |
|------|-----------|------|
| 成功 | `node/<name> cordoned` | cordon成功 |
| 成功 | `node/<name> drained` | drain完了 |
| 成功 | `pod/<name> evicted` | Pod eviction成功 |
| 成功 | `pod/<name> deleted` | Pod delete成功 |
| 情報 | `evicting pod %s/%s (dry run)` | dry-run時 |
| 警告 | 退避対象Podの警告メッセージ | DaemonSet管理Pod等の警告 |
| エラー | `error: unable to drain node "%s" due to error: %s, continuing command...` | drain失敗時（続行） |
| エラー | `There are pending nodes to be drained:` + ノード名一覧 | 残留ノードあり |
| エラー | `There are pending pods in node "%s" when an error occurred: %v` | 退避未完了Pod残留 |
| エラー | `pod/<name> eviction failed` / `deletion failed` | Pod退避失敗 |

## 例外処理

| 例外条件 | 動作 |
|---------|------|
| DaemonSet管理Podが存在し--ignore-daemonsets未指定 | エラーで停止 |
| コントローラー管理外Podが存在し--force未指定 | エラーで停止 |
| emptyDir使用Podが存在し--delete-emptydir-data未指定 | エラーで停止 |
| Eviction失敗 | 5秒後にリトライ |
| タイムアウト超過 | タイムアウトエラーで停止 |
| PDB制約によりEviction拒否 | PDB制約エラーをレポート |
| 一部ノードのdrain失敗 | 失敗ノードを報告し、エラーを集約して返す |

## 備考

- drainは内部でまずRunCordonOrUncordon(true)を呼び出してcordonを実行する（L326）
- EvictErrorRetryDelayは5秒に設定されている（L155）
- onPodDeletionOrEvictionStarted/Finishedコールバックにより、Pod退避の開始・完了が逐次報告される（L161-209）
- `--disable-eviction`を使用するとPDBチェックがバイパスされるため注意が必要
- 複数ノードを指定した場合、1ノードの失敗は他のノードの処理に影響しない（L334-353）
- チャンクサイズ（`--chunk-size`）はデフォルト500で、大量Podのリスト取得を分割する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | drain.go (cmd) | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | DrainCmdOptions構造体（L44-55）：drainerフィールドがdrain.Helper |
| 1-2 | drain.go (pkg) | `staging/src/k8s.io/kubectl/pkg/drain/drain.go` | drain.Helper構造体：Pod退避処理の本体 |

**読解のコツ**: `DrainCmdOptions.drainer`（drain.Helper型）がPod退避のコアロジックを担当する。cmdパッケージはCobra統合とUI表示を、pkgパッケージは実際のPod退避ロジックを担当する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | drain.go | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | NewCmdDrain関数（L211-239）：drainコマンド定義とフラグ登録 |

**主要処理フロー**:
1. **L212**: NewDrainCmdOptionsでオプション初期化
2. **L221-224**: Complete -> RunDrainの呼び出しチェーン
3. **L226-238**: 多数のフラグ登録（force, ignore-daemonsets, grace-period等）

#### Step 3: drain処理全体を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | drain.go | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | RunDrain（L325-364）：cordon実行後にPod退避 |
| 3-2 | drain.go | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | deleteOrEvictPodsSimple（L366-398）：Pod退避の実行 |

**主要処理フロー**:
- **L326-328**: まずRunCordonOrUncordon(true)でcordon
- **L334-353**: 各ノードのPod退避を実行し、成功/失敗を記録
- **L367-370**: drainer.GetPodsForDeletion()で退避対象Podを取得
- **L381**: drainer.DeleteOrEvictPods()で実際にPodを退避

#### Step 4: コールバック処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | drain.go | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | onPodDeletionOrEvictionFinished（L167-189）：退避完了コールバック |
| 4-2 | drain.go | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | onPodDeletionOrEvictionStarted（L192-209）：退避開始コールバック |

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

```
NewCmdDrain (L211)
    |
    +-- NewDrainCmdOptions (L149)
    |       +-- drain.Helper初期化
    |       +-- コールバック設定 (L161-163)
    |
    +-- Complete (L243)
    |       +-- f.KubernetesClientSet()
    |       +-- f.NewBuilder()
    |       +-- nodeInfosに格納
    |
    +-- RunDrain (L325)
            +-- RunCordonOrUncordon(true) (L326)
            |       +-- CordonHelper.PatchOrReplace()
            |
            +-- [各ノード] deleteOrEvictPodsSimple (L366)
                    +-- drainer.GetPodsForDeletion()
                    +-- drainer.DeleteOrEvictPods()
                    |       +-- [各Pod] evictPod() / deletePod()
                    |       +-- onPodDeletionOrEvictionStarted()
                    |       +-- onPodDeletionOrEvictionFinished()
                    +-- printer.PrintObj() ["drained"]
```

### データフロー図

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

ノード名 / --selector   Complete()
  --force              --> ノードリソース取得 ------->+---> API Server
  --ignore-daemonsets                                 |
  --grace-period       RunDrain()                     |
                       --> RunCordonOrUncordon(true) >+---> "cordoned"
                       --> GetPodsForDeletion() ----->|
                       --> [警告表示] --------------->+---> stderr (warnings)
                       --> DeleteOrEvictPods() ------>+---> API Server
                       |   +-- evictPod()             |     (Pod Eviction/Delete)
                       |   +-- deletePod()            |
                       --> [各Pod結果] -------------->+---> stdout
                       --> "drained" ----------------->      "evicted/deleted"
                                                              "drained"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| drain.go (cmd) | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go` | ソース | drain/cordon/uncordonコマンドのCobra定義と入出力 |
| drain.go (pkg) | `staging/src/k8s.io/kubectl/pkg/drain/drain.go` | ソース | drain.Helper：Pod退避のコアロジック |
| filters.go | `staging/src/k8s.io/kubectl/pkg/drain/filters.go` | ソース | Pod退避対象のフィルタリングロジック |
| drain_test.go | `staging/src/k8s.io/kubectl/pkg/cmd/drain/drain_test.go` | テスト | drainコマンドのユニットテスト |
