様々なリソースをKubernetesの管理に統一するCrossplaneについて

## はじめに
今年もこの季節がやってまいりました。本年もOpenStandiaでAdvent Calendarを書いていきます。今年は特にテーマは決めずOpenStandiaのメンバが興味のある技術について25日間書いていきますのでよろしくお付き合いください！




## crossplaneについて
1日目はさっそく今年Could Native Computing Foundation(CNCF)の Incubating project になったcrossplaneを評価してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675473/c74fcaf1-a810-595a-f1c3-cf3503aec98e.png)

crossplaneってなんぞやということで公式サイトをまずは見てみました。どうやら下記のように書かれています。

https://crossplane.io/
＃上の方にあるDocumentationからたどれます。
> Crossplane is an open source Kubernetes add-on that transforms your cluster into a universal control plane. Crossplane enables platform teams to assemble infrastructure from multiple vendors, and expose higher level self-service APIs for application teams to consume, without having to write any code.

CrossplaneはKubernetesクラスタを普遍的なコントロールプレーンへ変換するためのKubernetesのアドオンです。これによってプラットフォームチームが新しくコードを書くことになしに、様々なインフラを組み合わせたり、アプリチームが利用するためにKubernetesのAPIを公開することが可能になる。と書いています。いきなりこういわれてもわからんと思いますので、簡潔にまとめると主に下記の２つができるようになります。

- AWSやGCPなどのクラウドベンダーが提供するインフラや、Kubernetes/Helmなどのソフトウェアが提供するリソースを統合的にKubenetesで管理できるようになる。
- 上記のリソースを組み合わせたリソースを作成し、それに対応する新しいKubernetesのAPIを公開することができる。

つまり、いろいろなものがYAML化していきますね。YAMLおじさんの皆さん、おめでとうございます（白目）

## リソースをKubernetesで管理する
KuberentesにCrossplaneのProviderを入れることで、そのカスタムリソース定義(CRD)などが作成され、アクセストークンなどの認証情報を用いて、そのカスタムリソースが作成されると、対応するリソースが作成されます。
2022年12月現在で対応しているProviderは下記になります。
- aws
- azure
- gcp
- alubaba
- kubernetes
- helm
- newrelic
- gitlab
などなど他にもあります。詳細はこちらを確認ください。https://marketplace.upbound.io/providers
上記のベンダのリソース(たとえば、RDS)は下記のようなマニフェストで表現されています。このようなベンダ毎に作成されるリソースのことをcrossplaneでは`managed resource`と呼んでいます。
```yaml
apiVersion: database.aws.crossplane.io/v1beta1
kind: RDSInstance
metadata:
  name: rdspostgresql
spec:
  forProvider:
    region: us-east-1
    dbInstanceClass: db.t2.small
    masterUsername: masteruser
    allocatedStorage: 20
    engine: postgres
    engineVersion: "12"
    skipFinalSnapshotBeforeDeletion: true
  writeConnectionSecretToRef:
    namespace: crossplane-system
    name: aws-rdspostgresql-conn
```


## 新しいリソースを作成し、KubernetesのAPIで公開する
crossplaneをKubernetesにインストールすると、さまざまなリソースが作られます。その中で重要なものを紹介します。軽く概要を示す図を貼っておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675473/72d6c282-6058-ac38-f2b1-205de08571da.png)
軽く説明するとManaged Resource(MR)の集合体をComposite Resource(XR)といい、その利用までのインターフェースのことをCompositionResourceDefinition(xRD)と言います。xRDを基にclaimを使って実際のXRを作成します


### Composite Resource(複合リソース,XR)
Composite Resourceとは1つ以上のManaged Resourceからなるリソースです。。例えばAWSではネットワークが使えるようになるためには、VPCだったり、Subnetだったり、InternetGWなどなどを組み合わせることが前提となっており、その組み合わせたものを一つのKubernetesリソースとして扱うことができます
Composite Resourceのマニフェストの例としては下記になります。
```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: example
  labels:
    crossplane.io/xrd: xpostgresqlinstances.database.example.org
    provider: gcp
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef: # ★1
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
  - name: cloudsqlinstance
    base:
      apiVersion: database.gcp.crossplane.io/v1beta1
      kind: CloudSQLInstance
      spec:
        forProvider:
          databaseVersion: POSTGRES_12
          region: us-central1
          settings:
            tier: db-custom-1-3840
            dataDiskType: PD_SSD
            ipConfiguration:
              ipv4Enabled: true
              authorizedNetworks:
                - value: "0.0.0.0/0"
    patches: #★2
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.storageGB
      toFieldPath: spec.forProvider.settings.dataDiskSizeGb
```
★1部分については、resource以下で定義されているMRをまとめたリソースのAPIを公開するためのapiVersionとkindを設定している。また★2については、公開したAPIからどのようなValueが入り、それがresource配下のどのパラメータに対応するのかを定義している。

### Composite Resource Definition(複合リソース定義, XRD)

Composite Resource Definitionとは、作成したいCompositionのスキーマを定義するためのリソースです。実際にアプリチームとしては、このXRDによって定義されたAPIに対してマニフェストを作成し、Compositionを作成します。Compositionはクラウドベンダの色が出るパラメータになっていますが、うまくクラウドベンダ専用のものがなくなるようにXRDを定義することによって、同じマニフェストでAWSやAzure、GCPの同じようなリソースを作成できます。

xRDのマニフェストの例としては下記の通り。これは、DBのストレージサイズを求めるようなAPIを公開している。
```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.example.org
spec:
  group: database.example.org
  names:
    kind: XPostgreSQLInstance # ☆1
    plural: xpostgresqlinstances
  claimNames:
    kind: PostgreSQLInstance
    plural: postgresqlinstances
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema: 
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  storageGB:
                    type: integer
                required:
                - storageGB
            required:
            - parameters
```
★1部分では定義したCompositionのKind名を指定する。ちょうどXRのマニフェストの例の★1部分が該当する。


### Claim
上記xRDで公開したKubernetesのAPIを通して実際に利用するためのリソースをClaimと言います。Claimというと、PersistentVolumeClaimがあるのですが、考え方としては同じで、ユーザの要求に従ってCompositionResourceが払い出されます。マニフェストの例としては下記になります。
```yaml
apiVersion: database.example.org/v1alpha1
kind: PostgreSQLInstance
metadata:
  namespace: default
  name: my-db
spec:
  parameters:
    storageGB: 20
  compositionRef:
    name: production
  writeConnectionSecretToRef:
    name: my-db-connection-details
```

## 触ってみる
それでは実際にCrossplaneを触ってみましょう。
### 実行環境
- MacOS Monterey
- minikube v1.26.0
- helm v3.9.2
- crossplane ver1.10.1

### crossplaneのインストール

```bash
  $ kubectl create ns crossplane-system
  namespace/crossplane-system created
  $ helm install crossplane --namespace crossplane-system crossplane-stable/crossplane
  NAME: crossplane
  LAST DEPLOYED: Sun Nov 27 18:23:32 2022
  NAMESPACE: crossplane-system
  STATUS: deployed
  REVISION: 1
  TEST SUITE: None
  NOTES:
  Release: crossplane

  Chart Name: crossplane
  Chart Description: Crossplane is an open source Kubernetes add-on that enables platform teams to assemble 
  infrastructure from multiple vendors, and expose higher level self-service APIs for application teams to consume.
  Chart Version: 1.10.1
  Chart Application Version: 1.10.1

  Kube Version: v1.24.1
  $ kube get po -n crossplane-system
  NAME                                       READY   STATUS    RESTARTS   AGE
  crossplane-7d4bbdd8dd-dpmdb                1/1     Running   0          2m11s
  crossplane-rbac-manager-7f9b4f6d47-zp8mx   1/1     Running   0          2m11s

  $ curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
  kubectl plugin downloaded successfully! Run the following commands to finish installing it:

  sudo mv kubectl-crossplane /usr/local/bin
  kubectl crossplane --help

  Visit https://crossplane.io to get started. 🚀
  Have a nice day! 👋
```
### Providerのインストール

```bash
  $ cat provider.yaml
  apiVersion: pkg.crossplane.io/v1
  kind: Provider
  metadata:
    name: provider-aws
    namespace: crossplane-system
  spec:
    package: "xpkg.upbound.io/crossplane-contrib/provider-aws:v0.34.0"
  $ kubectl apply -f provider.yaml
  provider.pkg.crossplane.io/provider-aws created
  $ kubectl get provider -n crossplane-system
  NAME           INSTALLED   HEALTHY   PACKAGE                                                   AGE
  provider-aws   True        True      xpkg.upbound.io/crossplane-contrib/provider-aws:v0.34.0   52s
  $ AWS_PROFILE=default && echo -e "[default]\naws_access_key_id = $(aws configure get aws_access_key_id --profile 
  $AWS_PROFILE)\naws_secret_access_key = $(aws configure get aws_secret_access_key --profile $AWS_PROFILE)" > 
  creds.conf
  $ cat creds.conf
  [default]
  aws_access_key_id = XXXXXXXXXXXXX
  aws_secret_access_key = XXXXXXXXXXXXXXXXXXX
  $ kubectl create secret generic aws-creds -n crossplane-system --from-file=./creds.conf
  secret/aws-creds created
  $ kubectl get secret -n crossplane-system
  NAME                               TYPE                 DATA   AGE
  aws-creds                          Opaque               1      15s
```

### composite resource definitionの作成
```
  $ kubectl apply -f definition.yaml
  compositeresourcedefinition.apiextensions.crossplane.io/xpostgresqlinstances.database.example.org created
  $ kubectl get xrd
  NAME                                        ESTABLISHED   OFFERED   AGE
  xpostgresqlinstances.database.example.org   True          True      12s
```
### composite resourceの作成
```
  $ kubectl apply -f composition.yaml
  composition.apiextensions.crossplane.io/xpostgresqlinstances.aws.database.example.org created
  $ kubectl get composition
  NAME                                            AGE
  xpostgresqlinstances.aws.database.example.org   70s
```
### claimを通して、compotisionの作成
```
  $ kubectl apply -f claim.yaml
  postgresqlinstance.database.example.org/my-db created
  $ kubectl get rdsinstance
  NAME                READY   SYNCED   STATE       ENGINE     VERSION   AGE
  my-db-s66fm-f88f6   True    True     available   postgres   12.11     7m58s
```

#### 備考: crossplane で作成されたリソースは下記のように確認可能

```
  $ kubectl get claim
  NAME    SYNCED   READY   CONNECTION-SECRET   AGE
  my-db   True     True    db-conn             9m48s
  $ kubectl get composite
  NAME          SYNCED   READY   COMPOSITION                                     AGE
  my-db-s66fm   True     True    xpostgresqlinstances.aws.database.example.org   9m57s
  $ kubectl get managed
  NAME                                                       READY   SYNCED   STATE       ENGINE     VERSION   AGE
  rdsinstance.database.aws.crossplane.io/my-db-s66fm-f88f6   True    True     available   postgres   12.11     10m
```


## 感想
ちょうど上の例をGCPのCloudSQLで実現しようとすると下記の構成になります。
![image2022-10-17_11-37-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675473/c1bff93a-3f4a-4719-f06b-b33ab7a7b9ec.png)
また同じような考え方を用いることで、コンテナ基盤(例: ECS, Cloud Run, GKEなど)の抽象化も可能かなと考えています。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675473/90502c78-6414-ec16-c9de-41cdbe4f132a.png)
上記の例だと、例えばコンテナイメージ自体やバージョンをclaimに指定させて、状況によってCompositionを変えることで、同じ構成のアプリケーションがたくさん増やすことができるのかなと感じました。

ただし、このComposite Resource、composite resource definitionを各クラウドリソースの特徴を理解して、作成するスーパーマンみたいな人が必要かなと感じています。さらに作成した後、この方式を運用しなければならず、いろいろ検討するところはありそうです。Crossplane自体は、OpenApplicationModelのKubernetes実装としていて、運用面としては基盤チーム、アプリチームから、新しく第3のチームとしてのアプリ運用チームといった組織が面倒をみていく想定になっているようです。

## 参考リンク

https://crossplane.io/