Kubernetes: ownerReferencesはクラスタースコープ↔ネームスペーススコープで使えるのか？

こんにちは。Kubernetesの`ownerReferences`について、クラスタースコープのリソースとネームスペースリソースが関わった場合、どのような振る舞いになるのか気になったので実験してみました。本稿では、その結果についてご紹介します。

## 結論

検証の結果をまず先にお伝えします：

1. クラスタースコープのリソースをネームスペースリソースのownerReferencesに指定することは、APIサーバーレベルでは**可能**でした。
2. ネームスペースリソースをクラスタースコープのリソースのownerReferencesに指定することは、APIサーバーレベルで**完全に拒否**されました。

## 検証の背景と目的

Kubernetesの公式ドキュメントには、`ownerReferences`について以下のように記載されています：

> namespace間のowner referenceは、設計上許可されていません。namespaceの依存関係は、クラスタースコープまたはnamespaceのオーナーを指定できます。namespaceのオーナーは、依存関係と同じnamespaceに存在する必要があります。そうでない場合、owner referenceは不在として扱われ、すべてのオーナーが不在であることが確認されると、依存関係は削除される可能性があります。

この記述を読んで、以下の2つのエッジケースについて疑問を持ちました。

1. クラスタースコープのリソースをネームスペースリソースのownerReferencesに指定できるのか？
2. ネームスペースリソースをクラスタースコープのリソースのownerReferencesに指定できるのか？

## 検証環境

検証には以下の環境を使用しました：

- Kubernetes v1.32.0（kind環境）
- kind
- kubectl

## 検証方法

検証のために、以下のカスタムリソース定義（CRD）を作成しました：

### 1. クラスタースコープのCRD

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: globalconfigs.example.com
spec:
  group: example.com
  names:
    kind: GlobalConfig
    listKind: GlobalConfigList
    plural: globalconfigs
    singular: globalconfig
  scope: Cluster
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                configValue:
                  type: string
                description:
                  type: string
              required:
                - configValue
```

### 2. ネームスペーススコープのCRD

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: appconfigs.example.com
spec:
  group: example.com
  names:
    kind: AppConfig
    listKind: AppConfigList
    plural: appconfigs
    singular: appconfig
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                appName:
                  type: string
                settings:
                  type: object
                  x-kubernetes-preserve-unknown-fields: true
              required:
                - appName
```

## 検証1: クラスタースコープのリソースをネームスペースリソースのオーナーに指定

### 検証手順

1. ローカルKubernetesクラスターを作成

```bash
kind create cluster --name owner-ref-test
```

2. カスタムリソース定義（CRD）をインストール

```bash
# クラスタースコープのCRD
kubectl apply -f manifests/cluster-scoped-crd.yaml

# ネームスペーススコープのCRD
kubectl apply -f manifests/namespaced-crd.yaml
```

3. テスト用ネームスペースを作成

```bash
kubectl create namespace test
```

4. クラスタースコープのカスタムリソースを作成

```bash
kubectl apply -f manifests/cluster-resource.yaml
```

このコマンドは、以下の内容のクラスタースコープのカスタムリソースを作成します：

```yaml
apiVersion: example.com/v1
kind: GlobalConfig
metadata:
  name: global-config-1
spec:
  configValue: "global-setting"
  description: "クラスタースコープのリソース例"
```

5. 無効なネームスペースリソースの作成（クラスタースコープリソースを参照）

```bash
kubectl apply -f manifests/invalid-namespaced-resource.yaml
```

このコマンドは、以下の内容のネームスペースリソースを作成しようとします。このリソースは、クラスタースコープのリソースをownerReferencesとして参照しています：

```yaml
apiVersion: example.com/v1
kind: AppConfig
metadata:
  name: invalid-app-config
  namespace: test
  ownerReferences:
  - apiVersion: example.com/v1
    kind: GlobalConfig
    name: global-config-1
    uid: "PLACEHOLDER_UID" # 実行時に実際のUIDに置き換える必要があります
spec:
  appName: "invalid-test-app"
  settings:
    key1: "value1"
    key2: "value2"
```

注：実際の検証では、`PLACEHOLDER_UID`は`kubectl get globalconfig global-config-1 -o jsonpath='{.metadata.uid}'`コマンドで取得した実際のUIDに置き換えられます。

### 検証結果

**予想に反して、このコマンドは成功しました**。エラーは発生せず、クラスタースコープのリソースをownerReferencesに持つネームスペースリソースが正常に作成されました。

```bash
# 作成されたリソースの確認
kubectl get appconfig -n test invalid-app-config -o yaml
```

```yaml
apiVersion: example.com/v1
kind: AppConfig
metadata:
  # ...
  name: invalid-app-config
  namespace: test
  ownerReferences:
  - apiVersion: example.com/v1
    kind: GlobalConfig
    name: global-config-1
    uid: 741f513d-9b15-465e-a643-85fefd3bb3d4
  # ...
spec:
  appName: invalid-test-app
  settings:
    key1: value1
    key2: value2
```

## 検証2: ネームスペースリソースをクラスタースコープのリソースのオーナーに指定

### 検証手順

1. ローカルKubernetesクラスターを作成

```bash
kind create cluster --name owner-ref-reverse-test
```

2. カスタムリソース定義（CRD）をインストール

```bash
kubectl apply -f manifests/cluster-scoped-crd.yaml
kubectl apply -f manifests/namespaced-crd.yaml
```

3. テスト用ネームスペースを作成

```bash
kubectl create namespace test
```

4. ネームスペースリソースを作成（所有者として使用）

```bash
kubectl apply -f manifests/namespaced-owner-resource.yaml
```

このコマンドは、以下の内容のネームスペースリソースを作成します：

```yaml
apiVersion: example.com/v1
kind: AppConfig
metadata:
  name: owner-app-config
  namespace: test
spec:
  appName: "owner-test-app"
  settings:
    key1: "value1"
    key2: "value2"
```

5. 無効なクラスタースコープリソースの作成（ネームスペースリソースを参照）

```bash
# 実際のUIDを取得
NAMESPACE_RESOURCE_UID=$(kubectl get appconfig -n test owner-app-config -o jsonpath='{.metadata.uid}')

# UIDを置き換えたマニフェストを適用
kubectl apply -f manifests/invalid-cluster-resource.yaml
```

このコマンドは、以下の内容のクラスタースコープリソースを作成しようとします：

```yaml
apiVersion: example.com/v1
kind: GlobalConfig
metadata:
  name: invalid-global-config
  ownerReferences:
  - apiVersion: example.com/v1
    kind: AppConfig
    name: owner-app-config
    namespace: test
    uid: "PLACEHOLDER_UID" # 実行時に実際のUIDに置き換える
spec:
  configValue: "invalid-global-setting"
  description: "ネームスペースリソースを所有者に持つクラスタースコープのリソース例"
```

### 検証結果

このコマンドは**失敗**し、以下のようなエラーメッセージが表示されました：

```
Error from server (BadRequest): error when creating "...": GlobalConfig in version "v1" cannot be handled as a GlobalConfig: strict decoding error: unknown field "metadata.ownerReferences[0].namespace"
```


## まとめ

Kubernetes v1.32.0（kind環境）での検証結果から、以下のことがわかりました：

1. クラスタースコープのリソースをネームスペースリソースのownerReferencesに指定することは**可能**
2. ネームスペースリソースをクラスタースコープのリソースのownerReferencesに指定することは**不可能**

最後までお読みくださりありがとうございました。Twitterでは[@suin](https://twitter.com/suin)で技術情報をツイートしていますので、よかったらフォローお願いします。