# 通知設計書 52-FailedBinding

## 概要

本ドキュメントは、PersistentVolumeClaim（PVC）のバインドに失敗した際に発行されるイベント通知「FailedBinding」の設計について記述する。PV Controllerおよびエフェメラルボリュームコントローラから発行される。

### 本通知の処理概要

**業務上の目的・背景**：PVCはKubernetesにおけるストレージリクエストの中核メカニズムである。PVCが適切なPersistentVolume（PV）にバインドされない場合、Podはストレージを利用できず起動に失敗する。この通知は、バインド失敗の原因をクラスタ管理者に伝達し、ストレージリソースの不足や設定誤りを早期に検知するために存在する。また、エフェメラルボリューム（`ephemeral` タイプ）のPVC作成時にもエラーが発生した場合に同イベントが発行される。

**通知の送信タイミング**：PV ControllerがPVCの同期処理（`syncUnboundClaim`）を行う際に、利用可能なPVが存在せずStorageClassも設定されていない場合、または指定されたPVが既に別のPVCにバインドされている場合に発行される。また、エフェメラルボリュームコントローラがPodのエフェメラルボリュームのPVC処理に失敗した場合にも発行される。

**通知の受信者**：PVCオブジェクトまたはPodオブジェクトに対するKubernetes Eventとして発行される。`kubectl describe pvc` や `kubectl get events` でPVCの状態を監視しているクラスタ管理者、ストレージ管理者が受信者となる。

**通知内容の概要**：バインドに失敗した具体的な理由（「利用可能なPVがない」「PVが既に別のPVCにバインドされている」「エフェメラルボリュームのエラー」）が含まれる。

**期待されるアクション**：受信者は、PVの可用性確認、StorageClassの設定確認、PVの容量やアクセスモードの適合性確認、競合するPVCの確認、エフェメラルボリュームテンプレートの確認を行う。

## 通知種別

Kubernetes Event（EventType: Warning / Normal）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（PV Controller同期ループ内 / エフェメラルボリュームコントローラ同期ループ内） |
| 優先度 | 高（ストレージバインド失敗はPod起動を阻害） |
| リトライ | コントローラの同期ループにより自動再試行 |

### 送信先決定ロジック

PV Controllerの場合：PVCオブジェクトに対してEventを発行する。エフェメラルボリュームコントローラの場合：Podオブジェクトに対してEventを発行する。

## 通知テンプレート

### Kubernetes Event

| 項目 | 内容 |
|-----|------|
| 送信元コンポーネント | pv controller / ephemeral volume controller |
| EventType | Warning（PV既バインド時） / Normal（PV未存在時） |
| Reason | FailedBinding |
| 関連オブジェクト | PersistentVolumeClaim / Pod |

### 本文テンプレート

PV未存在・StorageClass未設定の場合：
```
no persistent volumes available for this claim and no storage class is set
```

PVが既に別PVCにバインド済みの場合：
```
volume "%s" already bound to a different claim.
```

エフェメラルボリュームの場合：
```
ephemeral volume %s: %v
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| volume.Name | バインド対象のPV名 | PV Spec | Yes（PV存在時） |
| vol.Name | エフェメラルボリューム名 | Pod.Spec.Volumes | Yes（エフェメラル時） |
| err | エフェメラルボリューム処理エラー | handleVolume()の戻り値 | Yes（エフェメラル時） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| PV Controller同期 | syncUnboundClaim | PVが存在せずStorageClass未設定 | pv_controller.go 381行目 |
| PV Controller同期 | syncUnboundClaim | 指定PVが別PVCにバインド済み | pv_controller.go 470行目、481行目 |
| エフェメラルボリューム同期 | syncHandler | エフェメラルボリュームのPVC処理失敗 | controller.go 245行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| StorageClass設定済み | StorageClassが設定されている場合は動的プロビジョニングが試行されるため、FailedBindingではなくプロビジョニング関連イベントが発行される |
| Pod削除中 | エフェメラルボリュームコントローラはDeletionTimestampが設定されたPodを無視する |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[PVC同期開始] --> B{PVCにVolumeName指定あり?}
    B -->|No| C{StorageClassあり?}
    C -->|No| D[Event: FailedBinding<br/>no persistent volumes available]
    C -->|Yes| E[動的プロビジョニングへ]
    B -->|Yes| F{指定PVは存在するか?}
    F -->|No| G[PVC Pending維持]
    F -->|Yes| H{PVはバインド済み?}
    H -->|No| I[バインド処理]
    H -->|Yes| J[Event: FailedBinding<br/>volume already bound]
    D --> K[PVC Pending維持]
    J --> K
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| PersistentVolumeClaims | PVCの状態確認 | コントローラキャッシュ経由 |
| PersistentVolumes | PVの可用性・バインド状態確認 | コントローラキャッシュ経由 |
| StorageClasses | 動的プロビジョニング判定 | Lister経由 |
| Pods | エフェメラルボリューム対象Podの確認 | コントローラキャッシュ経由 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Events | INSERT | FailedBindingイベントの記録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| PV未存在 | 要求に合致するPVが存在しない | PVの作成またはStorageClassの設定 |
| PV既バインド | 指定PVが別PVCにバインド済み | PVの解放待ちまたは別PVの指定 |
| エフェメラルPVC作成失敗 | PVC作成APIエラー | PVCテンプレートの確認 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | コントローラ同期ループにより無制限 |
| リトライ間隔 | PV Controllerの再同期間隔に依存 |
| リトライ対象エラー | 全バインドエラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Kubernetes Event APIのレート制限に準拠 |
| 1日あたり上限 | 同一理由のイベントは集約される |

### 配信時間帯

制限なし

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

- イベントメッセージにはPV名やPVC名が含まれるが、ストレージ接続情報などの機密情報は含まれない
- Kubernetes RBACによりEventの読み取り権限が制御される

## 備考

- FailedBindingはPV ControllerとEphemeral Volume Controllerの2箇所から発行される
- PV Controllerの「space shuttle style」コメントに記載の通り、全分岐が明示的に処理されている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/controller/volume/events/event.go` | 21行目: `FailedBinding` 定数定義。ボリューム関連イベントの全定数を把握 |

**読解のコツ**: PV Controller用のイベント定数はkubeletのイベント定数（`pkg/kubelet/events/event.go`）とは別パッケージに定義されている点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 376-482行目: syncUnboundClaim内でのFailedBinding発行ロジック |

**主要処理フロー**:
1. **381行目**: StorageClass未設定時のFailedBindingイベント発行
2. **437-441行目**: checkVolumeSatisfyClaim失敗時のVolumeMismatchイベント発行
3. **470行目**: ユーザ指定PVが別PVCにバインド済みの場合のFailedBinding発行
4. **481行目**: コントローラバインドPVが別PVCにバインド済みの場合のFailedBinding発行

#### Step 3: エフェメラルボリュームコントローラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | controller.go | `pkg/controller/volume/ephemeral/controller.go` | 243-246行目: handleVolumeエラー時のFailedBindingイベント発行 |

**主要処理フロー**:
- **243行目**: Pod.Spec.VolumesをイテレートしhandleVolumeを呼ぶ
- **245行目**: handleVolumeエラー時にWarningイベント発行

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

```
PersistentVolumeController.syncClaim()
    |
    +-- syncUnboundClaim(claim)
            |
            +-- (StorageClass未設定) eventRecorder.Event(claim, Normal, FailedBinding, "no persistent volumes...")
            |
            +-- (PV指定あり、別PVCにバインド済み) eventRecorder.Event(claim, Warning, FailedBinding, "volume already bound...")
            |
            +-- (PV指定あり、VolumeMismatch) eventRecorder.Event(claim, Warning, VolumeMismatch, ...)

ephemeralController.syncHandler()
    |
    +-- handleVolume(ctx, pod, vol)
            |
            +-- (error) recorder.Event(pod, Warning, FailedBinding, "ephemeral volume %s: %v")
```

### データフロー図

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

PVC Spec             ---->  PV Controller                ---->  Kubernetes Event
(VolumeName,                syncUnboundClaim()                  (FailedBinding)
 StorageClassName)          |
                            +-> findBestMatch()
                            +-> checkVolumeSatisfyClaim()

Pod Spec             ---->  Ephemeral Volume Controller  ---->  Kubernetes Event
(Volumes[].Ephemeral)       syncHandler()                       (FailedBinding)
                            +-> handleVolume()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/controller/volume/events/event.go` | ソース | FailedBinding定数定義（21行目） |
| pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | ソース | PV ControllerのFailedBinding発行ロジック（381, 470, 481行目） |
| controller.go | `pkg/controller/volume/ephemeral/controller.go` | ソース | エフェメラルボリュームコントローラのFailedBinding発行ロジック（245行目） |
| binder_test.go | `pkg/controller/volume/persistentvolume/binder_test.go` | テスト | バインドロジックのユニットテスト |
