Tanzu Application Platform にサンプルアプリをデプロイしてみる

[Tanzu Application Platform](https://tanzu.vmware.com/jp/application-platform) (TAP) は、VMware Tanzu の製品群の中でも、アプリ開発者寄りの機能を提供するソリューションで、あらゆる Kubernetes 上で、より優れた開発者体験を提供するための製品になります。

https://tanzu.vmware.com/jp/application-platform

なかなか豊富な機能を持っているので、ズバッと一言で表現するのが難しいのですが、Kubernetes の上で PaaS (Platform as a Service) のような機能を提供する製品と言うと分かりやすい方も多いと思います。
しかし、TAP は、もっと広い範囲でより優れた開発者体験を提供することを目指しており、最近の流行り言葉を使うと、プラットフォームエンジニアリングの文脈で、内部開発者プラットホーム (Internal Developer Platform) や、内部開発者ポータル (Internal Developer Portal) を実現できる製品です。

また、TAP は、2022年01月に TAP v1.0 がリリースされて以降、比較的早いサイクルで進化を続けており、2023年4月中旬の執筆時点では、TAP v1.4.2 が最新版になっています。
(が、ノロノロと記事を書いているうちに、v1.5 が GA しました...)

https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.4/tap/release-notes.html

今回はせっかくなので、この TAP v1.4 を使ったハンズオンとして、[VMware Tanzu の GitHub](https://github.com/vmware-tanzu) で公開されているサンプルアプリを、いくつかピックアップして、デプロイしてみたいと思います。

https://github.com/vmware-tanzu/application-accelerator-samples

「TAP をとりあえずインストールしてみたけど、実際にどう使えば良いのか、題材となるアプリが無くて、どうすれば良いの？」という方が、もしいらっしゃれば、参考にしていただければと思います。


# 前提

試している環境、および操作端末 (クライアント) 環境は、下記の通りです。

* TAP v1.4 を構築済み。
* (そこまで依存は無いと思っていますが) クライアントとしては Ubuntu 22.04 LTS を利用。
* kubectl コマンドがインストール済み。
* Git コマンドがインストール済み。


# サンプルアプリについて

はじめに、VMware Tanzu の GitHub で公開されているサンプルアプリ集として、どんなものがあるのか、簡単に解説したいと思います。

まず、実際にリポジトリにアクセスいただくと、何やらずらっとディレクトリが並んでいることが確認できると思います。

https://github.com/vmware-tanzu/application-accelerator-samples

いくつかドキュメントであったり、リポジトリ管理のためのファイルであったりしますが、おおよそ一つ一つがサンプルアプリのソースコードになり、TAP でデプロイするアプリの 1単位に相当する構成になっています。
今回は、この中から、シンプルなものを選んで、いくつかのパターンで、デプロイしてみたいと思います。


# 手順

## パターン共通: 事前準備

TAP では、Developer Namespace と呼ばれる実際にアプリをデプロイする Namespace を準備しておく必要があります。

https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.4/tap/set-up-namespaces.html

以降の手順では、`demo` という Namespace に対して、アプリをデプロイしていくので、この名前で Developer Namespace を作成し、準備していきます。

まず、TAP では、Tanzu Build Service (TBS) を利用して、ソースコードからコンテナイメージを作成します。そのため、TBS が作成したコンテナイメージを格納するイメージレジストリのログイン情報を Secret として作成し、全ての Namespace に対して公開します。

```sh
tanzu secret registry add tbs-registry-credentials --server REGISTRY-SERVER --username REGISTRY-USERNAME --password REGISTRY-PASSWORD --export-to-all-namespaces --yes --namespace tap-install
```

これによって、`tap-install` という Namespace に `tbs-registry-credentials` という名前で、コンテナレジストリへのログイン情報が格納されました。

また、もし、全ての Namespace に対して公開するのは、セキュリティ観点で宜しくないという場合には、特定の Namespace のみに公開することも可能です。

```sh
tanzu secret registry add tbs-registry-credentials --server REGISTRY-SERVER --username REGISTRY-USERNAME --password REGISTRY-PASSWORD --yes --namespace YOUR-NEW-DEVELOPER-NAMESPACE
```

次に、実際にアプリをデプロイする先の Namepsace を `demo` という名前で作成します。

```sh
kubectl create namespace demo
```

そして、この Namespace に対して、下記のようなコマンドで Label を設定すると、自動的に必要な設定、先ほどのコンテナレジストリへのログイン情報等が、Namespace に反映されます。なので、このラベル付けによって、狙った Namespace を、TAP の Developer Namespace 化するイメージです。

```sh
kubectl label namespaces demo apps.tanzu.vmware.com/tap-ns=""
```

実際に、反映された設定内容を確認するには、下記のコマンドを使って確認できます。

```sh
kubectl get secrets,serviceaccount,rolebinding,pods,workload,configmap -n demo
```

出力例:

```text
NAME                            TYPE                             DATA   AGE
secret/registries-credentials   kubernetes.io/dockerconfigjson   1      29s

NAME                     SECRETS   AGE
serviceaccount/default   1         61s

NAME                                                               ROLE                      AGE
rolebinding.rbac.authorization.k8s.io/default-permit-deliverable   ClusterRole/deliverable   29s
rolebinding.rbac.authorization.k8s.io/default-permit-workload      ClusterRole/workload      29s

NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      61s
```

これによって、`demo` という Namespace を TAP の Developer Namespace として扱えるようになったので、この Namespace に対して、サンプルアプリをデプロイしていきたいと思います。


## パターン①: Git リポジトリ上の Java アプリをコマンドでデプロイ

まずは、Tanzu CLI コマンドを使って、Git リポジトリ上で管理されている Java アプリを、TAP にデプロイしてみます。

### デプロイ方法

具体的なコマンドとして、下記の通りです。

```sh
tanzu apps workload create tanzu-java-web-app \
    --git-repo https://github.com/vmware-tanzu/application-accelerator-samples \
    --git-branch main \
    --sub-path tanzu-java-web-app \
    --type web \
    --label apps.tanzu.vmware.com/has-tests=true \
    --label app.kubernetes.io/part-of=tanzu-java-web-app \
    --yes \
    --namespace demo
```

指定しているオプションの簡単な説明をすると、↓こんな感じです。

* `--git-repo`
    * 名前の通りですが、アプリが管理されている Git リポジトリの URL です。
* `--git-branch`
    * こちらも名前の通り、どの Git ブランチのソースコードを対象にするのか指定します。
* `--sub-path`
    * このオプションは、シーンによっては、若干特殊なオプションになります。
    * 今回利用しているサンプルアプリ集には、ディレクトリ毎に、アプリが格納されているので、どのディレクトリのアプリを対象にするのかを指定しています。
    * もし、指定したリポジトリには、1つしかアプリが格納されておらず、直下がアプリのルートであれば、このオプション指定は不要です。
* `--type`
    * このアプリが、一般にどんな部類のアプリか？を指定します。
    * TAP v1.4 では、`web`, `server`, `worker` の 3タイプが用意されています。
        * [Workload types - VMware Tanzu Application Platform Product Documentation](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.4/tap/workloads-workload-types.html)
    * 今回は、HTTP ベースのアプリであり、リクエストの度に立ち上げるステートレスなアプリであるため、`web` が指定されています。
* `--label`
    * 名前の通り、Kubernetes の Label 機能を利用して、細やかな設定を行えるオプションになります。
    * さまざまなラベルオプションがあるため、解説は仕切れないのですが、名前の通り、「テストコードが同梱されているのか？」であったり、「もっと大きな括りで、なんらかのアプリの一部なのか？」を指定しています。
* `--yes`
    * このオプションを省略すると、後述の実行のタイミングで、「こんな感じのマニフェスト相当の実行になるけど良い？」という対話をスキップすることができます。
* `--namespace`
    * こちらも名前の通りですが、どの Namespace にアプリをデプロイするのか指定しています。
    * ここで指定する Namespace は、前述の Developer Namespace の設定が完了している必要がありますので、ご注意ください。

実際に、実行すると、↓こんな感じの出力となります。

```sh
$ tanzu apps workload create tanzu-java-web-app \
    --git-repo https://github.com/vmware-tanzu/application-accelerator-samples \
    --git-branch main \
    --sub-path tanzu-java-web-app \
    --type web \
    --label apps.tanzu.vmware.com/has-tests=true \
    --label app.kubernetes.io/part-of=tanzu-java-web-app \
    --yes \
    --namespace demo
🔎 Create workload:
      1 + |---
      2 + |apiVersion: carto.run/v1alpha1
      3 + |kind: Workload
      4 + |metadata:
      5 + |  labels:
      6 + |    app.kubernetes.io/part-of: tanzu-java-web-app
      7 + |    apps.tanzu.vmware.com/has-tests: "true"
      8 + |    apps.tanzu.vmware.com/workload-type: web
      9 + |  name: tanzu-java-web-app
     10 + |  namespace: demo
     11 + |spec:
     12 + |  source:
     13 + |    git:
     14 + |      ref:
     15 + |        branch: main
     16 + |      url: https://github.com/vmware-tanzu/application-accelerator-samples
     17 + |    subPath: tanzu-java-web-app
👍 Created workload "tanzu-java-web-app"

To see logs:   "tanzu apps workload tail tanzu-java-web-app --namespace demo --timestamp --since 1h"
To get status: "tanzu apps workload get tanzu-java-web-app --namespace demo"
```

このコマンドを契機に、TAP が Git リポジトリからソースコードを取得して〜、コンテナイメージをビルドして〜と、一連の処理が実行されますので、上記の出力の通り、ログを見ながらアプリがデプロイされるのを待機します。

デプロイまで完了すると、ステータス確認コマンドに対して、↓こんな感じの出力が得られます。

```sh
$ tanzu apps workload get tanzu-java-web-app --namespace demo
📡 Overview
   name:        tanzu-java-web-app
   type:        web
   namespace:   demo

💾 Source
   type:       git
   url:        https://github.com/vmware-tanzu/application-accelerator-samples
   sub-path:   tanzu-java-web-app
   branch:     main

   ...(中略)...

🛶 Pods
   NAME                                         READY   STATUS      RESTARTS   AGE
   tanzu-java-web-app-build-1-build-pod         0/1     Completed   0          9m29s
   tanzu-java-web-app-config-writer-jqdtz-pod   0/1     Completed   0          4m17s

🚢 Knative Services
   NAME                 READY   URL
   tanzu-java-web-app   Ready   https://tanzu-java-web-app.demo.tap.yusukei.test

To see logs: "tanzu apps workload tail tanzu-java-web-app --namespace demo --timestamp --since 1h"
```

Knative Services の部分で、`https://<アプリ名>.<Namespace名>.<Domain名>` の形式で、URL が発行されているのが分かります。

### ブラウザからのアクセス方法

実際に、ブラウザからアクセスしたい...のですが、Ingress によるアクセスになるので、名前解決された際に、Envoy が待ち受けている IP アドレスが返されるようにしておく必要があります。
具体的には、↓こんな感じで調べられる Envoy が待ち受けている IP アドレスを変えてしてくれる DNS レコード、またはラボ環境なんかであれば、`/etc/hosts` に該当のレコードを書き込んでおく必要があります。

```sh
kubectl get svc envoy -n tanzu-system-ingress
```

出力例:

```text
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
envoy   LoadBalancer   100.66.40.155   192.168.20.5   80:30594/TCP,443:31168/TCP   57d
```

私は、ワイルドカード指定で `*.tap.yusukei.test` に対して、`192.168.20.5` が返るような DNS レコードをラボの DNS サーバーに書いてあります。

ここまでの準備が整っていれば、実際にブラウザからアクセスしてみると、(ちゃんと証明書を設定していない場合はオレオレ証明書で) HTTPS 化されたサイトとして、サンプルアプリにアクセスできると思います。

### 後片付け方法

後片付けとして、デプロイしたアプリを削除する場合には、下記のコマンドを実行します。

```sh
tanzu apps workload delete tanzu-java-web-app --namespace demo
```

コマンドのミスも怖いので `--yes` オプションは指定していませんが、指定していれば、↓こんな感じの対話はスキップできます。

```sh
$ tanzu apps workload delete tanzu-java-web-app --namespace demo
❓ Really delete the workload "tanzu-java-web-app"? [yN]: y
👍 Deleted workload "tanzu-java-web-app"
```


## パターン②: Git リポジトリ上の Java アプリを構成ファイルからデプロイ

次は、ちょっと Kubernetes っぽくというか、GitOps っぽくというか、構成ファイル (マニフェスト) を使用して、アプリをデプロイしてみます。

用意する構成ファイルとしては、↓こんな感じになります。

```yaml:workload.yaml
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
  name: tanzu-java-web-app
  labels:
    apps.tanzu.vmware.com/workload-type: web
    apps.tanzu.vmware.com/has-tests: "true"
    apps.tanzu.vmware.com/auto-configure-actuators: "true"
    app.kubernetes.io/part-of: tanzu-java-web-app
spec:
  build:
    env:
      - name: BP_JVM_VERSION
        value: "11"
  params:
  - name: annotations
    value:
      autoscaling.knative.dev/minScale: "1"
  source:
    git:
      url: https://github.com/vmware-tanzu/application-accelerator-samples
      ref:
        branch: main
    subPath: tanzu-java-web-app
```

いくらか Tanzu CLI コマンドからデプロイした際より、Label や環境変数、Annotation が増えていますが、そこまで特殊なオプションを指定している訳ではないかと思います。

一点、補足すると、`autoscaling.knative.dev/minScale: "1"` という Annotation でしょうか。
このオプションを指定しない場合、Knative の仕組みとして、「最初の HTTP リクエストが発生するまで、アプリのコンテナは 0個で待機する」という挙動になります。これにより、アイドリング時の余剰リソースの消費を抑えることができますが、どうしても「最初のリクエスト1回目」は、若干、応答が遅延します。
このオプションを指定することで、「最低でも1個は、コンテナを立ち上げておく」という挙動になるので、リソース消費とレスポンスの兼ね合いを見て、この辺りの設定を行うことになるかと思います。

その他の設定を含む構成ファイルの記述方法について、↓こちらの Cartographer のサイトが参考になるかと思います。

https://cartographer.sh/docs/v0.6.0/reference/workload/

実際に、この構成ファイルを使って、デプロイするコマンドは下記の通りです。

```sh
tanzu apps workload create -f workload.yaml --yes -n demo
```

ブラウザからのアクセス方法や、後片付けの方法は、パターン①と同様です。


## パターン③: ローカルの Java のソースコードをコマンドでデプロイ

3つ目は、どちらかというと、アプリのコードを実際に書いている開発者が、自分の手元のソースコードを使って、アプリをデプロイしたい場合の方法になります。
こちらの方法は、前述の2つと異なり、ビルドしたコンテナイメージを、何という名前でレジストリに Push するのか、明示的に指定する必要がある点にだけ、ご注意ください。

まず、ローカルのソースコードをインプットとするため、サンプルアプリのリポジトリを手元にクローンする必要があります。

```sh
git clone https://github.com/vmware-tanzu/application-accelerator-samples
```

次に、目的のアプリのディレクトリまで、移動しておきます。

```sh
cd application-accelerator-samples/tanzu-java-web-app/
```

その上で、実行するコマンドとしては、下記のようなイメージになります。

```sh
tanzu apps workload apply tanzu-java-web-app \
    --app tanzu-java-web-app \
    --local-path . \
    --source-image <レジストリ名>/<イメージ名> \
    --type web \
    --namespace demo \
    --yes
```

ほぼ、パターン①で、解説済みのオプションですが、まだのものだけ解説すると...

* `--local-path`
    * デプロイするアプリが、ローカルのどのディレクトリにあるのかを指定します。
* `--source-image`
    * このオプションが、ちょっと名前から分かりづらいですが、「ビルドしたコンテナイメージを、何という名前でレジストリに Push するのか」の指定です。
    * ちなみに、私は初見で、「このアプリをビルドするためのベースイメージ」と勘違いしていました...

こちらも、ブラウザからのアクセス方法や、後片付けの方法は、パターン①と同様です。


# この先...

今回紹介した方法以外にも、既存のコンテナイメージをデプロイする方法であったり、VSCode と連携した Live Update であったり、(TAP v1.4 時点では Beta ですが) Function as a Service を実現したり、TAP ではまだまだ面白いことが出来ますので、別の機会で紹介できればと思います。