以下の記事ではBitnami Servicesを使ってRabbitMQのサービスインスタンスを動的にProvisionしました。
- Tanzu Application PlatformのBitnami Servicesを使ってSpring Cloud Streamのアプリをデプロイする
- Tanzu Application PlatformにSpring Cloud Data FlowのPre-packagedなStreamアプリをデプロイする
本記事では次の方を対象に、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アカウントでログインしてください。
インスタンス作成画面でNameを入力します。Planは"Little Lemur (Free)"を選択してください。
RegionとData Centerは"Amazon Web Services"の"AP_NorthEast-1 (Tokyo)"を選択し、インスタンスを作成してください。
インスタンス一覧画面から作成したインスタンス名をクリックしてください。
インスタンスの接続情報が表示されます。
ℹ️ この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にアクセスできます。
次にこのインスタンスに対する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)一覧を表示したい場合には必要です。
以下のドキュメントを参考にしています。
次のコマンドを実行してください。
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種類あります。違いは次のドキュメントに記載されています。
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
が利用可能です。
- Tanzu Application PlatformのBitnami Servicesを使ってSpring Cloud Streamのアプリをデプロイする
- Tanzu Application PlatformにSpring Cloud Data FlowのPre-packagedなStreamアプリをデプロイする
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を使用するメリットになります。