CAPで作ったサービスをKyma runtimeにデプロイ(4) UIを追加してLaunchpadにデプロイ

## はじめに
この記事は[CAPで作ったサービスをKyma runtimeにデプロイ](https://qiita.com/tami/items/8a2921217eba458eeb8a)シリーズの4回目です。以下のチュートリアルを参考にしつつ、

https://developers.sap.com/mission.btp-deploy-cap-kyma.html

- CAPで作ったアプリをKymaにデプロイするにはどのような仕組みが使われているか
- 一からアプリを作成してKymaにデプロイするには何が必要か

について確認することを目的としています。
4回目の今回は、[前回](https://qiita.com/tami/items/bac04b92414af5dc46a2)作成した認証付きのCAPサービスにUIを追加し、BTPのLaunchpadから使えるようにします。

## UIを追加してLaunchpadにデプロイ
**ステップ**
1. UIを作成
1. UIをビルド
1. HTML5 Appデプロイヤーの設定
1. Dockerイメージの作成とプッシュ
1. Helmの設定
1. Kyma runtimeにデプロイ
1. デプロイ結果の確認

### 1. UIを作成
ここではごく単純なList Reportを作成します。

コマンドから`Fiori: Open Application Generator`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/46dcf417-5c05-a3b6-369b-c316257bc6df.png)
List Report Object Pageを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3749a06c-f1d4-608e-d225-7e795c0d765e.png)
データソースに"Use a Local CAP Project"を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/38bb684f-79a5-4c54-3bf9-e17d14d617f9.png)
"Automatically add table columns to the list page ..."を選択します。これにより、自動的にUIアノテーションが設定されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/16e9181e-a0b6-d634-a393-a91070cd2f1b.png)
モジュール名などを入力します。"Add FLP configuration"に"Yes"を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4b7f5597-9477-1db9-dfa5-153dcb74dda0.png)
Fiori Launchpadのための設定を追加して完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/bdd28979-3013-cf95-762d-7c19f05d37a0.png)
appフォルダにbooklistが追加されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e9c629f3-45cd-b797-ce88-fc5288875e32.png)
annotations.cdsにアノテーションが定義されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3212ff52-fa7d-a1ef-6218-05e9731b96e9.png)
`cds watch`でサービスを起動し、アプリを開きます。認証が必要なので、.cdsrc.jsonに設定したユーザとパスワードを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0f8cb243-ee44-e1e1-9e80-f6efdb5df1ec.png)
画面が表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8003a691-9960-3446-344c-d572496f3f5a.png)

### 2. UIをビルド
app/booklistフォルダに移動し、以下のコマンドでデプロイ用の設定を追加します。

```
fiori add deploy-config cf
```
Destinaiton名と、Managed Approuterを使うか聞かれるので、以下のように回答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/25095844-b424-dfb8-62a9-127c6b36f830.png)

以下のコマンドでビルドを実行します。

```
npm run build:cf
```
distフォルダが作成されます。この中のnsbooklist.zipをこのあと使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/eebd9fb5-be85-2633-e05b-5a132b3b4f0c.png)

### 3. HTML5 Appデプロイヤーの設定
HTML5 Appデプロイヤーは、zipされたUIのコンテンツをCloud FoundryのHTML5 Appリポジトリにデプロイします。Cloud Foundryへのデプロイでも以前は[mta.yamlにデプロイヤーモジュールを定義していました](https://blogs.sap.com/2020/09/06/developing-a-fiori-elements-app-with-cap-and-fiori-tools/)。最近は使わなくなった手段ですが、Kyma runtimeへのデプロイでは現役です。

まず、プロジェクトのルートにdeployerというフォルダを作成し、その中にpackage.jsonを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/cd00d32e-f4b5-d070-c064-5450bcd74f5c.png)

```json:package.json
{
    "name": "html5-deployer",
    "engines": {
        "node": ">=6.0.0"
    },
    "dependencies": {
        "@sap/html5-app-deployer": "^4.1.2"
    },
    "scripts": {
        "start": "node node_modules/@sap/html5-app-deployer/index.js"
    }
}

```
resourcesという名前のフォルダを作成し、先ほど作成したnsbooklist.zipをコピーして格納します。node_modulesフォルダとpackage-lock.jsonがある場合は、それらを削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5aa7527f-a43b-157b-0743-977ec72fb82d.png)


### 4. Dockerイメージの作成とプッシュ
projectのルートで以下のコマンドを実行し、Dockerイメージを作成します。

```
pack build <コンテナレジストリ>/capkyma-html5-deployer \
     --path deployer \
     --buildpack gcr.io/paketo-buildpacks/nodejs \
     --builder paketobuildpacks/builder:base

# Powershellの場合、改行はバッククオート(`)
```
以下のコマンドでDockerイメージをコンテナレジストリにプッシュします。

```
docker push <コンテナレジストリ>/capkyma-html5-deployer
```

※UI側で追加のアノテーションを定義したため、CAPサービスのイメージも更新する必要があります。プロジェクトをビルドした後、[こちら](https://qiita.com/tami/items/90e353b0a62d0771b502#24-cap%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AEdocker%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%82%92%E4%BD%9C%E6%88%90)の要領でイメージをプッシュします。

### 5. Helmの設定
以下のコマンドで、Helm chartにHTML5 Appデプロイヤーの設定を追加します。

```
cds add helm:html5_apps_deployer
```
以下のセクション（destination, html5_app_deployer, html5_app_repo_host）が追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/ba970da8-1c48-e30e-87d3-e3e0ad4618dd.png)
html5_app_deployerのセクションを以下のように設定します。

```yaml
html5_apps_deployer:
  cloudService: booklist # UIのmanifest.jsonに設定されたsap.cloud.service
  backendDestinations:
    capkyma-srv: # UIのxs-app.jsonに設定されたdestination
      service: srv
  image:
    repository: docker.io/miyasuta/capkyma-html5-deployer # HTML5 Appデプロイヤーのイメージ
    tag: latest
```

[前回](https://qiita.com/tami/items/bac04b92414af5dc46a2)xs-security.jsonの`redirect-uris`を設定した関係で、LaunchpadのサイトのURLもここに追加します。（redirect-uriを設定した場合、ここに指定がないURLから呼ばれたときにエラーになる）

```json:xs-security.json
  "oauth2-configuration": {
    "redirect-uris": [
      "https://my-approuter.a2985eb.kyma.shoot.live.k8s-hana.ondemand.com/**",
      "http://localhost:5000/**",
      "https://9e9ab65etrial.launchpad.cfapps.eu10.hana.ondemand.com/**"
    ]
  }
```

`helm template capkyma ./chart`で作成されるリソースを見てみます。前回と比べ増えたものは以下です。

|リソース  |何をするものか  |
|---|---|
|ServiceInstance (destinations) |Destinationのサービスインスタンスを登録 |
|ServiceInstance (html5-apps-repo-host) |html5-apps-repo-hostのサービスインスタンスを登録 |
|ServiceBinding (destinations) |HTML5のdeployerにdestinationのシークレットをバインド |
|ServiceBinding (html5-apps-repo-host) |HTML5のdeployerにhtml5-apps-repo-hostのシークレットをバインド |
|ServiceBinding (html5-apps-deployer-xsuaa) |HTML5のdeployerにxsuaaのシークレットをバインド |
|Job (html5-apps-deployer) |UIのリソースをHTML5 Appリポジトリにデプロイ |

### 6. Kyma runtimeにデプロイ
以下のコマンドでKyma runtimeにデプロイします。

```
helm upgrade capkyma ./chart --install
```

### 7. デプロイ結果の確認
HTML5 Appリポジトリにコンテンツがデプロイされていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/462dc2be-326d-3385-d3b9-0d55a3766ba8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4971fc5d-6eb9-f50a-5f01-a358bf9f1f1d.png)


最初にデプロイしたとき、UIにアノテーションが付いておらず列が表示されませんでした。CAPのサービスモジュールのイメージは最新化していたのですが。そこで、イメージにタグ（1.0.0）をつけて作成し、chart/valus.yamlでも同じタグを指定したところアノテーションが更新されました。

```yaml:values.yaml
srv:
  bindings:
    db:
      fromSecret: capkyma-db
    auth:
      serviceInstanceName: xsuaa
  image:
    repository: docker.io/miyasuta/capkyma-srv
    tag: 1.0.0 # もとはlatest
```
デプロイされたコンテナのImagePullPolicyは"Always"になっていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/643bf508-7407-06fb-9ec7-2562904464b9.png)

Kubernatesの[ドキュメント](https://kubernetes.io/docs/concepts/containers/images/#updating-images)によると、Image Pull Policy: Alwaysの場合、以下の動作になります。

- コンテナを起動する都度、kubeletはコンテナイメージレジストリに問い合わせしてイメージの名前に対応するイメージダイジェストを取得しする
- kubeletがローカルに同じダイジェストのコンテナイメージをキャッシュで持っている場合、kubeletはキャッシュされたイメージを使用する
- そうでない場合、kubeletは問い合わせで取得したダイジェストを持つイメージを取得し、そのイメージを使ってコンテナを起動する

これを読む限りでは、latestタグを指定していてもイメージダイジェストが更新されていれば、最新のイメージを引っ張ってきてくれそうですが。。

## まとめ
基本的なCAPサービス、認証、UIと追加してきて、最終的にHelm chartでデプロイされたものとその関係を図にしてみます。向かって左側の青い箱群がKyma runtime (Kubernetes)特有のもの、右側の緑の箱群がCloud Foundryでも（同じ概念で）使っているものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2d5150a1-fb35-2c0c-5abd-1f9c391d9817.png)
「CAPで作ったサービスをKyma runtimeにデプロイする」という一連の作業をやってみての感想は以下です。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">CAPやApprouterなど「Cloud Foundry的なもの」をデプロイしようとするとKyma runtimeは余計に手間がかかる。あえてKymaでやる必要はあるのか。KymaにはKymaのユースケースがあるはず。</p>&mdash; tami (@tami62496318) <a href="https://twitter.com/tami62496318/status/1565084779489345536?ref_src=twsrc%5Etfw">August 31, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今後は、「そもそもKyma runtimeを使うと何がうれしいのか？」「Kyma runtimeの良さを生かした使い方は？」といったことについて調べていきたいと思います。