IK.AM

@making's tech note


Tanzu Application PlatformでPre-ProvisionedなRabbitMQを使ってResourceClaim / ClassClaimを作る


⚠️ 本記事の内容はVMwareによってサポートされていません。 記事の内容で生じた問題については自己責任で対応し、 VMwareサポート窓口には問い合わせないでください

以下の記事ではBitnami Servicesを使ってRabbitMQのサービスインスタンスを動的にProvisionしました。

本記事では次の方を対象に、Pre-ProvisionedなRabbitMQを使ってResourceClaim / ClassClaimを作る方法を紹介します。

  • TAP 1.5より前のバージョンを使用しており、Bitnami Servicesを利用できない
  • Dev環境ではBitnami Servicesを使用するが、Prod環境ではProduction-gradeなRabbitMQサービスを使用したい

目次

Service Binding互換なRabbitMQのSecretを作成する

Bitnami ServicesではCrossplaneによる動的なProvisionを使用しますが、今回は事前に静的にProvisionしたサービスを使用します。 静的にProvisionしたサービスを利用するには接続情報をService Bindingに対応したSecretに格納すれば良いです。

事前に作るRabbitMQの作成方法は以下の2パターンを紹介します。

  • Cloud AMQPを使ったHosted ServiceなRabbitMQ
  • BitnamiのHelm Chartを使ったSelf HostedなRabbitMQ

本記事の内容を試す場合はどちらかを選んでください。

Cloud AMQPを使う場合

FREEプランのあるRabbitMQのManaged ServiceであるCloudAMQPを使ってみます。

アカウントを作成するか、GitHubまたはGoogleアカウントでログインしてください。

image

インスタンス作成画面でNameを入力します。Planは"Little Lemur (Free)"を選択してください。

image

RegionとData Centerは"Amazon Web Services"の"AP_NorthEast-1 (Tokyo)"を選択し、インスタンスを作成してください。

image

インスタンス一覧画面から作成したインスタンス名をクリックしてください。

image

インスタンスの接続情報が表示されます。

image

ℹ️ このRabbitMQインスタンスにローカル環境から接続したい場合は、"URL"の文字列をコピーして、application.propertiesに次のように設定してください。

spring.rabbitmq.addresses=amqps://fmzkajiy:2g6hsLdxgPBLtdaSm19C1byLc-chHIZM@cougar.rmq.cloudamqp.com/fmzkajiy

spring.rabbitmq.host, spring.rabbitmq.port, spring.rabbitmq.username, spring.rabbitmq.password, spring.rabbitmq.addressesを設定する方法もありますが、
spring.rabbitmq.addresses であれば1プロパティで済みます。また、TLSの設定(spring.rabbitmq.ssl.enabled=true)もamqps://...から自動で判断して設定されます。
詳しくは https://docs.spring.io/spring-boot/docs/current/reference/html/messaging.html#messaging.amqp を参照してください。


インスタンス一覧画面から"RabbitMQ Manager"をクリックするとRabbitMQ Managementにアクセスできます。

image

次にこのインスタンスに対するService Binding互換なSecretを作成します。

Spring Bootの場合はService Binding互換なSecretから spring-cloud-bindings が自動でアプリケーションのプロパティにマッピングします。

Spring Cloud Bindingの作法に合わせてSecretを作成すれば良いです。RabbitMQにアクセスするためのSecret形式は https://github.com/spring-cloud/spring-cloud-bindings#rabbitmq を見れば良いです。 spring.rabbitmq.addresses プロパティにマッピングされるのは addresses キーです。またtypeキーにrabbitmqを持つ必要があります。 spring.rabbitmq.addresses プロパティさえ設定されれば他のフィールドは不要です。

したがって次のようなSecretは作成すれば良いです。

cat <<'EOF' > hello-rabbitmq-svcbind.yaml
apiVersion: v1
kind: Secret
metadata:
  name: hello-rabbitmq-svcbind
  labels:
    services.apps.tanzu.vmware.com/class: rabbitmq-provisioned
type: servicebinding.io/rabbitmq
stringData:
  type: rabbitmq
  addresses: amqps://fmzkajiy:2g6hsLdxgPBLtdaSm19C1byLc-chHIZM@cougar.rmq.cloudamqp.com/fmzkajiy
EOF

kubectl apply -f hello-rabbitmq-svcbind.yaml -n demo

services.apps.tanzu.vmware.com/class labelはService Bindingには必須ではないですが、後に使用します。 また、Secret名に決まりはありません。ここでは次のBitnami Helm Chartで生成されるSecret名と同じ名前を使いました。

Bitnami Helm Chartを使ってRabbitMQをKubernetes上にデプロイする場合

次はBitnami Helm Chartを使ってRabbitMQをKubernetes上でSelf Hostする方法を紹介します。
Bitnami Servicesは名前の通り、BitnamiのHelm Chartを動的にプロビジョンする仕組みですが、ここでは同じBitnamiのHelm Chartを静的にプロビジョンします。

BitnamiのHelm ChartのいくつかはService Bindingに対応したSecretを生成できるようになっています (関連記事)。
RabbitMQはChartのバージョン11.10.0以降でService Bindingに対応しています。ここでは記事執筆時点の最新版11.15.2を使用します。

BitnamiのHelm ChartはOCIレジストリを使用するため、Helmは3.8以上を使用してください。本記事の内容は以下のHelmバージョンで試しました。

$ helm version --short
v3.12.0+gc9f554d

次のコマンドでRabbitMQをデプロイするためのKubernetesマニフェストを生成します。serviceBindings.enabled=trueを設定することでService Bindingに対応したSecretが生成されます。

helm template hello-rabbitmq oci://registry-1.docker.io/bitnamicharts/rabbitmq \
  -n demo \
  --set serviceBindings.enabled=true \
  --set persistence.size=1Gi \
  --set 'commonLabels.services\.apps\.tanzu\.vmware\.com/class=rabbitmq-provisioned' \
  --version 11.15.2 > hello-rabbitmq-bitnami.yaml

このマニフェストをapplyします。

kubectl apply -f hello-rabbitmq-bitnami.yaml
kubectl wait pod/hello-rabbitmq-0 --for=condition=ready --timeout=120s -n demo

次のようなリソースが作成されます。hello-rabbitmq-svcbindという名前のSecretがService Bindingに対応したものです。

$ kubectl get pod,sts,secret -n demo -l app.kubernetes.io/name=rabbitmq
NAME                   READY   STATUS    RESTARTS   AGE
pod/hello-rabbitmq-0   1/1     Running   0          2m

NAME                              READY   AGE
statefulset.apps/hello-rabbitmq   1/1     2m

NAME                            TYPE                         DATA   AGE
secret/hello-rabbitmq           Opaque                       2      2m
secret/hello-rabbitmq-config    Opaque                       1      2m
secret/hello-rabbitmq-svcbind   servicebinding.io/rabbitmq   7      2m

どのような内容のSecretが作成されたか次のコマンドで見てみます。

$ kubectl get secret -n demo hello-rabbitmq-svcbind -ojson | jq '.data | map_values(@base64d)'
{
  "host": "hello-rabbitmq",
  "password": "X4WGwerjDhYgTpnO",
  "port": "5672",
  "provider": "bitnami",
  "type": "rabbitmq",
  "uri": "amqp://user:X4WGwerjDhYgTpnO@hello-rabbitmq:5672",
  "username": "user"
}

https://github.com/spring-cloud/spring-cloud-bindings#rabbitmq を見ると以下のマッピングが行われることがわかります。

  • host => spring.rabbitmq.host
  • port => spring.rabbitmq.port
  • username => spring.rabbitmq.username
  • password => spring.rabbitmq.password

RabbitMQ Managementにアクセスしたい場合は、次のコマンドでport forwardを行い

kubectl port-forward -n demo svc/hello-rabbitmq 15672:15672

http://localhost:15672 にアクセスしてください。

ClusterInstanceClassの作成

次にClusterInstanceClassを作成します。
ClusterInstanceClassはClasClaimを使用する場合は作成が必須ですが、ResourceClaimを使用する場合は必須ではありません。
ResourceClaimを使用する場合でも次に説明するtanzu service claimable list --class ...コマンドでClaim可能なリソース(Secret)一覧を表示したい場合には必要です。

以下のドキュメントを参考にしています。

https://docs.vmware.com/en/Services-Toolkit-for-VMware-Tanzu-Application-Platform/0.9/svc-tlk/usecases-introducing_different_service_implementations_in_different_environments.html

次のコマンドを実行してください。

cat <<EOF > rabbitmq-provisioned.yaml
apiVersion: services.apps.tanzu.vmware.com/v1alpha1
kind: ClusterInstanceClass
metadata:
  name: rabbitmq-provisioned
spec:
  description:
    short: Pre-provisioned RabbitMQ Instances
  pool:
    kind: Secret
    labelSelector:
      matchLabels:
        services.apps.tanzu.vmware.com/class: rabbitmq-provisioned
    fieldSelector: type=servicebinding.io/rabbitmq
EOF

kubectl apply -f rabbitmq-provisioned.yaml

spec.poolを指定することでPre Provisionedなサービスを対象としています。Crossplaneによる動的Provisionなサービスを定義する場合は代わりにspec.provisioner.crossplaneを指定します。
今回はlabel services.apps.tanzu.vmware.com/class: rabbitmq-provisionedを持ち、Secret Typeがservicebinding.io/rabbitmqであるSecretをrabbitmq-provisionedクラスと定義しました。


tanzu services classes listコマンドを実行すると、rabbitmq-provisionedが一覧に含まれます。

$ tanzu services classes list
  NAME                  DESCRIPTION                         
  mysql-unmanaged       MySQL by Bitnami                    
  postgresql-unmanaged  PostgreSQL by Bitnami               
  rabbitmq-provisioned  Pre-provisioned RabbitMQ Instances  <<----
  rabbitmq-unmanaged    RabbitMQ by Bitnami                 
  redis-unmanaged       Redis by Bitnami  

ここまで作ったSecretがrabbitmq-provisionedクラスの条件を満たすか確認します。

Cloud AMQP用のSecretを作った場合は

$ kubectl get secret -n demo --show-labels hello-rabbitmq-svcbind
NAME                     TYPE                         DATA   AGE    LABELS
hello-rabbitmq-svcbind   servicebinding.io/rabbitmq   2      122m   services.apps.tanzu.vmware.com/class=rabbitmq-provisioned

Helm ChartでRabbitMQでインストールした場合は

$ kubectl get secret -n demo --show-labels hello-rabbitmq-svcbind
NAME                     TYPE                         DATA   AGE   LABELS
hello-rabbitmq-svcbind   servicebinding.io/rabbitmq   7      81m   app.kubernetes.io/instance=hello-rabbitmq,app.kubernetes.io/managed-by=Helm,app.kubernetes.io/name=rabbitmq,helm.sh/chart=rabbitmq-11.15.2,services.apps.tanzu.vmware.com/class=rabbitmq-provisioned

のように出力されます。TypeもLabelも条件を満たしていることがわかります。

Service Toolkitに対するRBACの設定

ここまでで作成したSecretを使用するためにClassClaimまたはResourceClaimで"Claim"(要求)するわけですが、tanzu service claimable list --class ...コマンドで対象のクラスでClaim可能なリソース(Secret)一覧を表示することができます。

このコマンドを実行し、次のようなエラーが出力される場合は、Service Toolkitに対するRBACの設定が不足しています。

$ tanzu service claimable list --class rabbitmq-provisioned -n demo
Error: secrets is forbidden: User "system:serviceaccount:services-toolkit:resource-claims-apiserver" cannot list resource "secrets" in API group "" at the cluster scope

次のコマンドでRBACの設定を行います。

cat <<EOF > stk-secret-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: stk-secret-reader
  labels:
    servicebinding.io/controller: "true"
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - list
  - watch
EOF

kubectl apply -f stk-secret-reader.yaml

今度は次のようにClaim可能なリソースが出力されるでしょう。

$ tanzu service claimable list --class rabbitmq-provisioned -n demo
  NAME                    NAMESPACE  KIND    APIVERSION  
  hello-rabbitmq-svcbind  demo       Secret  v1 

この作業を行わなくてもClassClaimまたはResourceClaimを作成すること自体は可能ですが、設定しておくと便利でしょう。

Claimの作成

SecretができたのでいよいよClaimを作成します。

ClaimにはResourceClaimとClassClaimの2種類あります。違いは次のドキュメントに記載されています。

https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.5/tap/services-toolkit-concepts-class-claim-vs-resource-claim.html

ResourceClaimは対象のリソースを特定して作成します。したがって静的にProvisionしたサービスにのみ利用可能です。 一方ClassClaimはリソースは特定せず、クラス名のみを指定します。クラスによって対象のリソースが静的(pool)または動的(provisioner)に確保するかを選択できます。

ClassClaimはTAP 1.4以降で利用可能です。TAP 1.4以降であれば、特定のリソース名まで指定したいという要件がない限りはResouceClaimよりもClassClaimを使用した方が、可搬性が高くて良いでしょう。

ResourceClaimの作成 (TAP 1.3以前の場合)

まずはResourceClaimを使用する方法を紹介します。TAP 1.3以前のバージョンを利用している場合は必然的にこちらの選択肢になります。

以下のドキュメントと基本的には同じです。
https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.5/tap/services-toolkit-tutorials-direct-secret-references.html

Claim可能なリソースに対して、次のコマンドでResourceClaimを作成します。

tanzu services resource-claim create hello-rabbitmq \
  --resource-name hello-rabbitmq-svcbind \
  --resource-kind Secret \
  --resource-api-version v1 \
  -n demo

作成したResourceClaimを次のコマンドで確認します。

$ tanzu services resource-claims get hello-rabbitmq --namespace demo
Name: hello-rabbitmq
Status: 
  Ready: True
Namespace: demo
Claim Reference: services.apps.tanzu.vmware.com/v1alpha1:ResourceClaim:hello-rabbitmq
Resource Reference: 
  Name: hello-rabbitmq-svcbind
  Namespace: demo
  Group: 
  Version: v1
  Kind: Secret

Claimされるリソースは排他的なので、ResourceClaimが作成されればClaim可能なリソースからhello-rabbitmq-svcbindが消えます。

$ tanzu service claimable list --class rabbitmq-provisioned -n demo
No claimable service instances found.

次の記事の--service-ref hello-rabbitmq=services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:hello-rabbitmqの代わりに、--service-ref hello-rabbitmq=services.apps.tanzu.vmware.com/v1alpha1:ResourceClaim:hello-rabbitmqが利用可能です。

Time Sourceのデプロイしましょう。

tanzu apps workload apply ticktock-time \
  --app ticktock-time \
  --maven-group org.springframework.cloud.stream.app \
  --maven-artifact time-source-rabbit \
  --maven-version 3.2.1 \
  --env spring.cloud.stream.bindings.output.destination=ticktock.time \
  --build-env BP_JVM_VERSION=17 \
  --type web \
  --annotation autoscaling.knative.dev/minScale=1 \
  --service-ref hello-rabbitmq=services.apps.tanzu.vmware.com/v1alpha1:ResourceClaim:hello-rabbitmq \
  -n demo \
  -y

次にLog Sinkをデプロイしましょう。

tanzu apps workload apply ticktock-log \
  --app ticktock-log \
  --maven-group org.springframework.cloud.stream.app \
  --maven-artifact log-sink-rabbit \
  --maven-version 3.2.1 \
  --env spring.cloud.stream.bindings.input.destination=ticktock.time \
  --env spring.cloud.stream.bindings.input.group=log \
  --build-env BP_JVM_VERSION=17 \
  --type web \
  --annotation autoscaling.knative.dev/minScale=1 \
  --service-ref hello-rabbitmq=services.apps.tanzu.vmware.com/v1alpha1:ResourceClaim:hello-rabbitmq \
  -n demo \
  -y

Log Sinkのログを確認して、次のように毎秒ログが出力されればOKです。

$ stern -n demo ticktock-log -c workload

...
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:40:59.927  INFO [log-sink,fa8876f82adf3286,87ce0e02f538588e] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:40:59
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:00.928  INFO [log-sink,110363283f063f53,f0b640a58c3cf707] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:00
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:01.929  INFO [log-sink,666e768f86e30515,b7394cd3f9dc1333] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:01
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:02.930  INFO [log-sink,9be0d2a7dcbdd989,14ab4f7fce911a93] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:02
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:03.932  INFO [log-sink,ab81f203d86eae6c,bff8f15675d707c7] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:03
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:04.933  INFO [log-sink,173d23e1e2dc4cf7,14a2949c6de641ca] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:04
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:05.934  INFO [log-sink,aed73d5f177983d6,fbe38b0583b40dc3] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:05
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:06.936  INFO [log-sink,82656e5e46203429,7b83b0d9af6ae7f9] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:06
...

確認ができたらWorkloadとResourceClaimを削除します。

kubectl delete workload -n demo --all
tanzu services resource-claims delete -n demo hello-rabbitmq

ResourceClaimを削除すればhello-rabbitmq-svcbindは再びClaim可能になります。

$ tanzu service claimable list --class rabbitmq-provisioned -n demo
  NAME                    NAMESPACE  KIND    APIVERSION  
  hello-rabbitmq-svcbind  demo       Secret  v1 

ClassClaimの作成 (TAP 1.4以降で利用可能)

次はClassClaimを作成します。作成方法はBitnami Servicesの時と同じで指定するクラス名が変わるだけです。
次のコマンドでClassClaimを作成します。

tanzu service class-claim create hello-rabbitmq --class rabbitmq-provisioned -n demo

作成したClassClaimを次のコマンドで確認します。

$ tanzu services class-claims get hello-rabbitmq --namespace demo
Name: hello-rabbitmq
Namespace: demo
Claim Reference: services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:hello-rabbitmq
Class Reference: 
  Name: rabbitmq-provisioned
Parameters: None
Status: 
  Ready: True
  Claimed Resource: 
    Name: hello-rabbitmq-svcbind
    Namespace: demo
    Group: 
    Version: v1
    Kind: Secret

Claimされるリソースは排他的なので、ResourceClaimが作成されればClaim可能なリソースからhello-rabbitmq-svcbindが消えます。

$ tanzu service claimable list --class rabbitmq-provisioned -n demo
No claimable service instances found.

ClassClaimを作成以降は、Bitnami Servicesを使う場合と全く同じです。

Time Sourceのデプロイしましょう。

tanzu apps workload apply ticktock-time \
  --app ticktock-time \
  --maven-group org.springframework.cloud.stream.app \
  --maven-artifact time-source-rabbit \
  --maven-version 3.2.1 \
  --env spring.cloud.stream.bindings.output.destination=ticktock.time \
  --build-env BP_JVM_VERSION=17 \
  --type web \
  --annotation autoscaling.knative.dev/minScale=1 \
  --service-ref hello-rabbitmq=services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:hello-rabbitmq \
  -n demo \
  -y

次にLog Sinkをデプロイしましょう。

tanzu apps workload apply ticktock-log \
  --app ticktock-log \
  --maven-group org.springframework.cloud.stream.app \
  --maven-artifact log-sink-rabbit \
  --maven-version 3.2.1 \
  --env spring.cloud.stream.bindings.input.destination=ticktock.time \
  --env spring.cloud.stream.bindings.input.group=log \
  --build-env BP_JVM_VERSION=17 \
  --type web \
  --annotation autoscaling.knative.dev/minScale=1 \
  --service-ref hello-rabbitmq=services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:hello-rabbitmq \
  -n demo \
  -y

Log Sinkのログを確認して、次のように毎秒ログが出力されればOKです。

$ stern -n demo ticktock-log -c workload

...
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:40:59.927  INFO [log-sink,fa8876f82adf3286,87ce0e02f538588e] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:40:59
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:00.928  INFO [log-sink,110363283f063f53,f0b640a58c3cf707] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:00
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:01.929  INFO [log-sink,666e768f86e30515,b7394cd3f9dc1333] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:01
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:02.930  INFO [log-sink,9be0d2a7dcbdd989,14ab4f7fce911a93] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:02
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:03.932  INFO [log-sink,ab81f203d86eae6c,bff8f15675d707c7] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:03
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:04.933  INFO [log-sink,173d23e1e2dc4cf7,14a2949c6de641ca] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:04
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:05.934  INFO [log-sink,aed73d5f177983d6,fbe38b0583b40dc3] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:05
ticktock-log-00001-deployment-77665794b6-wr44c workload 2023-05-19 05:41:06.936  INFO [log-sink,82656e5e46203429,7b83b0d9af6ae7f9] 1 --- [tock.time.log-1] log-sink                                 : 05/19/23 05:41:06
...

確認ができたらWorkloadとClassClaimを削除します。

kubectl delete workload -n demo --all
tanzu services class-claims delete -n demo hello-rabbitmq

ClassClaimの場合は、Bitnami Servicesを使ってもPre-Provisionedなサービスを使ってもWorkloadの定義は全く同じです。 このWorkloadから作成されるマニフェストは当然同じであるため、そのまま異なるRunクラスタで利用可能です。

ですので、Dev環境ではBitmani Servicesを使いつつ、同じ定義のままProdではManaged Serviceを使うという使い分けが可能になります。 これがClassClaimを使用するメリットになります。


✒️️ Edit  ⏰ History  🗑 Delete