---
title: Tanzu Application Platform 1.2 on Azure ハンズオン
tags: ["Kubernetes", "Cartographer", "AKS", "Tanzu", "TAP", "Knative", "Azure", "Tekton", "Service Binding", "Code Server", "Contour", "cert-manager"]
categories: ["Dev", "CaaS", "Kubernetes", "TAP"]
date: 2022-09-01T17:38:20Z
updated: 2022-09-02T00:28:00Z
---
Tanzu Application Platform (TAP)をインストールし、様々な機能を試すハンズオンです。
TAPの大まかな構成要素を次の図で表すと、

本ハンズオンでカバーするのは次の黄色の部分のみです。

本ハンズオンではAzure Kubernetes Service (AKS)にTAP 1.2をインストールします。
以降、[O] がつく節はPlatform Operatorが担当する作業です。[D] がつく節はApp Developerが担当する作業です。
ハンズオン参加者は事前に与えられたハンズオン作業環境 (VS Code Server) にアクセスしてください。
> ℹ️ ハンズオン環境をAKS上に作成する方法は[こちら](https://docs.google.com/document/d/1_cDGvYR4F39oe9C-azkh_GRcuGRsXPIKbU2v39AqvRg)を参照してください。
>
> ハンズオン環境をローカルで実行したい場合は、以下で実行できます(未検証)。
>
> ```
> docker run \
> --name code-server \
> --rm \
> -p 3000:3000 \
> -e PASSWORD=password \
> -e PORT=3000 \
> -e NAMESPACE=handson-00 \
> ghcr.io/making/code-server@sha256:3b671ae5a4e991e72eda10253b6edc945d52ec49b4a246f3034d4ab93457cee2
> ```
次のようなダイアログが表示されたら"Yes, I trust the authors"をクリックしてください。

**目次**
### Tanzu Application Platformのインストール (Iterate Profile)
まずはイテレーション開発用のiterate profileをインストールします。Iterate ProfileはTAPの全構成要素のうち、次の図の部分のみをインストールします。

#### [O] リソースグループの作成
TAPをインストールするためのAzureリソースグループを作成します。

VS Codeのメニューから”Terminal” -> “New Terminal”を選択し、作業用のターミナルを開いてください。
まずはAzureにログインします。次のコマンドを実行してください。
```
az login --allow-no-subscriptions
```
ログインのURLとコードが出力されるので、ブラウザで開いてコードを入力し、

リソースグループ名は”tap-${NAMESPACE}”とします。次のコマンドを実行し、リソースグループを作成してください。
環境変数`NAMESPACE`に値が入っていない場合は任意の値を設定してください。
```
az group create --name tap-${NAMESPACE} --location japaneast
```
#### [O] Azure Container Registryインスタンスの作成
次にAzure Container Registry (ACR)のインスタンスを作成します。このRegistryにはTAPにデプロイするアプリの
* コンテナイメージ
* マニフェスト
* ソースコード
が格納されます。
次のコマンドを実行してください。
```
ACR_NAME=tap${RANDOM}
az acr create --resource-group tap-${NAMESPACE} \
--location japaneast \
--name ${ACR_NAME} --sku standard \
--admin-enabled true
```
ACRへの接続情報を変数に設定します。次のコマンドを実行してください。これらの変数はTAPインストール時に使用します。
```
ACR_SERVER=${ACR_NAME}.azurecr.io
ACR_USERNAME=${ACR_NAME}
ACR_PASSWORD=$(az acr credential show --name ${ACR_NAME} --resource-group tap-${NAMESPACE} --query 'passwords[0].value' --output tsv)
```
次のコマンドで、接続情報が正しいことを試します。試した後は生成されたconfigファイルを削除します。
```
docker login ${ACR_SERVER} -u ${ACR_USERNAME} -p ${ACR_PASSWORD}
rm -rf ${HOME}/.docker/config.json
```
> **TODO**
> 今回はadminユーザーを使用しているが、本来は使用すべきでない。要検討。
#### [O] Azure Kubernetes Serviceクラスタの作成
次にAzure Kubernetes Service (AKS)のK8sクラスタを作成します。このK8sクラスタにTAPをインストールします。
次のコマンドを実行してください。
```
az aks create \
--resource-group tap-${NAMESPACE} \
--location japaneast \
--name tap-sandbox \
--node-count 2 \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 10 \
--node-vm-size standard_f4s_v2 \
--load-balancer-sku standard \
--network-plugin kubenet \
--network-policy calico \
--zones 1 2 3 \
--generate-ssh-keys \
--enable-aad
```
次のコマンドでK8sクラスタへの接続情報を取得してください。
```
az aks get-credentials --resource-group tap-${NAMESPACE} --name tap-sandbox --admin --overwrite-existing
```
次のコマンドでK8sクラスタへ接続できることを確認してください。
```
kubectl cluster-info
```

#### [O + D] EULAのAgree
[Tanzu Network](https://network.tanzu.vmware.com/) のアカウントがない場合は作成してください。
Tanzu Networkにログインして、次のプロダクトのELUAをAgreeしてください。
* [Tanzu Application Platform](https://network.tanzu.vmware.com/products/tanzu-application-platform/)
* [Cluster Essentials for VMware Tanzu](https://network.tanzu.vmware.com/products/tanzu-cluster-essentials/)
Agreeしていない場合は、 "Click here to sign the EULA" をクリックしてください。
#### [O + D] Tanzu CLIのインストール
TAPをインストールしたり、TAPにアプリをデプロイするために使用するTanzu CLIをインストールします。Tanzu CLIは[Tanzu Network](https://network.tanzu.vmware.com/products/tanzu-application-platform/)からダウンロードできます。
CLIからTanzu CLIなどをダウンロードするために、API TOKENを取得します。
[https://network.tanzu.vmware.com/users/dashboard/edit-profile](https://network.tanzu.vmware.com/users/dashboard/edit-profile) (要ログイン)
下の図の"REQUEST NEW REFRESH TOKEN"をクリックし、API TOKENを取得してください。

取得したAPI TOKENを使用して、次のコマンドを実行してください。
```
API_TOKEN=......
pivnet login --api-token=${API_TOKEN}
```
次のコマンドを実行して、Tanzu CLIをダウンロードし、インストールしてください。
```
pivnet download-product-files --product-slug='tanzu-application-platform' --release-version='1.2.1' --glob='tanzu-framework-linux-amd64.tar'
tar xvf tanzu-framework-*-amd64.tar
mkdir -p ${HOME}/.local/bin
install cli/core/*/tanzu-core-*_amd64 ${HOME}/.local/bin/tanzu
```
次のコマンドを実行して、CLIのversionを確認してください。
```
tanzu version
```

次のコマンドを実行してTAPで使用するプラグインをインストールしてください。
```
tanzu plugin install --local cli all
```

不要なファイルは削除してください。
```
rm -f tanzu-framework-linux-amd64.tar
rm -rf cli
```
#### [O] Cluster Essentials for VMware Tanzuのインストール
TAPを管理するための主要なOSSコンポーネントとして、[kapp controller](https://github.com/vmware-tanzu/carvel-kapp-controller)と[secretgen controller](https://github.com/vmware-tanzu/carvel-secretgen-controller)があります。
これらはGithub上のmanifestを使用してもインストールできますが、Cluster Essentials for VMware Tanzuとして[Tanzu Network](https://network.tanzu.vmware.com/products/tanzu-cluster-essentials)からも配布されているため、今回はCluster Essentials for VMware Tanzuをインストールします。
次のコマンドでCluster Essentials for VMware TanzuのInstallerをダウンロードします。
```
mkdir -p ${HOME}/workspace/tap-install
cd ${HOME}/workspace/tap-install
pivnet download-product-files --product-slug='tanzu-cluster-essentials' --release-version='1.2.0' --glob='tanzu-cluster-essentials-linux-amd64-*'
```
次のコマンドでCluster Essentials for VMware Tanzuをインストールします。TANZUNET_USERNAMEにはTanzu Networkのユーザー名(メールアドレス)を、TANZUNET_PASSWORDにはパスワードを設定してください。
```
TANZUNET_USERNAME=...
TANZUNET_PASSWORD=...
mkdir tanzu-cluster-essentials
tar xzvf tanzu-cluster-essentials-*-amd64-*.tgz -C tanzu-cluster-essentials
export INSTALL_BUNDLE=registry.tanzu.vmware.com/tanzu-cluster-essentials/cluster-essentials-bundle:1.2.0
export INSTALL_REGISTRY_HOSTNAME=registry.tanzu.vmware.com
export INSTALL_REGISTRY_USERNAME=${TANZUNET_USERNAME}
export INSTALL_REGISTRY_PASSWORD=${TANZUNET_PASSWORD}
cd tanzu-cluster-essentials
./install.sh --yes
cd ..
```
次のコマンドを実行して、Cluster Essentials for VMware Tanzuインストール後の全Pod一覧を確認してください。
```
kubectl get pod -A
```

#### [O] Package Repositoryの登録
TAPはkapp controllerの[Package Repository](https://carvel.dev/kapp-controller/docs/v0.40.0/packaging/#package-repository)という仕組みを使用して配布されています。
次のコマンドで、TAPのPackage Repositoryを"tap-install" namespaceに登録します。
```
TANZUNET_USERNAME=...
TANZUNET_PASSWORD=...
kubectl create ns tap-install
tanzu secret registry add tap-registry \
--username "${TANZUNET_USERNAME}" \
--password "${TANZUNET_PASSWORD}" \
--server registry.tanzu.vmware.com \
--kubeconfig ${HOME}/.kube/config \
--export-to-all-namespaces \
--yes \
--namespace tap-install
tanzu package repository add tanzu-tap-repository \
--url registry.tanzu.vmware.com/tanzu-application-platform/tap-packages:1.2.1 \
--namespace tap-install \
--kubeconfig ${HOME}/.kube/config
```
登録したPackage Repositoryから利用可能なPackage一覧を次のコマンドで確認できます。
```
tanzu package available list --namespace tap-install --kubeconfig ${HOME}/.kube/config
```

#### [O] Iterate Profileのインストール
いよいよTAP (Iterate Profile)をインストールします。
次のコマンドでTAPをインストールするための設定ファイルを作成します。cnrs.domain_nameに設定したドメイン名は後に変更します。
```yaml
cd ${HOME}/workspace/tap-install
cat < tap-values.yml
profile: iterate
ceip_policy_disclosed: true
cnrs:
domain_name: tap.example.com
domain_template: "{{.Name}}-{{.Namespace}}.{{.Domain}}"
buildservice:
kp_default_repository: ${ACR_SERVER}/build-service
kp_default_repository_username: ${ACR_USERNAME}
kp_default_repository_password: ${ACR_PASSWORD}
supply_chain: basic
ootb_supply_chain_basic:
registry:
server: ${ACR_SERVER}
repository: supply-chain
gitops:
ssh_secret: ""
contour:
infrastructure_provider: azure
envoy:
service:
type: LoadBalancer
annotations: {}
EOF
```
次のコマンドでTAPをインストールします。
```
tanzu package install tap \
-p tap.tanzu.vmware.com \
-v 1.2.1 \
--values-file ${HOME}/workspace/tap-install/tap-values.yml \
-n tap-install \
--kubeconfig ${HOME}/.kube/config \
--wait=false
```
インストールの進捗状況を次のコマンドで確認できます。12分くらいでインストールが完了します。
```
while [ "$(kubectl -n tap-install get app tap -o=jsonpath='{.status.friendlyDescription}')" != "Reconcile succeeded" ];do
date
kubectl get app -n tap-install
echo "---------------------------------------------------------------------"
sleep 30
done
echo "✅ Install succeeded"
```

TAP上のアプリケーションへのリクエストは全て"tanzu-system-ingress" namespaceのenvoyを経由します。envoyにはLoad Balancerがアタッチされ、External IPが設定されます。このIPを使用して、ドメイン名を用意します。今回は[sslip.io](https://sslip.io/)を使用します。例えばdemo.8-8-8-8.sslip.ioは8.8.8.8に解決されます。
次のコマンドを実行して、tap-values.yml中のexample.comをa-b-c-d.sslip.ioに置換します。(a.b.c.dはEnvoyのExternal IP)
```
DOMAIN_NAME=$(kubectl get -n tanzu-system-ingress svc envoy -ojsonpath='{.status.loadBalancer.ingress[0].ip}' | sed 's/\./-/g').sslip.io
sed -i.bak "s|example.com|${DOMAIN_NAME}|g" tap-values.yml
```
次のコマンドを実行して、TAPに変更を適用します。
```
tanzu package installed update tap -f tap-values.yml -n tap-install --kubeconfig ${HOME}/.kube/config
```
設定されたことを次のコマンドで確認できます。
```
kubectl get cm -n knative-serving config-domain -ojsonpath='{.data}'
```

以降、アプリへのリクエストのルーティングは次の図のようになります。

### Tanzu Application PlatformにWorkloadをデプロイ
インストールしたTAPにWorkloadをデプロイします。
#### [O] Workloadデプロイのための準備
"demo" namespaceにWorkloadをデプロイします。本節の作業は**デプロイしたいnamespace毎に必要**です。
```
kubectl create ns demo
```
このnamespace上でTAPで作成されるコンテナイメージをACRからpullしたり、ACRにpushできるようにSecretを作成します。次のコマンドを実行してください。
```
tanzu secret registry add registry-credentials --server ${ACR_SERVER} --username ${ACR_USERNAME} --password ${ACR_PASSWORD} --namespace demo --kubeconfig ${HOME}/.kube/config
```
> **TODO**
> 今回はadminユーザーを使用しているが、本来は使用すべきでない。要検討。
このnamespace上にTAPがWorkloadをデプロイできるためのRBACの設定を行います。次のコマンドを実行してください。
```yaml
cat < ${HOME}/workspace/tap-install/rbac.yaml
apiVersion: v1
kind: Secret
metadata:
name: tap-registry
annotations:
secretgen.carvel.dev/image-pull-secret: ""
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: e30K
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
secrets:
- name: registry-credentials
imagePullSecrets:
- name: registry-credentials
- name: tap-registry
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: default-permit-deliverable
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: deliverable
subjects:
- kind: ServiceAccount
name: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: default-permit-workload
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: workload
subjects:
- kind: ServiceAccount
name: default
EOF
kubectl apply -f ${HOME}/workspace/tap-install/rbac.yaml -n demo
```
次のコマンドを叩いて、Secetを確認してください。
```
kubectl get secret -n demo
```

#### [D] Node.jsアプリをGit上のソースコードからデプロイ
"demo" namespaceに簡単なNode.jsのアプリをデプロイします。ソースコードは[Git](https://github.com/making/hello-nodejs)から取得します。
```
tanzu apps workload apply hello-nodejs \
--app hello-nodejs \
--git-repo https://github.com/making/hello-nodejs \
--git-branch master \
--type web \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=hello-nodejs
```
次のコマンドを実行し、"Knative Services"に **Ready** が表示されればアプリのデプロイが完了です。
```
tanzu apps workload get -n demo hello-nodejs
```

このWorkloadから作成される主要なリソースは次のコマンドで確認できます。
```
kubectl get workload,pod,gitrepo,imgs,build,podintent,taskrun,deliverable,imagerepository,app,ksvc -n demo -owide -l app.kubernetes.io/part-of=hello-nodejs
```

kubectl treeプラグインを使うと作成されたリソースが階層的にわかります。
```
kubectl tree -n demo workload hello-nodejs
```

アプリにアクセスしましょう。
```
curl -sv $(kubectl get ksvc -n demo hello-nodejs -ojsonpath='{.status.url}')
```

ソースコードを変更し、Gitにpushするとアプリはローリングアップデートで再デプロイされます。
[https://github.com/making/hello-nodejs](https://github.com/making/hello-nodejs) を自分のレポジトリにフォークして、Workloadを更新し、ソースコードを更新してみてください。
```
FORKED_REPO=https://…
tanzu apps workload apply hello-nodejs \
--app hello-nodejs \
--git-repo ${FORKED_REPO} \
--git-branch master \
--type web \
-n demo \
-y
```
TAPではRuntimeに使用している[Knative Serving](https://knative.dev/docs/serving/)の仕様によりデフォルトで、1分間リクエストがないとインスタンス数が自動で0になります (Scale to Zero)。再度リクエストを受け付けた際にインスタンスが自動で再作成 されます。
最小インスタンス数を1以上に設定することで、"Scale to Zero"を無効にできます。
次のコマンドで最小インスタンス数を指定できます。
```
tanzu apps workload apply hello-nodejs \
--app hello-nodejs \
--git-repo https://github.com/making/hello-nodejs \
--git-branch master \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
-n demo \
-y
```
デプロイしたWorkloadを次のコマンドで削除します。
```
tanzu apps workload delete -n demo hello-nodejs -y
```
#### [D] JavaアプリをGit上のソースコードからデプロイ
"demo" namespaceに簡単なJavaのアプリをデプロイします。ソースコードは[Git](https://github.com/sample-accelerators/tanzu-java-web-app)から取得します。
```
tanzu apps workload apply tanzu-java-web-app \
--app tanzu-java-web-app \
--git-repo https://github.com/sample-accelerators/tanzu-java-web-app \
--git-branch main \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=tanzu-java-web-app
```
次のコマンドを実行し、"Knative Services"に **Ready** が表示されればアプリのデプロイが完了です。
```
tanzu apps workload get -n demo tanzu-java-web-app
```

このWorkloadから作成される主要なリソースは次のコマンドで確認できます。
```
kubectl get workload,pod,gitrepo,imgs,build,podintent,taskrun,deliverable,imagerepository,app,ksvc -n demo -owide -l app.kubernetes.io/part-of=tanzu-java-web-app
```

kubectl treeプラグインを使うと作成されたリソースが階層的にわかります。
```
kubectl tree -n demo workload tanzu-java-web-app
```

アプリにアクセスしましょう。
```
curl -sv $(kubectl get ksvc -n demo tanzu-java-web-app -ojsonpath='{.status.url}')
```

デプロイしたWorkloadを次のコマンドで削除します。
```
tanzu apps workload delete -n demo tanzu-java-web-app -y
```
#### [D] Dockerfileでイメージビルド
TAPではデフォルトで[Kpack](https://github.com/pivotal/kpack)により[Tanzu Buildpacks](https://docs.vmware.com/en/VMware-Tanzu-Buildpacks/index.html)を使用してソースコードからコンテナイメージが自動で作成されます。Buildpackではなく、Dockerfileでコンテナイメージを作成することも可能です。内部的には[Kaniko](https://github.com/GoogleContainerTools/kaniko)が使用されます。
先ほどデプロイしたNode.jsのアプリを今度はDockerfileを使用してデプロイしましょう。次のコマンドを実行してください。
```
tanzu apps workload apply hello-nodejs \
--app hello-nodejs \
--git-repo https://github.com/making/hello-nodejs \
--git-branch master \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
--param dockerfile=./Dockerfile \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=hello-nodejs
```
次のコマンドを実行し、"Knative Services"に **Ready** が表示されればアプリのデプロイが完了です。
```
tanzu apps workload get -n demo hello-nodejs
```

このWorkloadから作成される主要なリソースは次のコマンドで確認できます。
```
kubectl get workload,pod,gitrepo,imgs,build,podintent,taskrun,deliverable,imagerepository,app,ksvc -n demo -owide -l app.kubernetes.io/part-of=hello-nodejs
```

kubectl treeプラグインを使うと作成されたリソースが階層的にわかります。**Buildpackを使った場合と作成されるリソースに違いがあることを確認してください**。
```
kubectl tree -n demo workload hello-nodejs
```

アプリにアクセスしましょう。
```
curl -sv $(kubectl get ksvc -n demo hello-nodejs -ojsonpath='{.status.url}')
```

デプロイしたWorkloadを次のコマンドで削除します。
```
tanzu apps workload delete -n demo hello-nodejs -y
```
#### [D] Docker Imageを直接デプロイ
ここまでTAPにソースコードからアプリをデプロイしてきましたが、ビルド済みのDocker Imageからアプリをデプロイすることもできます。この場合、これまでとは異なるSupply Chainを使用します。
利用可能なSupply Chain一覧を次のコマンドで確認できます。
```
tanzu apps cluster-supply-chain list
```

これまでは"source-to-url" Supply Chainを使用してきました。今回は"basic-image-to-url" Supply Chainを使用します。
それぞれのSupply Chainを使用するためのWorkloadの条件は次のコマンドで確認できます。
```
tanzu apps cluster-supply-chain get source-to-url
tanzu apps cluster-supply-chain get basic-image-to-url
```

Workloadのspec.imageに値が設定されている場合は"basic-image-to-url" Supply Chainが選択されます。
--imageオプションでデプロイしたいDocker Imageを指定します。ここではTAP 1.2のBuildpackではサポートされていないRubyのアプリケーションをデプロイします。次のコマンドを実行してください。
```
tanzu apps workload apply hello-ruby \
--app hello-ruby \
--image gcr.io/knative-samples/helloworld-ruby \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
--env TARGET="TAP" \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=hello-ruby
```
次のコマンドを実行し、"Knative Services"に **Ready** が表示されればアプリのデプロイが完了です。
```
tanzu apps workload get -n demo hello-ruby
```

このWorkloadから作成される主要なリソースは次のコマンドで確認できます。
```
kubectl get workload,pod,gitrepo,imgs,build,podintent,taskrun,deliverable,imagerepository,app,ksvc -n demo -owide -l app.kubernetes.io/part-of=hello-ruby
```

kubectl treeプラグインを使うと作成されたリソースが階層的にわかります。**Buildpackソースコードからコンテナイメージをビルドする場合と作成されるリソースに違いがあることを確認してください**。
```
kubectl tree -n demo workload hello-ruby
```

アプリにアクセスしましょう。
```
curl -sv $(kubectl get ksvc -n demo hello-ruby -ojsonpath='{.status.url}')
```

デプロイしたWorkloadを次のコマンドで削除します。
```
tanzu apps workload delete -n demo hello-ruby -y
```
#### [D] Javaアプリをローカルパスのソースコードからデプロイ
これまではGit上のソースコードからアプリをデプロイしていましたが、今回はローカルパスにあるソースコードからアプリをデプロイします。先にデプロイしたJavaアプリのソースコードをgit cloneします。
```
cd ${HOME}/workspace
git clone https://github.com/sample-accelerators/tanzu-java-web-app
cd tanzu-java-web-app
```
ローカルパスのソースコードをTAP上にデプロイするために、Tanzu CLIはいったんソースコードをコンテナレジストリにアップロードします。ACRにアップロードできるようにACRへログインします。ログインするためのスクリプトを次のコマンドで作成します。az acr loginコマンドでログインするにはDocker Deamonが必要です。Docker Daemonが起動していない環境ではスクリプトのようにログインできます。
```
cat << EOF > ${HOME}/acr-login.sh
###/bin/bash
set -e
ACR_NAME=$(az acr list --resource-group tap-${NAMESPACE} --query '[0].name' --output tsv)
az acr login -n ${ACR_NAME} --expose-token > ${HOME}/acr-token.json
ACR_SERVER=\$(cat ${HOME}/acr-token.json | jq -r .loginServer)
ACR_TOKEN=\$(cat ${HOME}/acr-token.json | jq -r .accessToken)
set -x
docker login${ACR_SERVER} -u 00000000-0000-0000-0000-000000000000 -p${ACR_TOKEN}
EOF
chmod +x ${HOME}/acr-login.sh
```
次のコマンドでACRへログインしてください。
```
${HOME}/acr-login.sh
```
Tokenは約3時間で失効します。失効したら再度このスクリプトを実行してください。
次のコマンドでローカルパスのソースコードをTAPにデプロイします。
```
cd ${HOME}/workspace/tanzu-java-web-app
tanzu apps workload apply tanzu-java-web-app \
--app tanzu-java-web-app \
--local-path . \
--source-image ${ACR_SERVER}/sources/demo/tanzu-java-web-app \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=tanzu-java-web-app
```
次のコマンドを実行し、"Knative Services"に **Ready** が表示されればアプリのデプロイが完了です。
```
tanzu apps workload get -n demo tanzu-java-web-app
```

このWorkloadから作成される主要なリソースは次のコマンドで確認できます。
```
kubectl get workload,pod,gitrepo,imgs,build,podintent,taskrun,deliverable,imagerepository,app,ksvc -n demo -owide -l app.kubernetes.io/part-of=tanzu-java-web-app
```

kubectl treeプラグインを使うと作成されたリソースが階層的にわかります。**Git上のソースコードからデプロイする場合と作成されるリソースに違いがあることを確認してください**。
```
kubectl tree -n demo workload tanzu-java-web-app
```

アプリにアクセスしましょう。
```
curl -sv $(kubectl get ksvc -n demo tanzu-java-web-app -ojsonpath='{.status.url}')
```

ブラウザからもアクセスしましょう。

#### [D] Tanzu VS Code Extensionsのインストール
開発中、ローカルパスのソースコードを変更する際に効率よくTAPにデプロイするためにVS CodeのExtensionが用意されています。
次のコードでExtensionをダウンロードしてください。
```
pivnet download-product-files --product-slug='tanzu-application-platform' --release-version=1.2.1 --glob='*.vsix'
```
次のコマンドでExtensnionをVS Code Serverにインストールします。
```
for vsix in $(ls *.vsix);do
/usr/lib/code-server/bin/code-server --install-extension ${vsix}
done
rm -f *.vsix
```

VS Code ServerのExtension一覧に次の図のような"Tanzu …" Extensnionが表示されていることを確認してください。

右下のポップアップの"Update Workspace Settings"ボタンをクリックし、 "Restart Now" ボタンをクリックしてリスタートしてください。
#### [D] Live Update
変更したソースコードが即時TAP上に反映される”Live Update”を試します。Live Updateは[Tilt](https://tilt.dev/)によって実現されます。
まずは、git cloneしたソースコードをVS Code Serverで開きます。メニューから”File” -> "Open Folder"を選択してください。

"/home/coder/workspace/tanzu-java-web-app"を選択してください。

次の図のように、Extensions一覧から”Tanzu Developer Tools”を右クリックして、”Extension Settings”を選択してください。

次の図のように、Workspaceタブで"Namespace"に"demo"を"Source Image"に
```
echo $(cat ${HOME}/acr-token.json | jq -r .loginServer)/sources/demo/tanzu-java-web-app
```
の出力結果を設定してください。

tanzu-java-web-appフォルダの"Tiltfile"を右クリックし、"Tanzu: Live Update Start" をクリックします。

次の図のようなエラーが出るでしょう。デプロイ先のK8sクラスタを誤らないために予防されています。

出力された `allow_k8s_contexts('tap-sandbox-admin')` を、次の図のようにTiltfileに追記してください。Tilefileを保存するとデプロイが始まります。

初回のデプロイが完了したら、ブラウザからアクセスしましょう。

次にソースコードを変更しましょう。 src/main/java/com/example/springboot/HelloController.java を開き、index()メソッドの返り値の文字列を変更してください。変更のあったファイルだけがコンテナに転送され、コンテナ内のアプリだけが再起動します。

ブラウザをリロードし、変更が即時に反映されていることを確認してください。

もう一度、コードを変更してください。

ブラウザをリロードし、変更が即時に反映されていることを確認してください。

Live Updateを止める場合は、Tiltfileを右クリックし、"Tanzu: Live Update Stop" をクリックします。

次の図のようなログが出力されます。エンターキーを押すと、ターミナルが閉じます。この手順でLive Updateを止めないと、Tiltが起動し続け、次のStart時にポートが衝突するので、注意してください。

#### [D] Live Debug
次にTAP上にデプロイされたアプリを直接Debugできる、"Live Debug"を試します。
次の図のように、tanzu-java-web-app/configフォルダの"workload.yaml"を右クリックし、"Tanzu: Java Debug Start" をクリックします。

しばらくすると、次の図のように、"Port forwarding to tanzu-java-web-app-******" というメッセージが出力され、フッターのバーの色が代わります。

ソースコードを開いて、止めたい行の左をクリックして、Break Pointを設定します。

この状態で、アプリにアクセスすると、次の図のようにBreak Pointを通った場所で停止します。この状態の変数などを確認することができます。バーの ▶️ ボタンをクリックすると、処理が再開します。

Live Debugを終了する場合は、バーからStopを選択してください。この方法で止めないとDebugモードやPort Forwardが残ったままになるので気をつけてください。

#### [O] HTTPS対応
TAPをデフォルトでインストールした場合はHTTPのみ有効になっています。TLS証明書を作成し、tap-values.ymlに設定することでHTTPSに対応できます。
TLS証明書は[cert-manager](https://cert-manager.io/)を用いて作成します。次のコマンドを実行してください。
```yaml
DOMAIN_NAME=$(kubectl get -n tanzu-system-ingress svc envoy -ojsonpath='{.status.loadBalancer.ingress[0].ip}' | sed 's/\./-/g').sslip.io
cat < ${HOME}/workspace/tap-install/default-tls.yaml
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: cnrs-selfsigned-issuer
namespace: tanzu-system-ingress
spec:
selfSigned: { }
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cnrs-ca
namespace: tanzu-system-ingress
spec:
commonName: cnrs-ca
isCA: true
issuerRef:
kind: Issuer
name: cnrs-selfsigned-issuer
secretName: cnrs-ca
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: cnrs-ca-issuer
namespace: tanzu-system-ingress
spec:
ca:
secretName: cnrs-ca
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cnrs-default-tls
namespace: tanzu-system-ingress
spec:
dnsNames:
- "*.tap.${DOMAIN_NAME}"
issuerRef:
kind: Issuer
name: cnrs-ca-issuer
secretName: cnrs-default-tls
---
apiVersion: projectcontour.io/v1
kind: TLSCertificateDelegation
metadata:
name: contour-delegation
namespace: tanzu-system-ingress
spec:
delegations:
- secretName: cnrs-default-tls
targetNamespaces:
- "*"
EOF
kubectl apply -f ${HOME}/workspace/tap-install/default-tls.yaml
```
次のコマンドで、作成されたSecretを確認します。
```
kubectl get secret -n tanzu-system-ingress
```

ワイルドカード証明書である、 "cnrs-default-tls" をデフォルトのTLS証明書として使用します。
TLSCertificateDelegationが作成されていることで、この"cnrs-default-tls"が他のnamespaceでも利用可能になります。
次のように、tap-values.ymlのcnrs.default_tls_secretを設定してください。
```yaml
### …
cnrs:
# ...
default_tls_secret: tanzu-system-ingress/cnrs-default-tls
### …
```
次のコマンドを実行して、TAPに変更を適用します。
```
tanzu package installed update tap -f ${HOME}/workspace/tap-install/tap-values.yml -n tap-install --kubeconfig ${HOME}/.kube/config
```

TAPのRuntimeであるKnative ServiceがHTTPSをデフォルトで使用するように、次のコマンドで設定を変更します。
```
kubectl patch configmap config-network -n knative-serving --type merge --patch '{"data":{"default-external-scheme": "https"}}'
```
次のコマンドを実行してください。Knative ServicesのURLが"https"に変更されているのがわかります。
```
tanzu apps workload get -n demo tanzu-java-web-app
```

アプリにアクセスしましょう。今回は自己署名証明書を使用しているため、curlのオプションに-kをつけてください。
```
curl -skv $(kubectl get ksvc -n demo tanzu-java-web-app -ojsonpath='{.status.url}')
```

ブラウザでもアクセスしましょう。Chromeの場合は、自己署名証明書を使用している場合、次の図のようなワーニングが表示されます。

ワーニング画面で"THISISUNSAFE"を入力すると、画面へアクセスできます。

### Tanzu Application Platformのインストール (Full Profile)
次にfull profileをインストールします。full ProfileはTAPの全構成要素をインストールします。ただし、今回は使用しないLearning Centerはインストールから除外します。

#### [O] Full Profileのインストール
まず、tap-values.ymlの"profile"を"iterate"から"full"に変更してください。
```yaml
profile: full
### …
```
そして、次のコマンドを実行し、tap-value.ymlにfull profileのインストールに必要な設定項目を追記してください。
```yaml
DOMAIN_NAME=$(kubectl get -n tanzu-system-ingress svc envoy -ojsonpath='{.status.loadBalancer.ingress[0].ip}' | sed 's/\./-/g').sslip.io
cat <> ${HOME}/workspace/tap-install/tap-values.yml
tap_gui:
ingressEnabled: true
ingressDomain: tap.${DOMAIN_NAME}
service_type: ClusterIP
tls:
secretName: cnrs-default-tls
namespace: tanzu-system-ingress
app_config:
app:
baseUrl: https://tap-gui.tap.${DOMAIN_NAME}
backend:
baseUrl: https://tap-gui.tap.${DOMAIN_NAME}
cors:
origin: https://tap-gui.tap.${DOMAIN_NAME}
accelerator:
domain: tap.${DOMAIN_NAME}
ingress:
include: true
enable_tls: true
tls:
secret_name: cnrs-default-tls
namespace: tanzu-system-ingress
server:
service_type: ClusterIP
metadata_store:
app_service_type: ClusterIP
ingress_enabled: "true"
ingress_domain: tap.${DOMAIN_NAME}
ns_for_export_app_cert: "*"
scanning:
metadataStore:
url: ""
excluded_packages:
- grype.scanning.apps.tanzu.vmware.com
- learningcenter.tanzu.vmware.com
- workshops.learningcenter.tanzu.vmware.com
EOF
```
次のコマンドを実行して、TAPに変更を適用します。
```
tanzu package installed update tap -f ${HOME}/workspace/tap-install/tap-values.yml -n tap-install --kubeconfig ${HOME}/.kube/config
```
インストールの進捗状況を次のコマンドで確認できます。5分くらいでインストールが完了します。
```
while [ "$(kubectl -n tap-install get app tap -o=jsonpath='{.status.friendlyDescription}')" != "Reconcile succeeded" ];do
date
kubectl get app -n tap-install
echo "---------------------------------------------------------------------"
sleep 10
done
echo "✅ Update succeeded"
```

#### [D] TAP GUIにアクセス
full profileではDeveloper向けのPortal UIとして、TAP GUIが用意されています。TAP GUIのFQDNは次のコマンドで確認できます。
```
kubectl get httpproxy -n tap-gui tap-gui
```

このFQDNにブラウザでアクセスしてください。次の図のようなワーニングが表示されるので、"THISISUNSAFE"を入力してください。

トップページが表示されます。TAP GUIの認証はK8sの認証と独立しており、デフォルトでは誰でもTAP GUIにアクセスできます。"Enter"ボタンをクリックしてください。

次の図のような、Catalog一覧画面が表示されます。

サイドバーの➕アイコンをクリックしてください。次の図のようにWorkload一覧が表示されます。

Workload名をクリックしてください。このWorkloadに対するSupply Chainが視覚化されます。

#### [D] Catalogの登録
TAP GUIにtanzu-java-web-appのCatalogを登録します。サイドメニューから🏠のアイコンをクリックしてください。
右上の"REGISTER ENTITY"ボタンをクリックします。

Repository URLに [https://github.com/sample-accelerators/tanzu-java-web-app/blob/main/catalog/catalog-info.yaml](https://github.com/sample-accelerators/tanzu-java-web-app/blob/main/catalog/catalog-info.yaml) を入力してください。

"ANALYZE" ボタンをクリックしてください。

"IMPORT" ボタンをクリックしてください。

再度、サイドメニューの🏠アイコンをクリックしてください。

左下の"YOUR ORGANIZATION”が"All 1"になっていることを確認してください。"All" をクリックしてください。

Component一覧に "tanzu-java-web-app" が表示されます。"tanzu-java-web-app" をクリックしてください。

componentの詳細画面が表示されます。Runtime Resourcesタブをクリックしてください。

tanzu-java-web-appに関するResource一覧が表示されます。
Resource Nameが"tanzu-java-web-app-000NN-deployment-XXXXX"で、Statusが"Running"でKindが"Pod"なResourceを選択してください。

Podの詳細情報が表示されます。"VIEW POD LOGS"をクリックしてください。

Podのログが表示されます。

#### [D] App Live Viewの確認
"Overview"タブに戻ってください。
Spring Bootで作られたアプリでSpring Boot Actuatorが含まれている場合は、"Live View" が見られます。
```
⚠️ AdBlockをインストールしている場合は、TAP GUIでは無効化してください。
```

Spring Boot Actuatorのエンドポイントを選択できます。

"Memory"を選んだ場合

"Threads"を選んだ場合

"Log Levels" を選んだ場合

#### [D] App Acceleratorから雛形プロジェクトを作成
サイドメニューから⨁アイコンをクリックしてください。
Accelerators一覧が表示されます。ここからプロジェクトを生成することができます。
"Tanzu Java Web App" の "Choose" を選択してください。

雛形のプレースホルダーに埋める値を入力します。“Name”に”hello-tanzu”を、“Prefix for the container image repository” に
```
echo $(cat ${HOME}/acr-token.json | jq -r .loginServer)/sources/demo
```
の出力結果を設定してください。 "EXPLORE FILE" ボタンをクリックするとプレースホルダーを埋めた状態で生成されるプロジェクトを確認することができます。

config/workload.yamlに "Name" で指定した値が設定されていることを確認してください。

Tiltfileに "Prefix for the container image repository" で指定した値が設定されていることを確認してください。
確認できたらダイアログを閉じて、 "NEXT" ボタンをクリックしてください。

"GENERATE ACCELERATOR" ボタンをクリックしてください。

ZIPファイルが生成されるのでDownloadしてください。

Downloadしたhello-tanzu.zipをworkspaceフォルダにDrag&Dropしてください。

TerminalでZipを展開します。次のコマンドを実行してください。
```
cd ${HOME}/workspace
unzip hello-tanzu.zip
cd hello-tanzu
```
メニューから”File” -> "Open Folder"を選択してください。"/home/coder/workspace/hello-tanzu" を選択し、"OK" ボタンをクリックしてください。

Extensions一覧から”Tanzu Developer Tools”を右クリックして、”Extension Settings”を選択し、”Source Image”に次のコマンドの出力結果を設定してください。
```
echo $(cat ${HOME}/acr-token.json | jq -r .loginServer)/sources/demo/hello-tanzu
```

ACRに再ログインしてください。
```
${HOME}/acr-login.sh
```
Tiltfileの末尾に次の内容を追記してください。
```
allow_k8s_contexts('tap-sandbox-admin')
```
Tiltfileを右クリックして "Tanzu: Live Update Start" を選択してください。

ローカルパスのソースコードからWorkloadが作成されるのでしばらく待ってください。

ログが出力され始めたらTAP GUIのサイドメニューの➕アイコンをクリックし、hello-tanzuのWorkloadを選択してください。Supply Chainでどこまで進んでいるかがわかります。

次のようなアプリログが出力されたら、デプロイが完了です。

TAP GUIでWorkloadを確認するとDeliveryに✅が入っていることが確認できます。

次のコマンドを実行してアプリにアクセスしてください。
```
curl -skv $(kubectl get ksvc -n demo hello-tanzu -ojsonpath='{.status.url}')
```

HelloControllerのソースコードを変更してください。

Live Updateでソースコードの変更が反映されたら、再度アプリにアクセスして、レスポンスが変わることを確認してください。
```
curl -skv $(kubectl get ksvc -n demo hello-tanzu -ojsonpath='{.status.url}')
```

確認できたらTiltfileを右クリックして、"Tanzu: Live Update Stop"をクリックし、Live Updateを終了してください。

次のコマンドを実行してWorkloadを削除してください。
```
tanzu apps workload delete -n demo hello-tanzu --kubeconfig ${HOME}/.kube/config -y
```
#### [O] Acceleratorの追加
[https://github.com/spring-socks/template](https://github.com/spring-socks/template) を追加します。
```
tanzu accelerator create spring-socks --git-repository https://github.com/spring-socks/template --git-branch main
```
Accelerator一覧に "Spring Socks Template" が表示されることを確認してください。

#### [O] TAP GUIのロゴのカスタマイズ
TAP GUIのロゴとタイトルを変更します。tap-values.ymlの次の箇所を変更します。
```yaml
### ….
tap_gui:
# ….
app_config:
customize:
custom_name: " Developer Portal"
custom_logo: ""
app:
title: " Developer Portal"
# ….
### ….
```
次のコマンドを実行して、TAPに変更を適用します。
```
tanzu package installed update tap -f ${HOME}/workspace/tap-install/tap-values.yml -n tap-install --kubeconfig ${HOME}/.kube/config
```
次のコマンドを実行し、TAP GUI のPod (server-xxxxx) が新規作成され、READYが "1/1" になるまで待ってください。
```
kubectl get pod -n tap-gui
```

READYになったら、TAP GUIに再アクセスしてください。
ロゴやタイトルが変更されていることを確認してください。
### Tanzu Application Platformのその他の機能
#### [O] "source-test-to-url" Supply Chainへの変更
ここまで "source-to-url" Supply Chainを使用してきましたが、今度はコンテナイメージを作る前にユニットテストを挟んだ"source-test-to-url" Supply Chainを使用します。
"source-test-to-url" Supply Chainはiterate profileでも利用可能です。
いったんここまで作ったWorkloadを次のコマンドで削除してください。
```
kubectl delete workload -A --all
```
現在のSupply Chainを次のコマンドで確認します。
```
tanzu apps cluster-supply-chain list
```

次のように、tap-values.yml中の"basic"と設定されている箇所を"testing"に変更してください。
```yaml
### …
supply_chain: testing
ootb_supply_chain_testing:
# …
### …
```
次のコマンドを実行して、TAPに変更を適用します。
```
tanzu package installed update tap -f ${HOME}/workspace/tap-install/tap-values.yml -n tap-install --kubeconfig ${HOME}/.kube/config
```
更新後のSupply Chainを次のコマンドで確認します。"source-test-to-url"が表示されることを確認してください。
```
tanzu apps cluster-supply-chain list
```

Supply Chainを使うためのSelectorを確認してください。
```
tanzu apps cluster-supply-chain get source-test-to-url
tanzu apps cluster-supply-chain get testing-image-to-url
```

"source-test-to-url" を使うにはWorkloadに "apps.tanzu.vmware.com/hast-tests: true" というlabelがついている必要があります。
#### [O or D] Pipelineの作成
Supply Chainの中で使用されるテストスクリプトをTektonのPipelineとして定義します。次のコマンドでPipelineを作成してください。
```yaml
cat <<'EOF' > ${HOME}/workspace/tap-install/pipeline-maven.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: maven-test-pipeline
labels:
apps.tanzu.vmware.com/pipeline: test
spec:
params:
- name: source-url
- name: source-revision
tasks:
- name: test
params:
- name: source-url
value: $(params.source-url)
- name: source-revision
value: $(params.source-revision)
taskSpec:
params:
- name: source-url
- name: source-revision
steps:
- name: test
image: eclipse-temurin:17
script: |-
set -ex
cd `mktemp -d`
curl -s $(params.source-url) | tar -m -xzvf -
./mvnw clean test -V --no-transfer-progress
EOF
kubectl apply -f ${HOME}/workspace/tap-install/pipeline-maven.yaml -n demo
```
次のコマンドでWorkloadを作成してください。
```
tanzu apps workload apply tanzu-java-web-app \
--app tanzu-java-web-app \
--git-repo https://github.com/sample-accelerators/tanzu-java-web-app \
--git-branch main \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
--label apps.tanzu.vmware.com/has-tests=true \
-n demo \
-y
```
次のコマンドでWorkloadの状態を確認してください。
```
tanzu apps workload get -n demo tanzu-java-web-app
```
Supply Chainに "source-tester" が追加されていることがわかります。

次のコマンドを実行してログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=tanzu-java-web-app
```
テストの実行ログを確認できます。

TAP GUIでSupply Chainを確認してください。"Source Tester"が追加されていることを確認できます。

Source Testerをクリックするとテストの実行ログを確認することができます。

テストがパスし、アプリがデプロイされてDeliveryに✅がつくまで待ってください。

次のコマンドでアプリにアクセスしてください。
```
curl -skv $(kubectl get ksvc -n demo tanzu-java-web-app -ojsonpath='{.status.url}')
```

* 同じnamespaceで複数のPipelineを扱いたい場合は [https://ik.am/entries/701](https://ik.am/entries/701) を参照。
* Pipelineでキャッシュを使いたい場合は [https://ik.am/entries/700](https://ik.am/entries/700) を参照。
#### [D + O] Service Binding
次はデータベースアクセスのあるアプリ vehicle-api をデプロイします。TAPでは"[Service Binding](https://github.com/servicebinding/spec)"の仕組みを使って、アプリにデータベースを"アタッチ"することができます。これによりアプリはデータベースの接続情報を意識することなく、データベースにアクセスできるようになります。
Service Bindingはiterate profileでも利用可能です
データベースとしてAzure PostgreSQLを使用します。
次のコマンドでAzure PostgreSQLのインスタンスを作成してください。
```
POSTGRESQL_USERNAME=vehicle
POSTGRESQL_PASSWORD=$(openssl rand -base64 32)
az postgres server create \
--resource-group tap-${NAMESPACE} \
--name vehicle-db-${NAMESPACE} \
--location japaneast \
--sku-name B_Gen5_1 \
--storage-size 5120 \
--admin-user ${POSTGRESQL_USERNAME} \
--admin-password ${POSTGRESQL_PASSWORD} \
--version 11 \
--output tsv
```
次のコマンドでAKSから作成したインスタンスにアクセスを許可するためのFirewallの設定を行います。
```
ALLOWED_IP=...
az postgres server firewall-rule create \
--resource-group tap-${NAMESPACE} \
--name vehicle-db-${NAMESPACE}-allowed-ip \
--server vehicle-db-${NAMESPACE} \
--start-ip-address ${ALLOWED_IP} \
--end-ip-address ${ALLOWED_IP} \
--output tsv
```
ALLOWED_IPの値はAKSのoutbound用のIPを設定します。次のコマンドで確認できます。
何も出力されなかったら、もう一度同じコマンドを実行してください。
```
kubectl run -n default curl --timeout=5m --restart=Never --image=curlimages/curl --rm -it -- curl -s https://httpbin.org/ip
```

作成したインスタンスにデータベースを作成します。
```
az postgres db create \
--resource-group tap-${NAMESPACE} \
--name vehicle \
--server-name vehicle-db-${NAMESPACE} \
--output tsv
```
PostgreSQLへの接続情報を次のコマンドで出力します。
```
az postgres server show --resource-group tap-${NAMESPACE} --name vehicle-db-${NAMESPACE} > ${HOME}/vehicle-db.json
```
[Service Bindingの仕様](https://github.com/servicebinding/spec#example-secret)に合わせて、次のような形式のSecretを作成します。
```yaml
apiVersion: v1
kind: Secret
metadata:
name:
type: servicebinding.io/
stringData:
type:
host: …
port: …
username: …
password: …
# …
```
実際にどのようなフィールドを設定すべきかは、このService Bindingに対応したクライアントライブラリに依存します。
TAPではSpring Bootのアプリのコンテナイメージには[spring-cloud-bindings](https://github.com/spring-cloud/spring-cloud-bindings)というライブラリをbuildpackで自動で埋め込みます。
このライブラリを使う場合は[PostgreSQL](https://github.com/spring-cloud/spring-cloud-bindings#postgresql-rdbms)にアクセスする際に、Secretのフィールドがアプリのプロパティへと次の表のようにマッピングされます。

このマッピングに合うように、Azure PostgreSQLにアクセスするためのSecretを次のように作成します。
```yaml
cat < ${HOME}/vehicle-db.yaml
apiVersion: v1
kind: Secret
metadata:
name: vehicle-db
type: servicebinding.io/postgresql
stringData:
type: postgresql
host: $(cat ${HOME}/vehicle-db.json | jq -r .fullyQualifiedDomainName)
port: "5432"
username: ${POSTGRESQL_USERNAME}@vehicle-db-${NAMESPACE}
password: "${POSTGRESQL_PASSWORD}"
database: vehicle
sslmode: require
EOF
kubectl apply -f ${HOME}/vehicle-db.yaml -n demo
```
このデータベースを使用したアプリをデプロイします。Service Bindingの設定は--service-refオプションで行います。
次のコマンドを実行してください。
```
tanzu apps workload apply vehicle-api \
--app vehicle-api \
--git-repo https://github.com/making/vehicle-api \
--git-branch main \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
--label apps.tanzu.vmware.com/has-tests=true \
--service-ref vehicle-db=v1:Secret:vehicle-db \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=vehicle-api
```
Known Issueですが、Service Bindingを使用してWorkloadをデプロイした場合、最初のrevision (00001) は接続情報のSecretが設定されていない状態でアプリがデプロイされてしまいます。その結果、デフォルトのlocalhostに接続しに行こうと試み、エラーが発生します。

revision 00001ができたすぐ後にrevision 00002がデプロイされ、このrevisionではは接続情報のSecretが正しく設定されるため、Service Binidingから接続情報をプロパティが設定され、PostgreSQLに接続できます。

次のコマンドでアプリにアクセスしてください。
```
curl -sk $(kubectl get ksvc -n demo vehicle-api -ojsonpath='{.status.url}')/vehicles | jq .
```

次のコマンドでデータを追加してください。
```
curl -sk $(kubectl get ksvc -n demo vehicle-api -ojsonpath='{.status.url}')/vehicles -d "{\"name\":"Sienta\"}" -H "Content-Type: application/json" | jq .
```

次のコマンドを再度実行し、追加したデータが表示されることを確認してください。
```
curl -sk $(kubectl get ksvc -n demo vehicle-api -ojsonpath='{.status.url}')/vehicles | jq .
```

アプリのPodを次のコマンドで削除してください。しばらくすると自動で復旧します。
```
kubectl delete pod -n demo -l app.kubernetes.io/component=run,app.kubernetes.io/part-of=vehicle-api --force
```
復旧後に再度アプリにアクセスし、データが失われていないことを確認してください。
```
curl -sk $(kubectl get ksvc -n demo vehicle-api -ojsonpath='{.status.url}')/vehicles | jq .
```
TAP GUIにvehicle-apiのCatalogを登録します。次のURLを登録してください。
[https://github.com/making/vehicle-api/blob/main/catalog/catalog-info.yaml](https://github.com/making/vehicle-api/blob/main/catalog/catalog-info.yaml)

vehicle-apiのApp Live Viewを確認してください。

Healthを選択するとPostgreSQLとの接続状態の確認ができます。

#### [D + O] Resource Claim
前節ではアプリに必要なデータベースの接続情報をSecretとして直接作成し、バインドしました。
今回はResource Claimという、プールされたリソースからサービスのインスタンスを確保する方法でService Bindingを行います。
Resource Claimはiterate profileでも利用可能です
demo namespaceに作成したSecretを次のコマンドで削除してください。
```
kubectl delete secret -n demo vehicle-db
```
次のコマンドでResource ClaimによってSecretを管理するためのRBACの設定を行います。
```yaml
cat < ${HOME}/workspace/tap-install/resourceclaim-role.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: resource-claims
labels:
servicebinding.io/controller: "true"
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
EOF
kubectl apply -f ${HOME}/workspace/tap-install/resourceclaim-role.yaml
```
PostgreSQLのInstance Classを定義します。ここでは "type" fieldが "servicebinding.io/postgresql" であるSecretをPostgreSQLインスタンスと定義します。
```yaml
cat < ${HOME}/workspace/tap-install/clusterinstanceclass.yaml
---
apiVersion: services.apps.tanzu.vmware.com/v1alpha1
kind: ClusterInstanceClass
metadata:
name: postgresql
spec:
description:
short: PostgreSQL
pool:
group: ""
kind: Secret
fieldSelector: type=servicebinding.io/postgresql
EOF
kubectl apply -f ${HOME}/workspace/tap-install/clusterinstanceclass.yaml
```
次のコマンドでこのクラスタ上で定義されているInstane Class一覧を表示します。
```
tanzu service classes list
```

次のコマンドでdemo namespaceで利用可能なPostgreSQLインスタンス一覧を表示します。
```
tanzu service claimable list --class postgresql -n demo
```
まだ利用可能なインスタンスは用意されていません。

サービスインスタンスを管理するnamespaceとしてservice-instancesを作成します。
```
kubectl create namespace service-instances
```
前説で作ったAzure PostgreSQLへの接続情報を持つSecretをservice-instances namespaceに作成します。
```
kubectl apply -f ${HOME}/vehicle-db.yaml -n service-instances
```
次のコマンドで再度demo namespaceで利用可能なPostgreSQLインスタンス一覧を表示します。
```
tanzu service claimable list --class postgresql -n demo
```
まだ利用可能なインスタンスはありません。vehicle-dbはdemo namespaceではなく、service-instances namespaceに作成されているからです。

次のコマンドでservice-instances namespaceに作成されているSecretを他のnamespaceで参照できるようなポリシーを作成します。
```yaml
cat < ${HOME}/workspace/tap-install/resourceclaimpolicy.yaml
apiVersion: services.apps.tanzu.vmware.com/v1alpha1
kind: ResourceClaimPolicy
metadata:
name: cross-namespace
namespace: service-instances
spec:
consumingNamespaces:
- '*'
subject:
group: ""
kind: Secret
EOF
kubectl apply -f ${HOME}/workspace/tap-install/resourceclaimpolicy.yaml
```
次のコマンドで再度demo namespaceで利用可能なPostgreSQLインスタンス一覧を表示します。
```
tanzu service claimable list --class postgresql -n demo
```
今度はservice-instances namespaceにvehicle-dbが利用可能になりました。

次のコマンドでservice-instances namespaceのvehicle-dbをdemo namespaceで使うための確保を行います。
```
tanzu service claim create vehicle-db \
--resource-name vehicle-db \
--resource-namespace service-instances \
--resource-kind Secret \
--resource-api-version v1 \
-n demo
```

確保したサービスの情報を次のコマンドで確認できます。
```
tanzu services claims get vehicle-db -n demo
```

service-instances namespaceからこのインスタンスに関するSecretがコピーされます。
```
kubectl get secret -n demo vehicle-db
```

次のコマンドで再度demo namespaceで利用可能なPostgreSQLインスタンス一覧を表示します。
```
tanzu service claimable list --class postgresql -n demo
```
vehicle-dbは確保されたため、利用可能ではなくなりました。

確保したリソースはResourceClaimリソースとして管理されます。次のコマンドでSecretの代わりにResourceClaimをバインドしてください。
```
tanzu apps workload apply vehicle-api \
--app vehicle-api \
--git-repo https://github.com/making/vehicle-api \
--git-branch main \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
--label apps.tanzu.vmware.com/has-tests=true \
--service-ref vehicle-db=services.apps.tanzu.vmware.com/v1alpha1:ResourceClaim:vehicle-db \
-n demo \
-y
```
次のコマンドでログを確認してください。
```
stern -n demo -l app.kubernetes.io/part-of=vehicle-api
```
生成されたmanifestがResourceClaimを使用していることが分かります。

今回はResourceClaimの名前とSecretの名前が同じだったので変更点が分かりづらいですが、ResourceClaimとSecretの名前は必ずしも一致するわけではありません。また、適切なRBAC設定されている場合、DeveloperはSecretを参照することはできませんが、ResourceClaimは参照できます。したがって、Developer観点ではSecretをバインドするよりもResourceClaimを参照する方がより良いです。Operator観点でもResouce Claimを使用すると、リソースをプールして公開することにより、self-serviceのようにDeveloperにデータベースを利用してもらえるという運用的メリットがあります。
#### [O] DeveloperのRBAC
Azure AD上でグループを作成してください。
```
GROUP_ID=$(az ad group create --display-name tap-demo-developer-${NAMESPACE} --mail-nickname tap-demo-developer-${NAMESPACE} --query id -o tsv)
```
グループ内のメンバー一覧を確認します。この段階ではメンバーは空です。
```
az ad group member list --group tap-demo-developer-${NAMESPACE}
```
ログインユーザーをグループに追加します。
```
az ad group member add --group tap-demo-developer-${NAMESPACE} --member-id $(az ad signed-in-user show --query id --output tsv)
```
グループ内のメンバー一覧を確認し、追加したユーザーが含まれていることを確認してください。
```
az ad group member list --group tap-demo-developer-${NAMESPACE}
```
グループに対し、demo namepsaceへの`app-editor` Roleと、`app-editor-cluster-access` CluserRoleをバインドします。
```
kubectl create rolebinding app-editor -n demo --clusterrole app-editor --group ${GROUP_ID}
kubectl create clusterrolebinding app-editor-${GROUP_ID} --clusterrole app-editor-cluster-access --group ${GROUP_ID}
```
demo namespaceのrolebiningを確認してください。
```
kubectl get rolebinding -n demo app-editor -owide
```

clusterrolebiningを確認してください。
```
kubectl get clusterrolebinding app-editor-${GROUP_ID} -o wide
```

adminではなく、一般ユーザーとしてAKSにアクセスするためのconfigを取得します。
```
az aks get-credentials --resource-group tap-${NAMESPACE} --name tap-sandbox --overwrite-existing
```
default namespaceのPod一覧の取得を試みてください。権限がなく、アクセスを拒否されます。
```
kubectl get pod -n default
```

demo namespaceのPod一覧の取得を試みてください。権限があるためPod一覧を取得できます。
```
kubectl get pod -n demo
```

demo namespaceのWorkload一覧を取得できることを確認してください。
```
tanzu apps workload list -n demo
```

demo namespaceでもSecretは取得できません。
```
kubectl get secret -n demo
```

一方、ResourceClaimであれば取得できます。
```
tanzu service claim list -n demo
```

Workloadを削除できることを確認してください。
```
tanzu apps workload delete -n demo vehicle-api -y
```
Workloadを作成できることを確認してください。
```
tanzu apps workload apply vehicle-api \
--app vehicle-api \
--git-repo https://github.com/making/vehicle-api \
--git-branch main \
--type web \
--annotation autoscaling.knative.dev/minScale=1 \
--label apps.tanzu.vmware.com/has-tests=true \
--service-ref vehicle-db=services.apps.tanzu.vmware.com/v1alpha1:ResourceClaim:vehicle-db \
-n demo \
-y
```
#### [O] TAP GUIのAzure AD連携
TBD
[https://ik.am/entries/708](https://ik.am/entries/708) を参照
#### [D + O] WorkloadのGitOps
TBD
[https://ik.am/entries/706](https://ik.am/entries/706) を参照
### リソースの削除
ハンズオンが終わったら、次のコマンドを実行して作成したリソースを削除してください。
```
az group delete --name tap-${NAMESPACE} -y
```