# 通知設計書 39-FailedAttachVolume

## 概要

本ドキュメントは、Kubernetesクラスタにおいてボリュームのアタッチに失敗した際に発行されるFailedAttachVolumeイベント通知の設計を記述する。kubeletのボリュームオペレーション生成器、またはattach-detachコントローラがボリュームのアタッチ操作に失敗した場合にこの通知が発行される。

### 本通知の処理概要

FailedAttachVolume通知は、Podに必要なボリュームをノードにアタッチする処理が失敗した際に発行されるWarningイベントである。この通知は2つの異なるコンポーネントから発行される可能性がある：kubeletのvolume operation generatorと、kube-controller-managerのattach-detachコントローラ（Multi-Attachエラー時）。

**業務上の目的・背景**：ボリュームのアタッチ失敗はPodが正常に起動できないことを意味する。ストレージバックエンドの問題、ノードの問題、またはMulti-Attach制限の違反など、様々な原因が考えられるため、運用者への迅速な通知が必要である。特にステートフルワークロードではデータアクセスに直結する重要な問題である。

**通知の送信タイミング**：ボリュームアタッチ操作が実行され、エラーが返された時点で発行される。kubelet側ではattach操作のeventRecorderFunc内で、コントローラ側ではMulti-Attach競合の検出時に発行される。

**通知の受信者**：イベントはPodオブジェクトに紐づけられる。Podの所有者、クラスタ管理者、モニタリングシステムが受信者となる。

**通知内容の概要**：アタッチ失敗のエラーメッセージ、またはMulti-Attachエラーの詳細（他ノードでの使用状況、使用中のPod名等）が含まれる。

**期待されるアクション**：クラスタ管理者はストレージバックエンドの状態確認、ボリュームの使用状況確認、Multi-Attach制限の確認を行う。ReplicaSetで単一PVCを使用している場合はアクセスモードの見直しが必要。

## 通知種別

Kubernetes Event（Warning）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（ボリューム操作完了コールバック内で発行） |
| 優先度 | 高（Warning EventType） |
| リトライ | ボリューム操作自体のリトライに含まれる |

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

イベントはボリュームを必要としているPodオブジェクトに対して記録される。Multi-Attachエラーの場合は、アタッチをスケジュールしているすべてのPodに対してイベントが記録される。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（Kubernetes Event） |
| 送信元名称 | kubelet / attach-detach controller |
| 件名 | N/A |
| 形式 | Kubernetes Event Object |

### 本文テンプレート

```
# 一般的なアタッチ失敗
{err.Error() の内容}

# Multi-Attachエラー（他ノードで使用中、Pod情報なし）
Multi-Attach error for volume "{volumeName}" Volume is already exclusively attached to one node and can't be attached to another

# Multi-Attachエラー（同一名前空間のPodが使用中）
Multi-Attach error for volume "{volumeName}" Volume is already used by pod(s) {pod1, pod2, ...}

# Multi-Attachエラー（異なる名前空間のPodが使用中）
Multi-Attach error for volume "{volumeName}" Volume is already used by {n} pod(s) in different namespaces
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | 添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| err | アタッチ操作のエラー | AttachVolume操作の戻り値 | Yes（一般エラー時） |
| volumeName | ボリューム名 | volumeToAttach.VolumeName | Yes（Multi-Attach時） |
| podNames | 使用中のPod名リスト | desiredStateOfWorld.GetVolumePodsOnNodes | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ボリューム操作 | AttachVolume操作失敗 | err != nil | kubelet volume operation generatorから発行 |
| 状態調整 | Multi-Attach競合検出 | 他ノードで排他的にアタッチ済み | attach-detach controllerから発行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| アタッチ成功 | 正常にアタッチが完了した場合はWarningイベントは発行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ボリュームアタッチ要求] --> B{アタッチ操作実行}
    B -->|成功| C[SuccessfulAttachVolume Event]
    B -->|失敗| D[eventRecorderFunc実行]
    D --> E[Warning Event: FailedAttachVolume]
    E --> F[対象Podすべてにイベント記録]

    G[attach-detach controller reconcile] --> H{他ノードでアタッチ済み?}
    H -->|Yes| I{使用中のPodを検索}
    I -->|Pod見つからない| J[Multi-Attach: exclusively attached]
    I -->|Pod見つかる| K[Multi-Attach: used by pods]
    J --> L[Warning Event: FailedAttachVolume]
    K --> L
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| VolumeSpec | ボリューム仕様の取得 | Podの volume定義 |
| desiredStateOfWorld | アタッチ対象ボリュームの状態 | kubelet / controller内部状態 |
| actualStateOfWorld | 実際のアタッチ状態 | kubelet / controller内部状態 |

### テーブル別参照項目詳細

#### desiredStateOfWorld

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| VolumePodsOnNodes | Multi-Attach時の使用中Pod一覧取得 | GetVolumePodsOnNodes(otherNodes, volumeName) |
| ScheduledPods | イベント送信先Podリスト | ボリュームにスケジュールされたPod |

### 更新テーブル一覧

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

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event.Reason | "FailedAttachVolume" | Kubernetes Eventオブジェクト |
| INSERT | Event.Type | "Warning" | EventTypeWarning |
| INSERT | Event.Message | エラーメッセージ / Multi-Attach詳細 | 送信元により異なる |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| アタッチ操作失敗 | ストレージバックエンドエラー | ボリューム操作のリトライ機構で再試行 |
| Multi-Attach排他制限 | ReadWriteOnceのボリュームが他ノードで使用中 | ユーザアクション必要（Podの移動 or アクセスモード変更） |
| Multi-AttachPod競合 | 同一ボリュームを複数Podが要求 | ReplicaSet設定の見直しが必要 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | ボリューム操作のリトライ機構に依存 |
| リトライ間隔 | Exponential Backoff |
| リトライ対象エラー | 一般的なアタッチエラー（Multi-Attachは除く） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | ボリューム操作のリトライ頻度に依存 |
| 1日あたり上限 | 制限なし |

### 配信時間帯

時間帯の制限なし。

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

- エラーメッセージにボリューム名やノード名が含まれる
- Multi-Attachエラーで他のPod名が公開される可能性がある
- イベントはKubernetes RBACにより閲覧権限が制御される

## 備考

- 2つの異なるコンポーネント（kubelet volume operation generator、attach-detach controller）から発行される
- Multi-Attachエラーの場合、使用中のPodが異なるNamespaceにある場合はPod名は公開されずPod数のみが表示される（プライバシー保護）
- CSI（Container Storage Interface）ドライバのアタッチ操作にも適用される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/kubelet/events/event.go` | 63行目: `FailedAttachVolume = "FailedAttachVolume"` 定数定義 |

#### Step 2: kubelet側のアタッチ失敗を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | operation_generator.go | `pkg/volume/util/operationexecutor/operation_generator.go` | 312-318行目: eventRecorderFunc（アタッチ失敗時のイベント記録） |

**主要処理フロー**:
1. **312行目**: `eventRecorderFunc := func(err *error)` - エラー時イベント記録関数定義
2. **314-316行目**: 失敗時に全ScheduledPodsに対してWarningイベントを記録

#### Step 3: コントローラ側のMulti-Attachエラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | reconciler.go | `pkg/controller/volume/attachdetach/reconciler/reconciler.go` | 388-437行目: Multi-Attach処理とイベント記録 |

**主要処理フロー**:
- **393行目**: 他ノードでの使用状況をチェック
- **396-399行目**: Pod情報なしの場合のメッセージ生成とイベント記録
- **408-433行目**: Pod情報ありの場合のメッセージ生成（同一NS/異なるNS）

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

```
[kubelet側]
operationGenerator.GenerateAttachVolumeFunc()
    |
    +-- attachableVolumePlugin.NewAttacher()
    +-- volumeAttacher.Attach()
    |       |
    |       +-- [失敗時] eventRecorderFunc
    |                  |
    |                  +-- recorder.Eventf(pod, Warning, "FailedAttachVolume", err)
    |
    +-- [成功時] recorder.Eventf(pod, Normal, "SuccessfulAttachVolume", ...)

[controller側]
reconciler.attachDesiredVolumesToNodes()
    |
    +-- isMultiAttachAllowed() チェック
    |
    +-- [Multi-Attach禁止]
           |
           +-- desiredStateOfWorld.GetVolumePodsOnNodes()
           +-- recorder.Eventf(pod, Warning, "FailedAttachVolume", multiAttachMsg)
```

### データフロー図

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

Pod.Spec.Volumes             operationGenerator                   Kubernetes Event
(ボリューム定義)     ----->   GenerateAttachVolumeFunc        ----> (Reason: "FailedAttachVolume",
                              (operation_generator.go)              Type: Warning,
VolumeAttacher               Attach操作実行                        InvolvedObject: Pod)
(CSI/in-tree)       ----->

desiredStateOfWorld          reconciler                           Kubernetes Event
actualStateOfWorld  ----->   attachDesiredVolumesToNodes      ----> (Multi-Attach error,
                              (reconciler.go)                      Type: Warning)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/kubelet/events/event.go` | ソース | イベント定数定義（FailedAttachVolume） |
| operation_generator.go | `pkg/volume/util/operationexecutor/operation_generator.go` | ソース | kubelet側ボリュームアタッチ操作とイベント記録 |
| reconciler.go | `pkg/controller/volume/attachdetach/reconciler/reconciler.go` | ソース | attach-detachコントローラのMulti-Attach処理 |
| reconciler_test.go | `pkg/controller/volume/attachdetach/reconciler/reconciler_test.go` | テスト | Multi-Attachのテストケース |
