Tanzu Application Platform Tanzu アプリケーションのモダナイゼーション

Tanzu Application Platformによってデータベースも管理:Crossplaneによる動的プロビジョニング

この記事では、VMware Tanzu Application Platform (以降 TAP )の動的プロビジョニングについて紹介します。

これまで開発者に対して TAP を利用しデータベースやメッセージ・ブローカーのようなバックエンド・サービスにワークロードも簡単にバインドできる強力な機能をご紹介してきました。
Tanzu Application Platform でサービス連携の「面倒なこと」を「約束」で解決:Service Bindings
Tanzu Application Platform で開発者が使いやすいサービスを – Services Toolkit の紹介
しかし、いままで紹介していきたブログでは、管理者が事前にデータベースなどを用意することが前提でした。ところが TAP 1.5 からついにデータベースの作成をも TAP からできるようになりました。
それが今回紹介する Crossplane と呼ばれる動的プロビジョニング機能です。

 

開発フェーズの違いで求められるデータサービス利用の要件

これまで提供してきた機能は開発者とっての使いやすさを実現してきました。
例えば、 Services Toolkit を利用したデータベース利用はサービスの裏方となる データベース(以降 DB ) などのデータサービス自体はプロビジョニングが手作業で行いプール化する方法をとっています。
この方法は確かに本番環境を想定したデータサービスとして、”開発者に勝手に DB を作って欲しくない”、”しっかりとしたDB設計”という観点で必要な機能となります。

しかし、実際の開発初期フェーズの段階では データベース管理者(以降 DBA ) とやり取りするために申請書を書いたり、DB が払い出されるまで待たなければなりません。
また、ローカルで実施していたとりあえずのモック環境等で開発を進めていたものが DB を接続すると問題が発生して後戻りするといったことも発生します。

DBA とやり取りする時間は取れない、でも DB は使いたい。これを実現するのがTAP 1.5.0でリリースされた動的プロビジョニング機能です。

 

動的プロビジョニング機能とは?

動的プロビジョニング機能は TAP 上( Tanzu Application Platform 1.5.0 以上)で利用できるものとなります。
実際の動きを見る前に Crossplane とは何なのか?簡単に説明したいと思います。
Crossplane は元々 Upbound という会社が開発し2020/5に CNCF のプロジェクトとして認定され現在、Incubating まで昇格している OSS です。
VMwareはUpboundとのパートナーシップを発表しており、今回ご紹介した TAP 1.5 の新機能として今後、複数のクラウドのプロバイダ機能がアップデートされていくことが期待されます。

では AWS RDS でのCrossplaneカスタマイズの例を公式ドキュメントに沿って試してみたいと思います。
始める前にCrossplaneで出てくるコンポーネントとその役割を下記のテーブルと公式サイトの概念図にまとめておきます。


TAP 1.5 からは、crossplane.tanzu.vmware.com という名前の Carvel パッケージが含まれており、デフォルトで full、iterate、run の各プロファイルに含まれています。
このパッケージに、動的プロビジョニング機能を実現する Upbound Universal Crossplane(UXP)が含まれています。
さらに、このパッケージには、provider-helm と provider-kubernetes という2つの設定済み Crossplane プロバイダが含まれています。どちらのプロバイダも、Composition の一部として使用できる便利なマネージドリソースを提供します。これらはどちらもこれからデモで実演する TAP の Bitnami Services で使用されています。

では早速、実際の動きを見ていきたいと思います。では早速、実際の動きを見ていきたいと思います。この記事では、 TAP 1.6 にて動作確認をしてみたいと思います。
TAP の動的プロビジョニング機能はプリインストールされた Bitnami Services を利用することができます。
開発者用のNamespaceのセットアップは既に完了していることを前提にしています。開発者用のNamespaceのセットアップはこちらを参考ください。

これから行うの動的プロビジョニング手順の流れは以下です。
1. 自身のNamespace環境で利用できるデータサービスのリスト表示
2. 利用できるデータサービスのパラメーター確認
3. データサービスの払い出し(postgresql インスタンス作成)
4. TAP GUIからJave Rest API雛形コードをダウンロード
5. データサービスと連携するアプリケーションをTAP上へデプロイ
6. APIの動作確認(API経由で顧客情報 DB へのデータ登録)

1. 自身のNamespace環境で利用できるデータサービスのリスト表示

自身の開発者 Namespace の環境に設定したら下記のコマンドを実行してください。このコマンドで開発者が利用できるデータサービスが表示されます。

tanzu service class list

出力は以下です。Bitnami Servicesが初期状態から利用できるようになっています。今回、PostgreSQLを利用していきます。

2. 利用できるデータサービスのパラメーター確認

ここに表示されているNAMEを使って、下記のコマンドでデータサービスの指定できるパラメーターを確認できます。

tanzu service class get postgresql-unmanaged

3. データサービスの払い出し(postgresql インスタンス作成)

上記の storageGBはDB容量の単位を指しており、今回は2GB容量の DB を作りたいと思います。下記のコマンドを実行してclass claim を作成します。

class-claimは今回、demo-claimとしています。任意の名称を指定することができます。

tanzu service class-claim create demo-claim --class postgresql-unmanaged -p storageGB=2

下記のコマンドを実行して、class claim の作成状況を確認します。

tanzu services class-claims get demo-claim

ReadyステータスがTrueになっていればDBの作成は終わりです。

DB の払い出しは1コマンドでできてしまいました。まさにセルフサービスでDBが用意されました。
では、早速これからアプリケーションをデプロイしてみます。

4. TAP GUIからJave Rest API雛形コードをダウンロード

TAP GUIを開き、Application Accelerators で Tanzu Java Restful Web App のテンプレートを使って作成したDBを利用したアプリケーションを作成していきます。

Generate AcceleratorsでTanzu Java Restful Web App のテンプレートのパラメーターを入れる画面に移ります。
今回はデモなのでシンプルにパラメーターはデフォルトのままを指定してテンプレートファイルをダウンロードします。

5. データサービスと連携するアプリケーションをTAP上へデプロイ

ソースコードはいじらずそのまま下記のコマンドでWorkload をデプロイします。朱色部分は環境に合わせて変更ください。初回のデプロイは時間がかかります。

tanzu apps workload apply api-with-db-demo \
--app api-with-db-demo \
--local-path <Tanzu Java Restful Web App のテンプレートのローカルパス> \
--source-image <ソースイメージを保存するレポジトリURL> \
--type web \
--build-env BP_JVM_VERSION=11 \
--label app.kubernetes.io/part-of=customer-profile \
--service-ref dbmaster=services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:demo-claim \
--annotation autoscaling.knative.dev/minScale=1 \
-y

下記コマンドでデプロイ完了の状態を確認しエンドポイントURLを確認します。

tanzu apps workload get api-with-db-demo

6. APIの動作確認(API経由で顧客情報 DB へのデータ登録)

APP_URL環境変数に入れてcurlコマンドを使ったREST API経由で顧客情報 DB にデータを登録します。

export APP_URL=<エンドポイントURL>
curl -kX POST -H 'Content-Type: application/json' $APP_URL/api/customer-profiles -d '{"firstName": "vmware", "lastName": "tanzu", "email": "[email protected]"}'

下記のREST API経由で登録した情報を確認します。

curl -kX GET $APP_URL/api/customer-profiles/

下記先ほど登録した情報がJSON形式で表示されれば成功です。

念の為、実際にDBにデータが登録されているかを下記のコマンドで確認してみます。

DB_NAME=$(kubectl get classclaim demo-claim -ojsonpath='{.status.provisionedResourceRef.name}')
kubectl exec -ti -n ${DB_NAME} ${DB_NAME}-0 -- bash -c 'PGPASSWORD=${POSTGRES_PASSWORD} /opt/bitnami/postgresql/bin/psql -U postgres ${POSTGRES_DB}'

TAPでデプロイした アプリケーションをJava REST API 経由でデータベースへの登録が行われたことが確認できました。
このようにTAPではデータサービスを開発者自身でセルフサービスでき、迅速なAPI開発ができることがイメージできたかと思います。
これにさらにTAPブログシリーズで紹介している API Gateway や SSO連携を活用することにより開発者の負担となる非機能要件を抽象化しコードに専念することができます。

 

Crossplane を活用したカスタマイズされた動的プロビジョニング機能

ここからは Crossplane という仕組みを使ってAWS RDS サービスを動的プロビジョニングできるようにカスタマイズする例をご紹介します。

ここまではご紹介してきた Bitnami Service による動的プロビジョニングは開発初期フェーズで非常に強力な機能となります。
一方で、本番環境でもこの動的プロビジョニングを使いたいケースはあるかと思います。例えば、クラウドを活用した開発チームが大規模でその中にデータサービスオペレーターがいるような場合です。 チームでクラウドアカウントを持っているので、 クラウドDBサービスを払い出せる体制が整っていれば、本番用にもより迅速な動的プロビジョニングを使うことができます。

このようなケースでも、 Crossplane を活用して外部の非Kubernetesリソースに接続し、プラットフォームチームがそれらのリソースを利用するためのカスタムKubernetes APIを構築することができます。
現在、TAPのドキュメンテーションでは AWS RDS および VMware SQL with Postgres for Kubernetes のカスタマイズ手順が掲載されています。この他も Crossplane の仕様さえ理解できれば本番向けのデータサービス動的プロビジョニングが可能となります。

では AWS RDS でのCrossplaneカスタマイズの例を公式ドキュメントに沿って試してみたいと思います。

前提条件として、CrossplaneでAWSサービスの動的プロビジョニングを設定する前にAWSへのアクセスできるアカウントを用意してください。
AWS RDS サービスインスタンスの動的プロビジョニング手順の流れは以下です。
1. AWS Provider for Crossplane をインストール
2. CompositeResourceDefinition を作成します。
3. Compositionを作成する
4. 開発者がサービスを検出できるように設定
5. RBAC を構成する
6. 構成を確認する
7. 動的サービスプロビジョニングで作成したAWS RDS サービスでアプリをデプロイ
※upbound marketplaceのここの記載にある手順でUp command-lineとCrossplane(UXP)のインストールは不要です。TAPの場合、Crossplaneはインストール済みであるためです

1. AWS Provider for Crossplane をインストール

TAP 1.6.1 では、3つの選択肢が用意されています。今回はVMware推奨の upbound/provider-family-aws をインストールします(1.5.x ではこの AWS Provider がサポートされていません)。
upbound/provider-family-aws は他の2つの Provider と比較して、インストールする際により多くの制御を行うこととパフォーマンスの改善がなされています。
ここでは provider-family-aws と provider-aws-rds Provider の両方をインストールします。
バージョンはこのブログの執筆時点の最新バージョンを適用します。下記、 provider-family-aws.yaml という名前のファイルを作成します。

---
# The AWS "family" Provider - manages the ProviderConfig for all other Providers in the same family.
# Does not have to be created explicitly, if not created explicitly it will be installed by the first Provider created
# in the family.
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: upbound-provider-family-aws
spec:
  package: xpkg.upbound.io/upbound/provider-family-aws:v0.38.0
  controllerConfigRef:
    name: upbound-provider-family-aws
---
# The AWS RDS Provider - just one of the many Providers in the AWS family.
# You can add as few or as many additional Providers in the same family as you wish.
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: upbound-provider-aws-rds
spec:
  package: xpkg.upbound.io/upbound/provider-aws-rds:v0.38.0
  controllerConfigRef:
    name: upbound-provider-family-aws
---
# The ControllerConfig applies settings to a Provider Pod.
# With family Providers each Provider is a unique Pod running in the cluster.
apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: upbound-provider-family-aws

ファイルをTanzu Application Platformクラスタに適用します。

kubectl apply -f provider-family-aws.yaml

kubectl describe providersとkubectl get providersでインストールされたプロバイダを確認します。
出力結果でINSTALLEDの値はTrueで、HEALTHYがTrueになることを確認します。このステータスになるまでは5分かかることがあります。

AWS APIへの認証方法はいくつか用意されています。ここではKubernetesシークレットを使った静的クレデンシャルの方法を試します。その他、RoleARN を利用する ServiceAccount などの認証方法が利用できます。
ではAWS用のKubernetesシークレットを作成していきます。
AWSキーペアファイルのテキストファイル(aws-credentials.txt)を用意します。

[default]
aws_access_key_id = <aws_access_key>
aws_secret_access_key = <aws_secret_key>

テストなどでAWS SDK での一時的セキュリティ認証情報の使用を行っている場合は下記のsession tokenも追加してください。

aws_session_token = <aws_session_token>

下記のコマンドでKubernetesクラスタ内にKubernetesシークレットオブジェクトを生成します。
注意:NamespaceはTAPで自動作成されている crossplane-system を指定

kubectl create secret \
generic aws-secret \
-n crossplane-system \
--from-file=creds=./aws-credentials.txt

インストールされたプロバイダにAWS認証情報をアタッチするためのProviderConfig Kubernetes設定ファイルを作成します。

cat << EOF | kubectl apply -f -
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-secret
      key: creds
EOF
cat <<bucket=$(echo "upbound-bucket-"$(head -n 4096 /dev/urandom | openssl sha1 | tail -c 10))

以上でプロバイダの設定は完了ですが、確認のためマネージド・リソースの作成が機能するかを確認します。
下記の例では、AWS S3 ストレージバケットを作成します。


bucket=$(echo "upbound-bucket-"$(head -n 4096 /dev/urandom | openssl sha1 | tail -c 10))

cat <<EOF | kubectl apply -f -
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: $bucket
spec:
  forProvider:
    region: us-east-1
  providerConfigRef:
    name: default
EOF

kubectl get buckets を使ってバケットの作成を確認します。

kubectl get buckets

EADYとSYNCEDの値がTrueになればS3のバケットが作成されたことを示しています。これは5分ほどかかるかもしれません。
実際のAWS画面をみてみるとしっかりと作成されています。

kubectl delete <バケット名> で同じ Bucket オブジェクトファイルを指定して、管理対象リソースを削除します。AWSコンソール上からも削除されているはずです。

2. CompositeResourceDefinition の作成

AWS Provider for CrossplaneがインストールされたのでここからAWS RDS サービスインスタンスを作成するためのカスタムリソース定義を作成していきます。
※Crossplaneで利用されるカスタムリソース定義は CompositeResourceDefinition (XRD)と呼ばれます。CustomResourceDefinition(CRD)に少し似ていますが、違うものです。
下記でXRDを作成してください。
xpostgresqlinstances.database.rds.example.org.xrd.yaml という名前のファイルを作成し、以下の内容をコピーします。

---
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.rds.example.org
spec:
  claimNames:
    kind: PostgreSQLInstance
    plural: postgresqlinstances
  connectionSecretKeys:
  - type
  - provider
  - host
  - port
  - database
  - username
  - password
  group: database.rds.example.org
  names:
    kind: XPostgreSQLInstance
    plural: xpostgresqlinstances
  versions:
  - name: v1alpha1
    referenceable: true
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              storageGB:
                type: integer
                default: 20
            type: object
        type: object
    served: true

これにより、アプリケーションチームがクレームを作成する際に、AWS RDS サービスインスタンスに適切なストレージ量を選択できるようになります。
アプリケーションチームに公開するパラメータは、ここに記載されるパラメーターでカスタマイズが可能です。ここでは spec.forProvider.allocatedStorage でストレージ容量を指定できるようにしています。

ファイルを TAP クラスタに適用します:

kubectl apply -f xpostgresqlinstances.database.rds.example.org.xrd.yaml

3. Compositionを作成する

xpostgresqlinstances.database.rds.example.org.composition.yamlという名前のファイルを作成し、以下の内容をコピーします。ここで マニュアル上はconnectionDetails.host.fromConnectionSecretKey の値に endpoint を指定していますが、Service Bindings 経由でサービスにアクセスする時に接続文字列が正しく生成されず動作しません。endpoint から address へ値を変更してください。これでService Bindings 経由でサービスにアクセスする時に接続文字列が正しく生成されます。

---
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  labels:
    provider: "aws"
    vpc: "default"
  name: xpostgresqlinstances.database.rds.example.org
spec:
  compositeTypeRef:
    apiVersion: database.rds.example.org/v1alpha1
    kind: XPostgreSQLInstance
  publishConnectionDetailsWithStoreConfigRef:
    name: default
  resources:
  - base:
      apiVersion: database.aws.crossplane.io/v1beta1
      kind: RDSInstance
      spec:
        forProvider:
          # NOTE: configure this section to your specific requirements
          dbInstanceClass: db.t2.micro
          engine: postgres
          dbName: postgres
          engineVersion: "12"
          masterUsername: masteruser
          publiclyAccessible: true                # <---- DANGER
          region: us-east-1
          skipFinalSnapshotBeforeDeletion: true
        writeConnectionSecretToRef:
          namespace: crossplane-system
    connectionDetails:
    - name: type
      value: postgresql
    - name: provider
      value: aws
    - name: database
      value: postgres
    - fromConnectionSecretKey: username
    - fromConnectionSecretKey: password
    - name: host
      fromConnectionSecretKey: address
    - fromConnectionSecretKey: port
    name: rdsinstance
    patches:
    - fromFieldPath: metadata.uid
      toFieldPath: spec.writeConnectionSecretToRef.name
      transforms:
      - string:
          fmt: '%s-postgresql'
          type: Format
        type: string
      type: FromCompositeFieldPath
    - fromFieldPath: spec.storageGB
      toFieldPath: spec.forProvider.allocatedStorage
      type: FromCompositeFieldPath

先ほど作成したaws provider for crossplane用のCRD(kind:XPostgreSQLInstance)がCompositeResourceを作成するときに参照するテンプレートがCompositionとなります。
実運用では、本番用、開発用など特定の要件に合わせて設定ができます。
なお、このサイトで設定できるパラメーターを確認することができます。

このファイルを TAP クラスタに適用します。

kubectl apply -f xpostgresqlinstances.database.rds.example.org.composition.yaml

4. 開発者がサービスを検出できるように設定

アプリケーション・チームがサービスを発見できるようにする:
rds.class.yamlという名前のファイルを作成し、以下の内容をコピーします。

---
apiVersion: services.apps.tanzu.vmware.com/v1alpha1
kind: ClusterInstanceClass
metadata:
  name: aws-rds-psql
spec:
  description:
    short: Amazon AWS RDS PostgreSQL
  provisioner:
    crossplane:
      compositeResourceDefinition: xpostgresqlinstances.database.rds.example.org

このファイルを TAP クラスタに適用します。

kubectl apply -f rds.class.yaml

5. RBAC を構成する

app-operator ロールを持つユーザにクラスからのクレームを許可するために、ロールベースのアクセス制御 (RBAC) を構成します。
app-operator-claim-aws-rds-psql.rbac.yamlという名前のファイルを作成し、以下の内容をコピーします。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: app-operator-claim-aws-rds-psql
  labels:
    apps.tanzu.vmware.com/aggregate-to-app-operator-cluster-access: "true"
rules:
- apiGroups:
  - "services.apps.tanzu.vmware.com"
  resources:
  - clusterinstanceclasses
  resourceNames:
  - aws-rds-psql
  verbs:
  - claim

このファイルを TAP クラスタに適用します。

kubectl apply -f app-operator-claim-aws-rds-psql.rbac.yaml

下記で作成した aws-rds-psql という名前の aws サービスクラスが確認できます。

tanzu service class list

6. 構成を確認する

tanzu service class get aws-rds-psql でアプリケーションチームが指定できるパラメーターが確認できます。今回のデモではDBのストレージ容量のみ指定できます。
下記のコマンドでAWS RDS サービスインスタンスのクレームを作成します。

tanzu service class-claim create rds-psql-1 --class aws-rds-psql -p storageGB=30 -n demo

下記のコマンドでclaim処理の状況を確認します。StatusのReadyがTrueになるまで待ちます。

tanzu services class-claims get rds-psql-1 --namespace demo

AWSコンソール上を確認するとRDSのリソースが自動的に作成されている状況が確認できます。ステータスが作成中 -> バックアップ中 -> 利用可能になります。

7. 動的サービスプロビジョニングで作成したAWS RDS サービスでアプリをデプロイ

では作成したRDSサービスを利用して最初のデモでダウンロードしたアプリケーションコードをそのまま使いRDSサービスにアタッチされたAPIアプリケーションをデプロイします。
あとは、クレーム要求したAWS RDS サービスを使って、このブログで最初に作成したBitnamiサービスの Tanzu Java Restful Web App と同じコードを使ってデプロイできます。
まず、先ほど作成したアプリケーションとサービスクレームを削除します。

tanzu apps workload delete api-with-db-demo
tanzu services class-claims delete demo-claim

Service Bindings 経由でサービスにアクセスするよう指示します。ワークロードデプロイ時の–service-refに指定する値は下記で取得ください。

tanzu services class-claims get rds-psql-1 --namespace demo

下記のコマンドでWorkload をデプロイします。

tanzu apps workload apply api-with-db-demo \
--app api-with-db-demo \
--local-path <Tanzu Java Restful Web App のテンプレートのローカルパス> \
--source-image <ソースイメージを保存するレポジトリURL> \
--type web \
--build-env BP_JVM_VERSION=11 \
--label app.kubernetes.io/part-of=customer-profile \
--service-ref dbmaster=services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:rds-psql-1 \
--annotation autoscaling.knative.dev/minScale=1 \
-y

APP_URL環境変数に入れてcurlコマンドを使ったREST API経由で顧客情報 DB にデータを登録します。

export APP_URL=<エンドポイントURL>
curl -kX POST -H 'Content-Type: application/json' $APP_URL/api/customer-profiles -d '{"firstName": "vmware", "lastName": "tanzu", "email": "[email protected]"}'

下記のREST API経由で登録した情報を確認します。

curl -kX GET $APP_URL/api/customer-profiles/

下記先ほど登録した情報がJSON形式で表示されれば成功です。念の為、実際にDBにデータが登録されているかをpsqlでRDS に接続しデータが登録されているかを確認してみます。


RDS 上に customer_profile テーブルが作成され、データが登録されていることも確認できました。
結果、開発者は最初のデモで利用したアプリケーションコードをそのまま利用し、TAP  へのワークロードデプロイ時に--service-ref の指定を変更するだけでパブリッククラウドのデータサービスを利用できるようになりました。

このようにCrossplaneの仕様を理解すれば組織、チームにあったデータサービス設計をオペレーター側で定義、開発者に見せたくない裏のリソース制御も事前に設定や裏ではネットワークリソースをチーム間で共有するような設定を行うことができます。開発者は用意されたサービスリストを使って開発者が必要なパラメーターのみを入力することでデータサービスが動的にプロビジョニングがされます。

Crossplane は AWS だけでなく Azure ( provider-azure CRDs )  や GCP ( provider-gcp CRDs ) のサービスも利用できるようになっています。基本は Crossplane の仕様を理解すると複数のクラウドのサービスを利用することができます。
※1つの TAP 上で AWS 、Azure 、および GCP の複数の Crossplane プロバイダを使うとクラスタパフォーマンスの低下が懸念されるため本番利用時はTAP の Run クラスタを分けるなど構成にご注意ください。

 

まとめ

以上、今回は TAP の強力なデータサービスの動的プロビジョニングの機能について紹介させていただきました。
アプリケーションを開発する上ではデータサービスを迅速に展開していくことを開発のアジリティをあげる上では大きな課題となります。
TAPで提供されるService Bindings、Services Toolkit、Crossplaneを利用した動的プロビジョニングは開発者にとってまさに必要とされる機能なのではないでしょうか?
引き続き TAP の様々な機能を VMware ブログにて紹介していきたいと思います。