# 通知設計書 21-Failed (PullImage)

## 概要

本ドキュメントは、Kubernetesクラスタにおいてコンテナイメージのプルに失敗した際に発行されるイベント通知「Failed (PullImage)」の設計を定義する。

### 本通知の処理概要

コンテナイメージのプル処理が失敗した場合に、kubeletのimageManagerがWarningイベントとしてKubernetes Event APIに記録する通知である。

**業務上の目的・背景**：コンテナイメージのプルはPod起動プロセスにおける重要なステップであり、この失敗はPodが正常に起動できない直接的な原因となる。運用者やクラスタ管理者がイメージプル失敗を迅速に検知し、レジストリの可用性、認証情報の正当性、イメージ名の正確性などを確認・修正するために本通知が必要である。

**通知の送信タイミング**：kubeletがコンテナイメージのプル処理を実行し、CRI（Container Runtime Interface）経由でランタイムからエラーレスポンスを受け取った時点で即座に発行される。具体的には`imageManager.pullImage()`メソッド内で`puller.pullImage()`の結果がエラーとなった場合に発行される。

**通知の受信者**：Kubernetes Event APIを通じて、対象PodのObjectReferenceに紐づくイベントとして記録される。kubectl describe pod等で確認可能であり、イベント監視ツール（Prometheus、Datadog等）や外部通知システムを通じてクラスタ管理者・アプリケーション運用者に伝達される。

**通知内容の概要**：失敗したイメージ名とエラー詳細を含むメッセージが通知される。メッセージ形式は「Failed to pull image "イメージ名": エラー詳細」となる。レジストリ不達やシグネチャ検証失敗などの特定エラーの場合はより詳細なメッセージが付与される。

**期待されるアクション**：受信者はエラーメッセージの内容に基づいて、(1)イメージ名・タグの正確性を確認、(2)コンテナレジストリの可用性を確認、(3)imagePullSecretの設定・認証情報の有効性を確認、(4)ネットワーク接続性を確認、(5)レジストリのシグネチャポリシーを確認、などの対応を行う。

## 通知種別

Kubernetes Event（APIリソース）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（イベント記録はイメージプル処理内で即時実行） |
| 優先度 | 高（EventType: Warning） |
| リトライ | イベント記録自体のリトライはEventRecorderの内部実装に依存。イメージプルのリトライはbackoffメカニズムにより自動的に再試行される |

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

対象PodのObjectReference（`*v1.ObjectReference`）に対してイベントが記録される。ObjectReferenceがnilの場合はklogへのログ出力のみとなる。

## 通知テンプレート

### メール通知の場合

本通知はKubernetes Eventとして記録されるため、メール通知テンプレートは直接存在しない。外部通知システムとの連携によりメール等に変換される。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（Kubernetes Event） |
| 送信元名称 | kubelet |
| 件名 | N/A |
| 形式 | テキスト |

### 本文テンプレート

```
Failed to pull image "{イメージ名}": {エラー詳細}
```

特定のCRIエラーの場合:
```
image pull failed for {イメージ名} because the registry is unavailable{詳細}
```
```
image pull failed for {イメージ名} because the signature validation failed{詳細}
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| image | プル対象のコンテナイメージ名 | EnsureImageExists引数のrequestedImage | Yes |
| err | プル失敗時のエラー詳細 | puller.pullImage()の返却エラー | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | CRIランタイムからのイメージプルエラーレスポンス | imagePullResult.errがnilでない場合 | pullImage()メソッド内でpuller.pullImage()の結果を受信後に判定 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ObjectReferenceがnil | ObjectReferenceがnilの場合はEvent発行ではなくklogへのWarningログ出力のみとなる |
| backoff期間中 | イメージプルがbackoff期間中の場合は、BackOffイベント(別通知No.24)が発行され、本通知は発行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Pod起動処理: EnsureImageExists呼び出し] --> B[imagePullPrecheck実行]
    B --> C{プルが必要か?}
    C -->|はい| D[pullImage実行]
    C -->|いいえ| E[既存イメージ使用]
    D --> F[backoff期間チェック]
    F -->|backoff中| G[BackOffイベント発行]
    F -->|backoff外| H[CRIランタイムへプルリクエスト]
    H --> I{プル結果}
    I -->|成功| J[Pulledイベント記録]
    I -->|失敗| K[Failed PullImageイベント記録]
    K --> L[backoff期間設定]
    L --> M[エラーメッセージ評価 evalCRIPullErr]
    M --> N[ErrImagePull返却]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | - | Kubernetes EventはetcdにEvent APIリソースとして保存される |

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

本通知はデータベースを直接参照しない。Kubernetes API経由でPod情報、Secret情報などを取得する。

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Event APIリソース（etcd） | INSERT | Warningイベントの記録 |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Type | Warning | EventType |
| INSERT | Reason | Failed | イベント理由 |
| INSERT | Message | "Failed to pull image..." | エラー詳細を含むメッセージ |
| INSERT | InvolvedObject | Pod ObjectReference | 対象Pod |
| INSERT | Source.Component | kubelet | 発行元コンポーネント |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ErrImagePull | 一般的なイメージプル失敗 | backoff付きリトライ |
| RegistryUnavailable | レジストリが応答しない | backoff付きリトライ、レジストリの可用性確認 |
| SignatureValidationFailed | イメージ署名の検証に失敗 | 署名ポリシーの確認、イメージの再署名 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 制限なし（backoffメカニズムにより間隔を空けて継続的にリトライ） |
| リトライ間隔 | exponential backoff（flowcontrol.Backoff使用） |
| リトライ対象エラー | 全てのイメージプルエラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | EventRecorderのバースト設定に依存 |
| 1日あたり上限 | 制限なし（ただしEvent TTLによる自動削除あり） |

### 配信時間帯

制限なし（24時間365日）

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

- イメージ名にプライベートレジストリのURLが含まれる場合があるが、Eventとして記録されるため、RBACによるアクセス制御が適用される
- 認証情報（imagePullSecret）の内容はイベントメッセージには含まれない
- CRIランタイムからのエラーメッセージがそのまま記録されるため、内部情報が露出する可能性がある

## 備考

- イメージプル失敗後、kubeletはbackoffメカニズムにより自動的にリトライを行う
- backoff期間中のリトライ要求に対してはBackOffイベント（No.24）が発行される
- evalCRIPullErr関数により、CRI固有のエラー（RegistryUnavailable、SignatureValidationFailed）は専用のエラーメッセージに変換される

---

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

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

### 推奨読解順序

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

まず、イメージプル処理に関連するエラー型とインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.go | `pkg/kubelet/images/types.go` | ErrImagePull, ErrImagePullBackOff等のエラー変数定義（27-42行目）、ImageManagerインターフェース（49-54行目） |
| 1-2 | event.go | `pkg/kubelet/events/event.go` | FailedToPullImage定数の定義（49行目）、Image event reason list（46-53行目） |

**読解のコツ**: Go言語のerrors.New()でセンチネルエラーが定義されており、これらがコンテナステータスのwaiting.reasonフィールドに反映される。

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

処理の起点となるEnsureImageExists関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | image_manager.go | `pkg/kubelet/images/image_manager.go` | EnsureImageExists関数（164-282行目）がイメージ確保の主要エントリーポイント |

**主要処理フロー**:
1. **164行目**: EnsureImageExists関数定義、Pod情報とイメージ名を受け取る
2. **176-181行目**: デフォルトタグの適用とInspectFailedイベント発行（タグ解析失敗時）
3. **197行目**: imagePullPrecheckによるプル前チェック
4. **281行目**: pullImage呼び出し

#### Step 3: プル前チェック処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | image_manager.go | `pkg/kubelet/images/image_manager.go` | imagePullPrecheck関数（109-136行目）、pullPolicy別の分岐処理 |

**主要処理フロー**:
- **117-127行目**: PullAlwaysの場合は即座にプルへ、PullIfNotPresent/PullNeverの場合はイメージ存在確認
- **122-126行目**: GetImageRef失敗時にInspectFailedイベント発行

#### Step 4: イメージプル実行とエラーハンドリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | image_manager.go | `pkg/kubelet/images/image_manager.go` | pullImage関数（317-382行目）、backoffチェック、CRIプル実行、エラー評価 |

**主要処理フロー**:
- **337-350行目**: backoff期間チェック。期間中はBackOffイベントを発行しErrImagePullBackOffを返却
- **355行目**: Pullingイベント発行
- **359-360行目**: CRIランタイムへのプルリクエスト送信と結果受信
- **361-370行目**: プル失敗時のFailedイベント発行、backoff設定、evalCRIPullErrによるエラー評価
- **384-413行目**: evalCRIPullErr関数、RegistryUnavailableとSignatureValidationFailedの特別処理

#### Step 5: CRI呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | kuberuntime_manager.go | `pkg/kubelet/kuberuntime/kuberuntime_manager.go` | EnsureImageExistsの呼び出し元（1799行目付近） |

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

```
kuberuntime_manager.go: startContainer()
    |
    +-- imagePuller.EnsureImageExists()       [image_manager.go:164]
            |
            +-- applyDefaultImageTag()         [image_manager.go:417]
            +-- imagePullPrecheck()            [image_manager.go:109]
            |       |
            |       +-- imageService.GetImageRef()
            |       +-- imageNotPresentOnNeverPolicyError()  [image_manager.go:156]
            |
            +-- pullImage()                    [image_manager.go:317]
                    |
                    +-- backOff.IsInBackOffSinceUpdate()
                    +-- puller.pullImage()      (CRIランタイム呼び出し)
                    +-- logIt() --> recorder.Event()  [Failedイベント発行: 362行目]
                    +-- evalCRIPullErr()        [image_manager.go:384]
```

### データフロー図

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

Pod spec               --> EnsureImageExists()          --> imageRef (成功時)
  requestedImage            |                               error (失敗時)
  pullSecrets               +-- imagePullPrecheck()
  pullPolicy                +-- pullImage()
                                |
CRIランタイム           <-- puller.pullImage()
  (containerd/CRI-O)        |
                            +-- pullResult (err)
                                |
                            +-- logIt()                 --> Kubernetes Event API
                                recorder.Event()            (Warning/Failed)
                                |
                            +-- evalCRIPullErr()        --> ErrImagePull
                                                            ErrRegistryUnavailable
                                                            ErrSignatureValidationFailed
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/kubelet/events/event.go` | ソース | イベント理由定数の定義 |
| types.go | `pkg/kubelet/images/types.go` | ソース | エラー型・ImageManagerインターフェース定義 |
| image_manager.go | `pkg/kubelet/images/image_manager.go` | ソース | イメージプル処理の主要実装、イベント発行ロジック |
| kuberuntime_manager.go | `pkg/kubelet/kuberuntime/kuberuntime_manager.go` | ソース | EnsureImageExistsの呼び出し元（コンテナ起動処理） |
| image_manager_test.go | `pkg/kubelet/images/image_manager_test.go` | テスト | イメージプル失敗シナリオのテストケース |
