# 機能設計書 124-EmptyDirボリューム

## 概要

本ドキュメントは、EmptyDirボリュームプラグインの設計を記述する。EmptyDirはPodの起動時に作成され、Pod終了時に削除される一時的なボリュームであり、Pod内の全コンテナから共有可能なストレージを提供する。

### 本機能の処理概要

**業務上の目的・背景**：Pod内のコンテナ間でファイルを共有する一時的なストレージを提供する。キャッシュ、一時ファイル、ソートのワークエリア等に使用される。Podのライフサイクルと連動し、データの永続化は行わない。

**機能の利用シーン**：Pod Specでvolumes.emptyDirを指定した際にkubeletが自動的にマウントする。Default（ディスク）、Memory（tmpfs）、HugePages（hugetlbfs）の3種類のmediumをサポートする。

**主要な処理内容**：
1. SetUp/SetUpAt: ボリュームディレクトリの作成とマウント
2. TearDown/TearDownAt: ボリュームディレクトリの削除とアンマウント
3. Medium判定（Default/Memory/HugePages）に基づくセットアップ分岐
4. サイズ制限の計算と適用（tmpfs/quota）
5. FSGroup権限の適用

**関連システム・外部連携**：kubeletのvolume manager、cgroup（Pod メモリ制限）、tmpfs/hugetlbfsファイルシステム、fsquota（ディスククォータ）。

**権限による制御**：ボリュームディレクトリのパーミッションは0777で作成される。FSGroupが指定されている場合は権限変更が適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | EmptyDirはCLI画面を持たないkubelet内部処理 |

## 機能種別

ボリュームプラグイン / 一時ストレージ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| medium | StorageMedium | No | ストレージメディア（Default/Memory/HugePages/HugePages-\<size\>） | 列挙値チェック |
| sizeLimit | *resource.Quantity | No | サイズ制限 | 正の値チェック |

### 入力データソース

- Pod Spec（Volume定義、containers[].resources）
- ノードアロケータブルリソース（Memory/HugePagesの上限算出用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| マウントポイント | ディレクトリ | Pod内のボリュームディレクトリ |
| readyファイル | メタデータ | セットアップ完了を示すマーカーファイル |

### 出力先

- `<kubeletDir>/pods/<podUID>/volumes/kubernetes.io~empty-dir/<volumeName>/`
- `<kubeletDir>/pods/<podUID>/plugins/kubernetes.io~empty-dir/<volumeName>/` (readyマーカー)

## 処理フロー

### 処理シーケンス（SetUpAt）

```
1. マウントポイント状態確認（IsLikelyNotMountPoint）
2. readyマーカー確認
   ├─ [Memory + マウント済み] → 完了（冪等性）
   └─ [Default + ディレクトリ存在] → quota割り当てして完了
3. medium別セットアップ分岐
   ├─ Default: setupDir（ディレクトリ作成）
   ├─ Memory: setupTmpfs（tmpfsマウント）
   └─ HugePages: setupHugepages（hugetlbfsマウント）
4. FSGroup権限適用
5. readyマーカー設定
6. quota割り当て（Default時）
```

### フローチャート

```mermaid
flowchart TD
    A[SetUpAt開始] --> B{マウントポイント?}
    B -->|エラー & 非NotExist| C[エラー返却]
    B -->|OK| D{readyマーカー存在?}
    D -->|Yes & Memory & マウント済み| E[完了]
    D -->|Yes & Default & ディレクトリ存在| F[quota割り当て→完了]
    D -->|No| G{medium?}
    G -->|Default| H[setupDir]
    G -->|Memory| I[setupTmpfs]
    G -->|HugePages| J[setupHugepages]
    H --> K[FSGroup権限適用]
    I --> K
    J --> K
    K --> L[readyマーカー設定]
    L --> M[quota割り当て]
    M --> N[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-124-1 | デフォルトパーミッション | ディレクトリは0777パーミッションで作成される | 全medium |
| BR-124-2 | tmpfsサイズ制限 | min(sizeLimit, podMemoryLimit, nodeAllocatableMemory) | Memory medium |
| BR-124-3 | tmpfs noswap | 対応環境ではnoswapオプションを追加 | Memory medium |
| BR-124-4 | HugePages ページサイズ | Pod内のコンテナリソース要求からページサイズを推定 | HugePages medium |
| BR-124-5 | 冪等性 | readyマーカーが存在する場合は再セットアップをスキップ | 全medium |
| BR-124-6 | quota割り当て | fsquota対応の場合はディスククォータを設定 | Default medium |
| BR-124-7 | HugePages単一ページサイズ | Hugepagesメディア（サイズ未指定）では全コンテナで同一ページサイズが必須 | HugePages medium |

### 計算ロジック

**Memoryサイズ制限の計算**（`calculateEmptyDirMemorySize`、114-140行目）:
1. `sizeLimit = nodeAllocatableMemory`（初期値）
2. `podResourceConfig = cm.ResourceConfigForPod(pod)` でPodメモリ制限を取得
3. `podMemoryLimit < sizeLimit` の場合、`sizeLimit = podMemoryLimit`
4. `spec.Volume.EmptyDir.SizeLimit != nil` かつ `volumeSizeLimit < sizeLimit` の場合、`sizeLimit = volumeSizeLimit`

**HugePagesページサイズ取得**（`getPageSizeMountOption`、389-445行目）:
- `Hugepages` medium: 全コンテナのHugePagesリソース要求から単一ページサイズを検出（複数の場合はエラー）
- `Hugepages-<size>` medium: 指定サイズと一致するリソース要求を検出

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

本機能はデータベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | マウントエラー | tmpfs/hugetlbfsマウント失敗 | エラー返却（kubeletがリトライ） |
| - | パーミッションエラー | ディレクトリ作成/chmod失敗 | エラー返却 |
| - | サイズ不一致 | HugePagesページサイズの不一致 | エラーメッセージで不一致詳細を提示 |
| - | 複数ページサイズ | Hugepagesで複数のページサイズが要求された | エラーメッセージで制約を提示 |
| - | quotaエラー | fsquota設定失敗 | ログ出力のみ（非致命的） |

### リトライ仕様

本プラグイン自体にリトライ機構はない。kubeletのvolume managerが再試行を管理する。

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

ファイルシステム操作であり、アトミックなトランザクションは存在しない。readyマーカーが最後に書き込まれることで、不完全なセットアップを検出可能。

## パフォーマンス要件

Default mediumはディスクI/O性能に依存。Memory mediumはメモリ帯域に依存。HugePagesはTLBミス削減により高性能メモリアクセスを提供。

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

- EmptyDirはPod内の全コンテナから読み書き可能
- Memory mediumは物理メモリを使用するため、サイズ制限がないとノードのメモリを枯渇させるリスクがある
- ユーザーネームスペース対応でfsquotaの動作が変わる

## 備考

- EmptyDirはConfigMap、Secret、DownwardAPI、Projectedボリュームの内部実装として使用される（wrappedVolumeSpec）
- LocalStorageCapacityIsolationFSQuotaMonitoringフィーチャーゲートでquota機能が制御される

---

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | emptyDirPlugin構造体（59-61行目）: プラグインのホスト参照保持 |
| 1-2 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | emptyDir構造体（209-218行目）: medium、sizeLimit、mountDetectorを含む |
| 1-3 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | mountDetectorインターフェース（198-205行目）: マウントメディア判定の抽象 |

#### Step 2: セットアップ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | SetUpAt()（234-288行目）: メインのセットアップ処理 |
| 2-2 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | **234-264行目**: マウントポイント確認とreadyマーカーによる冪等性チェック |
| 2-3 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | **266-275行目**: medium別のセットアップ分岐（Default/Memory/HugePages） |
| 2-4 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | setupTmpfs()（318-340行目）: tmpfsマウント処理 |
| 2-5 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | setupHugepages()（343-385行目）: hugetlbfsマウント処理 |
| 2-6 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | setupDir()（448-483行目）: ディレクトリ作成とパーミッション設定 |

#### Step 3: サイズ計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | calculateEmptyDirMemorySize()（114-140行目）: tmpfsサイズ制限の算出 |
| 3-2 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | getPageSizeMountOption()（389-445行目）: HugePagesページサイズの特定 |
| 3-3 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | generateTmpfsMountOptions()（573-584行目）: tmpfsマウントオプション生成 |

#### Step 4: ティアダウン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | TearDownAt()（495-525行目）: medium判定とティアダウン分岐 |
| 4-2 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | teardownDefault()（527-542行目）: quota削除とディレクトリ削除 |
| 4-3 | empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | teardownTmpfsOrHugetlbfs()（544-555行目）: アンマウントとディレクトリ削除 |

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

```
kubelet volume manager
    |
    ├─ emptyDirPlugin.NewMounter(spec, pod)
    |      └─ newMounterInternal(spec, pod, mounter, mountDetector)
    |             └─ calculateEmptyDirMemorySize()  [Memory medium時]
    |
    ├─ emptyDir.SetUp()
    |      └─ SetUpAt(dir)
    |             ├─ IsLikelyNotMountPoint()
    |             ├─ IsReady() / SetReady()
    |             ├─ setupDir() / setupTmpfs() / setupHugepages()
    |             |      └─ [tmpfs] generateTmpfsMountOptions()
    |             |      └─ [tmpfs] mounter.MountSensitiveWithoutSystemd()
    |             |      └─ [hugepages] getPageSizeMountOption()
    |             |      └─ [hugepages] mounter.MountSensitiveWithoutSystemd()
    |             ├─ volume.NewVolumeOwnership().ChangePermissions()
    |             └─ assignQuota()
    |                    └─ fsquota.SupportsQuotas() / fsquota.AssignQuota()
    |
    └─ emptyDir.TearDown()
           └─ TearDownAt(dir)
                  ├─ mountDetector.GetMountMedium()
                  ├─ [Default] teardownDefault()
                  |      └─ fsquota.ClearQuota()
                  |      └─ os.RemoveAll()
                  └─ [Memory/HugePages] teardownTmpfsOrHugetlbfs()
                         └─ mounter.Unmount()
                         └─ os.RemoveAll()
```

### データフロー図

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

Pod Spec ──────────────▶ emptyDirPlugin.NewMounter()
  medium ──────────────▶     │
  sizeLimit ───────────▶     │
                              │
Node Allocatable ──────▶ calculateEmptyDirMemorySize() ──▶ sizeLimit
                              │
emptyDir ──────────────▶ SetUpAt()
                              ├──▶ ディレクトリ (/var/lib/kubelet/pods/.../volumes/)
                              ├──▶ [Memory] tmpfsマウント
                              ├──▶ [HugePages] hugetlbfsマウント
                              └──▶ readyマーカー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | ソース | メイン実装（プラグイン、マウンター、アンマウンター） |
| empty_dir_linux.go | `pkg/volume/emptydir/empty_dir_linux.go` | ソース | Linux固有のマウント検出実装 |
| fsquota/ | `pkg/volume/util/fsquota/` | ソース | ファイルシステムクォータ管理 |
| swap/ | `pkg/kubelet/util/swap/` | ソース | tmpfs noswapオプション判定 |
