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

Tanzu Application Platform でサービス連携の「面倒なこと」を「約束」で解決:Service Bindings

この記事では、VMware Tanzu Application Platform  (以降TAP)の Service Bindings について紹介します。

ほとんどのアプリケーションはデータベースなどの「サービス」と連携することが当たり前です。この「当たり前」のことがらには実は多くの課題が潜んでいます。TAPではこの課題を解決する一つの手段に Service Bindings 機能を提供し、より開発者に「面倒なこと」は意識させないようにしています。

サービスを連携する上での「面倒なこと」とは?

TAP が目指しているものとは、すこしでも開発者に面倒なことを減らしていく、App-Aware Platformになることです。その一つがここで、紹介する Service Bindingsです。

TAP の Service Bindings の恩恵を理解するためにはまず、サービスと連携する「面倒なこと」について再認識したいと思います。Kubernetes 上で動くアプリケーションがデータベースに接続することを想像してみてください。この際、データベースにアクセスするためのURLだけでなく、認証のユーザ名やパスワードが必要です。

Kubernetesが登場してしばらくたちますが、一般的にアプリケーションに認証情報を伝達するには、以下の手段が使われていました。

  • 認証情報などをKubernetes の Secret として登録する
  • その Secret をアプリケーションの環境変数で参照する

図示すると以下のようなイメージです。Application を視点にすれば、 MY_DB_ USERNAME と MY_DB_PASSWORD が Kubernetes により環境変数として渡されます。アプリケーションとしては、その環境変数をベースに開発を行えばよいです。Kubernetes の機能により、db-secrets とよばれる Secret 内に保管された認証情報である DB_USER (foo) , DB_PASSWORD (bar) にマッピングされます。

このやり方ですが、図示してわかるとおり複雑であり、これがすでに 「面倒なこと」です。これらの仕組みを理解していないとプラットフォームを使いこなせないことにつながります。開発者が開発にのみ集中するができなくなるひとつの要因となりえます。

もう一つの「面倒なこと」がアプリケーション側に標準化されていない変数を使ってアクセスしないといけないことです。上の例でもそうですが、 MY_DB_PASSWORD など、アプリケーションが最終的に読み込むべき値によってコーディングを変えないといけません。さらには USER もしくは USERNAME 、PASS もしくは PASSWORD といったように設定した人によって微妙に異なるケースがあります。「全然つながらないと思ったら、 MY_DB_PASS 変数をまちがってつかっていました・・・」という会話は Kubernetes 開発でよく聞く話だと思います。

開発者としてほしいのは「このDBにつなぎたい」という要件のみです。ただし現状のKuberenetesだとそれを実現するための、セットアップや前提知識が多すぎます。上の絵のような構成などを気にすることなく、プラットフォームが自動的にサービスの認証情報を提供してくれる仕組みが望ましいです。

サービス連携に「約束」を:Service Bindings

TAP ではこのサービス連携の「面倒なこと」の原因は “Kubernetes には自由がありすぎる” と考えました。そこで解決方法として考えたのが、「約束」をつくっていくアプローチです。
そのアプローチの集大成、それが OSSとして立ち上げた Service Bindings です。このOSSは VMware だけでなく、IBM や Redhat なども協力して開発しています。そして TAP はこの Service Bindings を商用利用を想定した数少ない製品です。

https://servicebinding.io/

さて、この Service Bindings ですが、極端にいってしまうとおこなっていることは、たった一つだけです。

  • 認証情報をコンテナ内の決められたファイルシステムにマウントする

「え、それだけ?」と思うかもしれませんが、もうすこし解剖していきましょう。ここでいう「認証情報」として登録可能なものが執筆時点は以下の二つです。

これらについては、後述のデモでより深くみていきますが、図示すると以下のイメージです。
(なお、認証情報を「ファイルシステムとしてマウント」に違和感を覚えるかもしれませんが、この決定の背景は Service Bindings を定義している RFC でかたられています。)

 

 

さて、繰り返しですが、 Service Bindings はこれらの認証情報をファイルシステムにマウントするだけです。アプリケーションは、どうやってそれらを読み取るのでしょうか?ここで重要なのが、Service Bindings により、認証情報がファイルステムにマウントされているという「約束」ができたことです。アプリケーションはこの「約束」をベースに2つのアプローチでこれらを読み取ることができます。

  1. 自前のライブラリを開発して、認証情報にアクセス
  2. コミュニティが開発しているライブラリをもとに、認証情報を透過的にアクセス

Service Bindings の機能がより世の中に浸透していく上で不可欠なのが、この 2. にアプローチに賛同して、Sevice Bindingsに対応したコミュニティライブラリが数多く誕生することです。コミュニティが成長することによって、決められたルールをもとに、認証情報が配置され、変数名の統一化が行われていきます。このコミュニティのライブラリはすでに Spring Cloud Bindings , Quarkus を筆頭に日々増えています。いずれにせよ、アプリケーション開発はこのライブラリが定義する「約束」さえ守れば、プラットフォームを意識することなく、認証情報の引き渡しができます。これによって「面倒なこと」であったサービスとの連携が、プラットフォームによって透過的に認証情報を引き渡すことができます。

具体例でみる Service Bindings + Spring Cloud Bindings

さて、上の説明だけだといまいちピンとわからないと思うので、実際の TAP を使いこの Service Bindings + Spring Cloud Bindings がどのように動くのか見てみたいと思います。なお、TAP で Spring のアプリケーションをデプロイすると自動的に Spring Cloud Bindings がインストール(厳密には Packeto Buildspack によってビルド時に構成される)されるため、すぐに利用できる状態になっています。

アプリ準備

この回で紹介したアプリケーションをもとに解説をします。このアプリケーションは以下の特徴をもっています。

  • シンプルなREST APIの提供
  • 端末にて起動
  • インメモリDBを起動 < ここが重要

JDK 8+ がインストールされた端末で以下を実行します。(初めて起動する場合はすこし時間がかかります。)

git clone https://github.com/mhoshi-vm/appaccelerator-demo
cd appaccelerator-demo
./mvnw spring-boot:run

起動が完了したら、以下のコマンドを複数回実行して起動が問題ないことを確認してください。

curl -X PUT "localhost:8080/create?name=aaa"
curl "localhost:8080"

今回のデモでは、このアプリケーションを一切編集せずに Service Bindings の機能のみで、インメモリDBから外部DBへの切り替え方法を以下2つ紹介します。

  1. Tanzu MySQL を使った Provisioned Service 経由でサービスにアクセス
  2. Amazon RDS 上の DB を Direct Secret References 経由でサービスにアクセス

Tanzu MySQL を使った Service Binding Specs 経由でサービスにアクセス

このデモを実行するには以下の前提が必要です。

Tanzu MySQL Operatorのインストールが完了したら、以下の Kubernetes の Yaml を適用し TAP と同一クラスタに MySQL のインスタンスを作成します。

apiVersion: with.sql.tanzu.vmware.com/v1
kind: MySQL
metadata:
  name: mysql-sample
spec:
  resources:
    mysql:
      requests:
        memory: 1Gi
  storageSize: 1Gi

適用が完了すると以下のように、MySQLのインスタンスが作成されます。

では、これを TAP でデプロイするアプリケーションにつなげてみたいと思います。実行するコマンドは以下です。(コマンドは appaccelerator-demo を展開したディレクトリーで実行し、かつ source-image はセットアップ環境ごとに違うので変更してください。)

tanzu apps workload create sb-demo \
  --local-path ./ \
  --app sb-demo \
  --source-image <repository>/<project>/sb-demo-source \
  --type web \
  --service-ref="db-secret=with.sql.tanzu.vmware.com/v1:MySQL:mysql-sample"

このなかで重要なのが、–service-ref であり、前の手順で作成した MySQL リソースを”<任意の名前>=<apiVersion>:<kind>:<metadata.name>”のフォーマットで指定しています。こうすることで、Service Bindings によって二つのことが行われます。

  1. MySQLリソースに紐づいている認証情報を自動で見つけ出す
  2. その認証情報を指定のファイルシステムにマウントする

さてデプロイが完了したら、以下のコマンドを実行して参照するべきURLを取得します。

$ kubectl get ksvc sb-demo 
NAME   URL            LATESTCREATED LATESTREADY  READY REASON 
sb-demo http://sb-demo.<fqdn> sb-demo-00004 sb-demo-00004 True

この際 http://sb-demo.<fqdb> は環境ごとに異なりますが、以下のコマンドを数回実行します。

curl -X PUT "http://sb-demo.<fqdb>/create?name=aaa" 
curl "http://sb-demo.<fqdb>:8080"

完了したら、以下のコマンドを実行してみましょう。(Knativeの機能によりPod が 0スケールしている場合は、もう一度HTTPリクエストを実行してください。)

kubectl exec -it sb-demo-<pod名> -- ls -lR /bindings/

ただしく設定されていれば、以下のように表示されるはずです。Service Bindingsの機能により /bindings/ というディレクトリ以下に認証情報が配置されている状態です。

Service Bindings の仕事はここまでであり、ここからはコミュニティライブラリである Spring Cloud Bindings の仕事です。このライブラリはここのファイルシステムを参照してそれをアプリケーションに参照できるようにしています。結果として、この認証情報だけで、ソースコードを変更することなく外部データベースへのアクセスが可能となります。

実際に、アプリデプロイ後に MySQL をみると、データがデータベースにあることが確認できます。

このデモにある通り、Provisioned Serviceを使えば、「このDBをこのアプリにつなげてください」というレベルの宣言でアプリケーションとサービスと接続ができることがわかります。

Amazon RDS 上の DB を Direct Secret References 経由でサービスにアクセス

次に Tanzu MySQL ではない、外部の MySQL ともつなげてみたいと思います。このデモでは外部のデータベースとして Amazon RDS を使います。環境ごとにセットアップは異なりますので、詳細なセットアップは割愛します。

下のYamlを環境に定義します。Direct Secret Referenceの場合、Provisioned Serviceと異なり Secret リソースを定義する必要があります。Service Bindingsとしてはここで定義するものはなんでもいいですが、Spring Cloud Bindings が読み取れるよう Type : MySQL RDBMS の規約にしたがって記載をします。この規約さえ守れば、これ以降のアプリケーションのマッピングが簡略化されるのが特徴です。

apiVersion: v1
kind: Secret
metadata:
  name: production-db-secret
type: servicebinding.io/mysql
stringData:
  type: mysql
  host: XXXXXXX
  port: "3306"
  username: admin
  password: XXXXXX
  database: demo

以下のコマンドで TAP にデプロイします。コマンドは appaccelerator-demo を展開したディレクトリーで実行し、かつ source-image はセットアップ環境ごとに違うので変更してください。

tanzu apps workload create sb-demo \
  --local-path ./ \
  --app sb-demo \
  --source-image <repository>/<project>/sb-demo-source \
  --type web \
  --service-ref="db-secret=v1:Secret:production-db-secret"

ここでも重要なのが、service-ref であり、これが、前の手順で作成した Secret のパスを指定しています。Provisioned Service のときとは異なり、自動で認証情報を見つけ出すこと機能がなくなりますが、指定された Secret をファイルシステムへとマウントしていきます。

デプロイが完了したら、同じく以下のコマンドを実行して参照するべきURLを取得します。

$ kubectl get ksvc sb-demo 
NAME   URL            LATESTCREATED LATESTREADY  READY REASON 
sb-demo http://sb-demo.<fqdn> sb-demo-00004 sb-demo-00004 True

この際 http://sb-demo.<fqdb> は環境ごとに異なりますが、以下のコマンドを数回実行します。

curl -X PUT "http://sb-demo.<fqdb>/create?name=aaa" 
curl "http://sb-demo.<fqdb>:8080"

Provisioned Serviceと同じくコンテナの中をみてみましょう。ただしく設定されていれば、以下のように表示されるはずです。

Provisioned Service だろうと、Direct Secret Reference だろうと Service Bindings  によって、指定したファイルシステムに認証情報が配置されていることがわかります。

では次に(RDSなどの)コンソールをみてみましょう。すると今回開発したアプリケーションがアクセスがあることがわかり、インメモリDBではなく、MySQLを利用していることがわかります。Spring Cloud Bindings により /bindings/db-secret が参照されアプリケーションに引き渡されたことでアクセスができています。

 

以上二つの Service Bindings + Spring Cloud Bindings のデモでした。

ポイントが Provisioned Service と Direct Secret Reference の違いとしては、Provisioned Serviceの場合よりSecret の内容を隠蔽することができました。サービスの抽象化としては Provisioned Service のほうが高いです。しかし Direct Secret Reference だろうと、規約さえまもれば、最終的に同じコードでサービスにアクセスできました。

結果として、全く同じコードのまま以下の環境でつかうことできつつ、プラットフォームによってサービスの連携がされることが確認できました。

  • 端末
  • Kubernetes上の MySQL インスタンス
  • Amazon上のRDS

Kubernetes というプラットフォームを利用しつつ、開発者にプラットフォームを意識させない工夫、その一つが Service Bindings と実感できると思います。

まとめ

この記事では、TAP のサービス連携に役立つ Service Bindings を紹介しました。VMware Blogs では TAP の最新機能を紹介していきます。