# 機能設計書 133-Localボリューム

## 概要

本ドキュメントは、Kubernetes における Local ボリュームプラグインの機能設計を記述する。ノードのローカルストレージ（ディレクトリまたはブロックデバイス）を PersistentVolume として使用する機能について定義する。

### 本機能の処理概要

**業務上の目的・背景**：分散ストレージシステムやデータベースなど、ローカルディスクの高いI/Oパフォーマンスを必要とするワークロードに対して、ノードのローカルストレージを PersistentVolume として公開する。リモートストレージと比較して低レイテンシ・高スループットを実現する。

**機能の利用シーン**：分散データベース（Cassandra, MongoDB等）、メッセージキュー（Kafka等）、高性能キャッシュなど、データの局所性が重要なアプリケーションに使用される。ノードアフィニティと組み合わせて、特定ノードのストレージに Pod をバインドする。

**主要な処理内容**：
1. ローカルパスの種別判定（ディレクトリ or ブロックデバイス）
2. ディレクトリの場合: バインドマウントによる Pod へのマウント
3. ブロックデバイスの場合: デバイスのフォーマットとマウント、またはRAWブロックアクセス
4. ファイルシステムリサイズのサポート（ブロックデバイス時）
5. fsGroup によるボリューム所有権管理
6. 複数 Pod からの共有マウント時の fsGroup 競合検出

**関連システム・外部連携**：Linux ファイルシステム、mount/umount システムコール、mkfs ユーティリティ

**権限による制御**：PV/PVC の RBAC によるアクセス制御。fsGroup による Pod 単位の所有権制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は PV/PVC 定義を通じて Kubelet が自動的に実行する |

## 機能種別

ボリュームライフサイクル管理（Mount/Unmount/Expand）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | string | Yes | ノード上のローカルパス | 空でないこと。バックステップ（..）を含まないこと |
| fsType | *string | No | ファイルシステムタイプ（ブロックデバイス時） | デフォルト: ext4 |

### 入力データソース

- PersistentVolume の `.spec.local` フィールド

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| mountPath | string | Pod 内のマウントポイントパス |
| globalPath | string | ローカルパスまたはグローバルマウントパス |

### 出力先

- Pod のボリュームマウントパス（バインドマウント）
- Pod のブロックデバイスパス（ブロックモード時）

## 処理フロー

### 処理シーケンス

```
1. パス種別判定
   └─ GetFileType() でディレクトリかブロックデバイスかを判定
2. MountDevice（ブロックデバイスの場合のみ）
   └─ FormatAndMount() でファイルシステムをフォーマット・マウント
3. SetUpAt（バインドマウント）
   └─ volumeLocks でグローバルパスをロック
   └─ パスのバリデーション（バックステップチェック）
   └─ マウント済み判定
   └─ fsGroup 競合検出
   └─ バインドマウント実行
   └─ ボリューム所有権変更（初回マウント時のみ）
4. TearDownAt（アンマウント）
   └─ CleanupMountPoint() で拡張マウントポイントチェック付きアンマウント
5. UnmountDevice（ブロックデバイスの場合のみ）
   └─ baseMountPath 内のパスの場合のみアンマウント
```

### フローチャート

```mermaid
flowchart TD
    A[MountDevice開始] --> B{パス種別}
    B -->|ディレクトリ| C[何もしない - 直接バインドマウント可能]
    B -->|ブロックデバイス| D[FormatAndMount実行]
    D --> E[グローバルマウントパスに作成]
    C --> F[SetUpAt開始]
    E --> F
    F --> G[パスバリデーション]
    G --> H{既にマウント済み?}
    H -->|Yes| I[何もしない]
    H -->|No| J{fsGroup指定あり?}
    J -->|Yes| K{他のPodがマウント中?}
    J -->|No| L[バインドマウント実行]
    K -->|Yes| M{fsGroupが一致?}
    K -->|No| L
    M -->|No| N[警告イベント発行]
    M -->|Yes| L
    N --> L
    L --> O{初回マウント?}
    O -->|Yes| P[所有権変更]
    O -->|No| Q[完了]
    P --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-133-01 | PVのみサポート | Local ボリュームは PersistentVolume としてのみ使用可能（インラインボリューム不可） | 常時 |
| BR-133-02 | パス種別制限 | ディレクトリとブロックデバイスのみサポート | 常時 |
| BR-133-03 | デフォルトファイルシステム | fsType 未指定時は ext4 をデフォルトとする | ブロックデバイス時 |
| BR-133-04 | fsGroup競合警告 | 異なる fsGroup で複数 Pod がマウントする場合に警告イベントを発行 | fsGroup 指定時 |
| BR-133-05 | 初回所有権変更 | ボリューム所有権は最初のマウント時にのみ変更 | 初回マウント + !readOnly |
| BR-133-06 | バックステップ禁止 | パスに ".." を含むバックステップは許可しない | パスバリデーション時 |

### 計算ロジック

- globalLocalPath: ディレクトリの場合はそのパスをそのまま使用。ブロックデバイスの場合は `{pluginDir}/mounts/{pvName}` を使用

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

本機能にはデータベース操作はない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | パス未設定 | local.path が空 | エラー返却 |
| - | サポート外ファイルタイプ | ディレクトリでもブロックデバイスでもない | エラー返却 |
| - | バックステップ検出 | パスに ".." が含まれる | エラー返却 |
| - | マウント失敗 | バインドマウントの実行失敗 | アンマウント試行後にディレクトリ削除してエラー返却 |
| - | fsGroup競合 | 異なる fsGroup での多重マウント | 警告イベント発行（エラーにはしない） |

### リトライ仕様

マウント失敗時のリトライは kubelet の sync loop に委ねる。プラグイン内部のリトライはない。

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

keymutex によるグローバルパス単位のロックで排他制御。マウント操作が失敗した場合、可能な限りクリーンアップ（アンマウント、ディレクトリ削除）を試行する。

## パフォーマンス要件

- ローカルストレージのため、ネットワーク遅延は発生しない
- ブロックデバイスの初回フォーマットはデバイスサイズに依存
- ファイルシステムリサイズは GenericResizeFS を使用

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

- パスのバックステップチェックによるディレクトリトラバーサル防止
- fsGroup によるボリューム所有権の制御
- FSGroupChangePolicy によるパーミッション変更方針の制御
- ReadOnly マウントオプションのサポート
- SELinux コンテキストマウントは非サポート（SupportsSELinuxContextMount = false）

## 備考

- プラグイン名: `kubernetes.io/local-volume`
- サポートするアクセスモード: ReadWriteOnce のみ
- NodeExpandableVolumePlugin インターフェースを実装（ブロックデバイスのリサイズ対応）
- Windows プラットフォームでの動作差異あり（MkdirAll のスキップ）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | local.go | `pkg/volume/local/local.go` | localVolume 構造体（490-501行目）: globalPath がローカルストレージパスを保持 |
| 1-2 | local.go | `pkg/volume/local/local.go` | localVolumeMounter（507-511行目）: readOnly, mountOptions を保持 |

#### Step 2: エントリーポイントとパス種別判定

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | local.go | `pkg/volume/local/local.go` | ProbeVolumePlugins()（48-50行目）でプラグイン登録 |
| 2-2 | local.go | `pkg/volume/local/local.go` | getGlobalLocalPath()（270-292行目）: パス種別に応じたグローバルパス生成 |
| 2-3 | local.go | `pkg/volume/local/local.go` | MountDevice()（365-384行目）: ディレクトリなら即返却、ブロックデバイスならフォーマット＆マウント |

#### Step 3: バインドマウントと所有権管理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | local.go | `pkg/volume/local/local.go` | SetUpAt()（529-628行目）: バインドマウントの主要ロジック。fsGroup 競合検出含む |
| 3-2 | local.go | `pkg/volume/local/local.go` | filterPodMounts()（631-639行目）: Pod ディレクトリ内のマウントのみをフィルタ |

**主要処理フロー**:
- **530-531行目**: volumeLocks でグローバルパスをロック
- **537-540行目**: パスのバリデーション（バックステップチェック）
- **552-572行目**: fsGroup 競合検出と警告イベント発行
- **582-590行目**: バインドマウント実行（MountSensitiveWithoutSystemd）
- **619-625行目**: 初回マウント時の所有権変更

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

```
ProbeVolumePlugins() → localVolumePlugin
    │
    ├─ NewDeviceMounter() → deviceMounter
    │      └─ MountDevice()
    │             ├─ GetFileType() [パス種別判定]
    │             └─ mountLocalBlockDevice() [ブロックデバイス時]
    │                    └─ FormatAndMount()
    │
    ├─ NewMounter() → localVolumeMounter
    │      └─ SetUpAt()
    │             ├─ ValidatePathNoBacksteps()
    │             ├─ filterPodMounts()
    │             ├─ MountSensitiveWithoutSystemd()
    │             └─ VolumeOwnership.ChangePermissions()
    │
    ├─ NodeExpand()
    │      ├─ GetFileType()
    │      └─ GenericResizeFS() [ブロックデバイス時]
    │
    └─ NewUnmounter() → localVolumeUnmounter
           └─ TearDownAt() → CleanupMountPoint()
```

### データフロー図

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

PV Spec             ──▶ getGlobalLocalPath()     ──▶ globalPath
  (local.path)           GetFileType()

globalPath          ──▶ MountDevice()            ──▶ mountedGlobalPath
                         (ブロックデバイスのみ)

mountedGlobalPath   ──▶ SetUpAt()                ──▶ podVolumePath
                         bind mount
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| local.go | `pkg/volume/local/local.go` | ソース | プラグイン全体の実装 |
| local_test.go | `pkg/volume/local/local_test.go` | テスト | ユニットテスト |
| local_linux_test.go | `pkg/volume/local/local_linux_test.go` | テスト | Linux固有テスト |
| doc.go | `pkg/volume/local/doc.go` | ソース | パッケージドキュメント |
