CrossplaneのUsage機能を使ってKubernetesリソースの誤削除を防ぐ

# はじめに

<!-- 挨拶文を入れてください -->
みなさん、こんにちは。日立製作所の宮村です。今年の8月に開催されたKubeDay Japanにて、「Towards a World Without Dependency Consideration: All Resources Be Deleted Simply」というタイトルで発表しました。  
発表をざっくりと説明すると、  
>・ 意図せずにKuberenetesのリソースを誤削除することでアプリやクラスターに影響が出てしまう  
  ・ リソースを削除する際に、リソース間の依存性を考慮して削除する必要があるケースも存在する  
そこで、上記の問題を解決するために*Lien*という機能をKubernetes本体に組み込むためにKEP-2839で提案している。しかし、現在KEP-2839の議論が停滞している。そのため、Crossplaneに組み込まれたLienと類似の機能であるUsageを使用して、誤削除や削除順の制御の動作イメージをデモする  

といった内容でした。

そこで、本記事ではKubeDayの発表でお見せしたデモを実施する手順について共有したいと思います。  
発表内容についてもっと詳しく知りたい方は、以下のリンク先から確認できるので、見てみてください。  
- [発表スライド](https://static.sched.com/hosted_files/kubedayjapan2024/67/Towards_a_World_Without_Dependency_Consideration.pdf?_gl=1*1i6fbd4*_gcl_au*MTAwNjkzNjMwOS4xNzI3NzQxMjQw*FPAU*MTAwNjkzNjMwOS4xNzI3NzQxMjQw)  
- 発表動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/UiDodYNTCMk?si=-MiIWMXT1aP4ud4r" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

<!-- 挨拶文を入れてください -->

#  前提条件
- 今回のデモではKuberentesのバージョン1.30で試しています
- Helmのバージョンはv3.2.0以上が必要です
- IAMのクレデンシャル(アクセスキーとシークレットキー)が必要です
- AWSのサービス(S3のバケット)を作成するため、これらのリソースを作成できるパーミッションを持つAWSアカウントが必要です
- Crossplaneの最新バージョンは1.18ですが、デモで使用したバージョンは1.16です。


# 1. 事前準備
## 1-1. Crossplaneのインストール

1. CrossplaneのHelmレポを追加します
```
helm repo add crossplane-stable https://charts.crossplane.io/stable
```
```
helm repo update
```

2.  Crossplaneをcrossplane-system namespaceにインストールします。  
 `Usage`の機能はalpha featureのため、デフォルトでは無効になっています。そのため、フィーチャーフラグを有効にしてあげる必要があります。

```
helm install crossplane \
--namespace crossplane-system \
--create-namespace crossplane-stable/crossplane \
--set args='{"--enable-usages"}'
```

3. インストールされたことを確認します
```
kubectl get pod -n crossplane-system

NAME                                                        READY   STATUS    RESTARTS       AGE
crossplane-5c9bb84977-l8vzs                                 1/1     Running   11 (23m ago)   124d
crossplane-rbac-manager-bcddfb7-hm4l7                       1/1     Running   11 (23m ago)   124d
```

4. crosspleneのKubernetes APIが作成されていることを確認します
```
kubectl api-resources  | grep crossplane

compositeresourcedefinitions               xrd,xrds     apiextensions.crossplane.io/v1         false        CompositeResourceDefinition
compositionrevisions                       comprev      apiextensions.crossplane.io/v1         false        CompositionRevision
compositions                               comp         apiextensions.crossplane.io/v1         false        Composition
environmentconfigs                         envcfg       apiextensions.crossplane.io/v1alpha1   false        EnvironmentConfig
usages                                                  apiextensions.crossplane.io/v1alpha1   false        Usage
configurationrevisions                                  pkg.crossplane.io/v1                   false        ConfigurationRevision
configurations                                          pkg.crossplane.io/v1                   false        Configuration
controllerconfigs                                       pkg.crossplane.io/v1alpha1             false        ControllerConfig
deploymentruntimeconfigs                                pkg.crossplane.io/v1beta1              false        DeploymentRuntimeConfig
functionrevisions                                       pkg.crossplane.io/v1beta1              false        FunctionRevision
functions                                               pkg.crossplane.io/v1beta1              false        Function
locks                                                   pkg.crossplane.io/v1beta1              false        Lock
providerrevisions                                       pkg.crossplane.io/v1                   false        ProviderRevision
providers                                               pkg.crossplane.io/v1                   false        Provider
storeconfigs                                            secrets.crossplane.io/v1alpha1         false        StoreConfig
```

## 1-2. AWSとCrossplaneを接続するためにAWS Providerをインストール

1. AWS S3用のAWS Providerをインストール
```
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws-s3
spec:
  package: xpkg.upbound.io/upbound/provider-aws-s3:v1.10.0
EOF
```
※ここでは、デモで使用したv1.10.0を指定していますが、2024年12月時点での最新バージョンは、v1.17.0です。  

2. インストールされたことを確認します
```
kubectl get providers
NAME                          INSTALLED   HEALTHY   PACKAGE                                                 AGE
provider-aws-s3               True        True      xpkg.upbound.io/upbound/provider-aws-s3:v1.10.0         124d
upbound-provider-family-aws   True        True      xpkg.upbound.io/upbound/provider-family-aws:v1.10.0     124d
```
`provider-aws-s3` というS3用のプロバイダーが作成されます。また、`upbound-provider-family-aws`というプロバイダーも同時に作成されています。
`upbound-provider-family-aws`プロバイダーは、全てのAWS family providers(S3やDynamoDBなどのプロバイダーはファミリープロバイダーの一部)に対して、AWSへの認証を管理するプロバイダーのことです。

### 1-3. AWS用のシークレットを作成します  

1. `aws-credentials.txt`にアクセスキーとシークレットキーを入力します
```
[default]
aws_access_key_id = <access_key>
aws_secret_access_key = <secret_key>
```

2. Secretを作成します
```
kubectl create secret \
generic aws-secret \
-n crossplane-system \
--from-file=creds=./aws-credentials.txt
```

3. 作成したSecretを確認します
```
kubectl describe secret aws-secret -n crossplane-system
Name:         aws-secret
Namespace:    crossplane-system
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
creds:  116 bytes
```

## 1-4. ProviderConfigの作成

AWS Providerの設定をカスタマイズするためにProviderConfigを作成します。今回は、`secretRef`に作成したシークレットを設定します。
```
cat <<EOF | kubectl apply -f -
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-secret
      key: creds
EOF
```

ここまでで、CrossplaneでS3を管理するための準備が完了しました。  
ここから、S3の作成からUsageを使用して削除を防止するまでの手順を記載します。

# 2. Usageを使用して作成したS3バケットの削除を防止
## 2-1.  Crossplaneで S3 bucketのmanaged resource作成

1. managed resource用のyamlファイル(例: s3-mr.yaml)を作成します
```
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  generateName: crossplane-bucket- #名前の末尾にランダムのハッシュ値が追加される
spec:
  forProvider:
    region: ap-northeast-1
  providerConfigRef:
    name: default
```

2. S3 bucketを作成します
```
kubectl create -f s3-mr.yaml
bucket.s3.aws.upbound.io/crossplane-bucket-8fhmp created
```

3. S3 bucketが作成されたことを確認します
```
kubectl get buckets
NAME                      SYNCED   READY   EXTERNAL-NAME             AGE
crossplane-bucket-8fhmp   True     True    crossplane-bucket-8fhmp   19s
```
READYとSYNCEDの値がTrueであれば、bucketの作成が完了しています。

4. 実際にAWSの環境でも出来ているかawsコマンドで確認してみます
```
aws s3 ls |grep crossplane
2024-12-02 12:15:52 crossplane-bucket-8fhmp
```

## 2-2.  S3バケット用のUsageを作成
1. Usage用のyamlファイル(例: s3-usages.yaml)を作成します
```
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: Usage
metadata:
  name: protect-s3-bucket
spec:
  of:
    apiVersion: s3.aws.upbound.io/v1beta1
    kind: Bucket
    resourceRef:
      name: crossplane-bucket-8fhmp  # 削除防止対象のリソース名
  reason: "Production S3 bucket - should never be deleted!"
```
`spec.of`で削除を防止する対象を指定します。  
`spec.of.apiVersion`と`spec.of.kind`は、managed resourceで定義した値を記入します。  
`reason`には、`kubectl delete`などで削除しようした時に出力されるメッセージを記入します。

2. Usageを作成します
```
kubectl create -f s3-usages.yaml
usage.apiextensions.crossplane.io/protect-s3-bucket created
```

3. Usageが作成されたことを確認します
```
kubectl get usages
NAME                  DETAILS                                                             READY   AGE
protect-s3-bucket     Production S3 bucket - should never be deleted!                     True    18s
```

4. Usageが作成されると、保護対象のリソースの` crossplane.io/in-use=true`という使用中のラベルが付与されます。  
このラベルが付与されている間はリソースが削除から保護されます。  
in-useラベルが作成したS3バケットに付与されているか確認します。
```
kubectl describe bucket crossplane-bucket-8fhmp |grep in-use
Labels:       crossplane.io/in-use=true
```

## 2-4. S3バケットを削除しようとするとUsageが削除を防止するか確認
managed resource(bucket)を削除します。
```
# kubectl delete buckets crossplane-bucket-8fhmp
Error from server (This resource is in-use by 1 Usage(s), including the Usage "protect-s3-bucket" with reason: "Production S3 bucket - should never be deleted!".): admission webhook "nousages.apiextensions.crossplane.io" denied the request: This resource is in-use by 1 Usage(s), including the Usage "protect-s3-bucket" with reason: "Production S3 bucket - should never be deleted!".
```
無事、Admission webhookが削除リクエストを拒否してくれたので削除が防止されました。
> admission webhook "nousages.apiextensions.crossplane.io" denied the request

# 終わりに

<!-- 〆の挨拶文を入れてください -->
以上、CrossplaneのUsage機能を使用してKubernetesリソースの誤削除を防止する機能の紹介でした。 
S3バケット単体の誤削除防止のデモで使用した手順についての説明でした。次回は、S3とDynamoDBを例として、Usageにおける削除の順序性を考慮したリソースの保護について紹介したいと思います。
<!-- 〆の挨拶文を入れてください -->

# 参考リンク
[Install Crossplane](https://docs.crossplane.io/v1.16/software/install/)  
[Usages](https://docs.crossplane.io/latest/concepts/usages/)  
[AWS Quickstart](https://docs.crossplane.io/v1.16/getting-started/provider-aws/)

---

<!-- 他社商標に関する記載を適宜修正してください -->

- AWS は、米国その他の諸国における Amazon.com, Inc. またはその関連会社の商標です。
- Crossplaneは、米国およびその他の国におけるThe Linux Foundationの商標または登録商標です。
- Kubernetesは、米国およびその他の国におけるThe Linux Foundationの商標または登録商標です。
- その他、記載されている会社名および商品・製品・サービス名は、各社の商標または登録商標です。