Codewindを使ってSpring BootアプリをKubernetes上にビルドしてみた

#はじめに

[Kabanero](https://developer.ibm.com/open/projects/kabanero/)はKubernetes環境でのアプリ開発・運用のためのツール群(Knative, Tekton, Appsody, Codewind等)を提供するオープンソース・プロジェクトです。IBM Cloud Pak for ApplicationsではAccelerators for Teamsという名前になってクラウドネイティブ・アプリ開発フレームワークを提供しています。
とはいえ、実はこの記事を書いている時点でIBM Cloud Pak for Applicationsは既に販売を終了しており、後継製品のWebSphere Hybrid EditionではAccelerators for Teamsを提供していません。
なので今更感はありますが、クラウドネイティブ・アプリ開発試したい取っ掛かりとして[IBM Developer](https://developer.ibm.com/)サイトの"[Use Kabanero, Appsody, and Codewind to build a Spring Boot application on Kubernetes](https://developer.ibm.com/tutorials/kabanero-introduction-to-modern-microservices-development-for-kubernetes/)"というチュートリアルをやってみたので、手順を残しておきます。

#環境
このチュートリアルでは以下の前提ソフトウェアが必要です。
<dl>
  <dt>Docker</dt>
  <dd>後述のkindやCodewindは[Docker](https://www.docker.com/get-started)前提なのでインストールしておきます。v17.06以降が必要です。</dd>
  <dt>Kubernetes</dt>
  <dd>[kind](https://kind.sigs.k8s.io/docs/user/quick-start/)や[minikube](https://minikube.sigs.k8s.io/docs/start/)を使えばローカルPC上でKubernetesクラスターを作れます。今回はkind (Kubernetes IN Docker)を使用しました。</dd>
  <dt>Knative</dt>
  <dd>[Knative](https://knative.dev/docs/)はKubernetes上でサーバーレス基盤を構築するコンポーネントです。今回はkind上にセットアップします。</dd>
  <dt>Appsody CLI</dt>
  <dd></dd>
  <dt>Codewind</dt>
  <dd>VS CodeやEclipseなどのIDEでクラウドネイティブなアプリを開発、ビルドできるツールです。今回はVS Codeを使用します。</dd>
</dl>

今回使用した環境はこちら。
Ubuntu 20.04.3
Docker version 20.10.12
docker-compose version 1.29.2
kind version 0.11.1
Knative version 1.1.0
Visual Studio Code
Codewind 0.14.1
Appsody 0.6.5

#セットアップ
###kind
今回Kubernetes環境として使用したkindは色々使えて便利なので、セットアップ手順は別記事にまとめました。
[Linux環境でKubernetesクラスターを動かす](https://qiita.com/miekoma/items/cb1dd4528f19ff8eb37a)


###Docker
Docker Engineはkindの前提としてインストールしているので、上記の別記事をご参照ください。
ちなみにLinux環境では、Codewindの前提としてDocker Composeも必要となるので、公式の[手順](https://docs.docker.com/compose/install/)に従ってインストールします。

* docker-composeをダウンロードして、実行権限を付与します。

```console
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
```

* コマンド実行してみます。以下のようにバージョンが表示されればOKです。

```console
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c
```
###Knative
Knativeも汎用的に使えるのでセットアップ手順は別記事にまとめました。
[kind + Knativeでローカルにサーバーレス環境をつくる](https://qiita.com/miekoma/items/e559c065ccbeb2ba1572)

###Appsody CLI
Appsody CLIをインストールします。ちなみにLinux環境の場合はdockerアクセスのため、ユーザーをdocker groupに追加しておきましょう。
[AppsodyのRelease page](https://github.com/appsody/appsody/releases)を参照して、最新パッケージをダウンロード、インストールします。今回はappsody_0.6.5_amd64.debでした。

###VS Code + Codewind
* VS Codeをインストールします
VS Codeは[公式サイト](https://code.visualstudio.com/Download)などからダウンロードしてインストールしておいてください。

* Codewindをインストールします
[Codewind](https://marketplace.visualstudio.com/items?itemName=IBM.codewind)はVS Codeの拡張機能です。VS Codeの拡張機能(Extensions)ビューでCodewindを検索してインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/9e2a16b9-0658-31a4-298f-c69203608a67.png)

* Codewindを開始します
エクスプローラー・ペインでCodewindを開きます。こんな風にLocalに緑のチェックマークがついていればOKです。起動していない場合は、Localを右クリックして"Start Local Codewind"を実行してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/b01bc3e1-d778-9bfb-5047-4132f001496a.png)

* Appsody Stackのアプリケーション・テンプレートを使用できるように設定します
CodewindのLocalを右クリックして、"Template Source Manager"をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/c237822e-d092-919c-5a2f-618d545b49bb.png)
Appsody Stacks - incubatorがEnabledになっていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/6231b8c2-c6bf-4479-227d-a9caf930dec8.png)



#アプリ開発
###Spring Bootプロジェクトを作成

チュートリアルではプロジェクトの作成などAppsody CLIを使っているのですが、今回はAppsody CLIはインストールせずにCodewindの機能だけで進めてみます。

Localを右クリックして"Create New Project"を選択します。"Appsody Spring Boot default template"を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/07dda297-b1bc-c15f-d677-254971abbd95.png)

名前を入力してEnterを押し、ディレクトリーを選択すると、java-spring-boot2というテンプレートからプロジェクトの作成が開始されます。以下はログ抜粋ですが、内部的にDockerイメージをpullしてきて実行してくれているのがわかります。

```shell-session
Running development environment...
Pulling docker image docker.io/appsody/java-spring-boot2:0.3
Running command: docker pull docker.io/appsody/java-spring-boot2:0.3
0.3: Pulling from appsody/java-spring-boot2
Digest: sha256:b1dc6398a112888f2b4a5e46a517f5f39b13d0712132e2ec53d9382c15be8aaa
Status: Image is up to date for appsody/java-spring-boot2:0.3
[Warning] The stack image does not contain APPSODY_DEPS
Running command: docker run --rm -v appsody-controller-0.3.5:/.appsody appsody/init-controller:0.3.5
Unable to find image 'appsody/init-controller:0.3.5' locally
0.3.5: Pulling from appsody/init-controller
0669b0daf1fb: Pulling fs layer
e83a650ab835: Pulling fs layer
050bec57020f: Pulling fs layer
050bec57020f: Verifying Checksum
050bec57020f: Download complete
0669b0daf1fb: Download complete
0669b0daf1fb: Pull complete
e83a650ab835: Verifying Checksum
e83a650ab835: Download complete
e83a650ab835: Pull complete
050bec57020f: Pull complete
Digest: sha256:056ff05de4a425b710d970d308faf1f022ed50ddbf591cc571cc9e776128b98d
Status: Downloaded newer image for appsody/init-controller:0.3.5
Running command: docker run --rm -P --name cw-kabanerotestspringboot2-35a7a7d0-60c0-11ec-a1db-bb1d995c8778 --network codewind_network -u 0:0 -e "APPSODY_USER=0" -e "APPSODY_GROUP=0" -v /home/goma/codewind-data/kabanero-test-springboot2/:/project/user-app -v appsody-kabanero-test-springboot2-cwdeps1:/mvn/repository -v appsody-controller-0.3.5:/.appsody -t --entrypoint /.appsody/appsody-controller docker.io/appsody/java-spring-boot2:0.3 "--mode=run"
[Container] Running command:  /project/java-spring-boot2-build.sh run
[Container] [0;34mInstalling parent dev.appsody:spring-boot2-stack:0.3.30 and required dependencies...[0m
[Container] [0;32m> mvn install -q -f /project/appsody-boot2-pom.xml[0m
```

こんな構成でプロジェクトが作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/c692e6ae-2334-a33e-98aa-f30a43091613.png)

Codewindビューでプロジェクトを右クリックすると、ログの表示やプロジェクトに対するオペレーションメニューを選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/7be2b769-6583-a450-f9a0-fa9784b44de3.png)
"Open Application"を実行してみると、アプリケーションが実行されてWebブラウザーに以下の画面が表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/af393b48-f30f-fe9d-3b4a-bee8d068780f.png)

試しにソースを編集してみます。VS Codeでsrc/main/resources/public/index.htmlを開いてテキストを書き換えてみます。変更を保存してからブラウザーで再表示すると、変更が即反映されているのが確認できます。

```html
  <body>
    <h3>Hello from Appsody & Codewind!</h3>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143627/fc53ea4a-6fc9-f5c4-361d-e9f25eea8f26.png)


##Kubernetesクラスターにデプロイ
次はいよいよKubernetesクラスターへデプロイします。
本当はKubernetes側にもCodewindをセットアップしてVS Codeからリモート接続で操作したかったのですがうまくいかず、とりあえずAppsody CLIを使って実行します。

* Appsody Operatorのインストール

Appsodyで開発したアプリをKubernetes環境へデプロイするための機能を提供してくれるので、インストールしておきます。

```console
$ sudo appsody operator install 
```

* プロジェクトをビルド、DockerリポジトリーへPush

以下、VS Codeで作成したプロジェクト(kabanero-test-springboot2)のディレクトリーへ移動して作業します。
まずはビルドします。

```console
$ sudo appsody build --knative -t {username}/kabanero-test-springboot2
$ sudo docker push {username}/kabanero-test-springboot2
```
{username}の部分は自分のDocker Hubユーザー名に置き換えます。appsodyコマンドの--knativeはKnative Serviceとしてデプロイするためのオプションです。

ビルドしたイメージをDockerリポジトリーへPushします。

```console
$ sudo docker push {username}/kabanero-test-springboot2
```

* デプロイ

アプリケーションをデプロイします。

```console
$ sudo appsody deploy --knative -t {username}/kabanero-test-springboot2
```

上は直接デプロイしていますが、
1. `appsody deploy --generate-only`でyamlファイルを生成して
2. `kubectl apply -f app-deploy.yaml`コマンドでデプロイ
してもよいです。
以下が生成されたapp-deploy.yaml例です。ちなみに--knativeオプションを指定すると、"createKnativeService"がtrueになるようです。

```app-deploy.yaml
apiVersion: appsody.dev/v1beta1
kind: AppsodyApplication
metadata:
  annotations:
    commit.stack.appsody.dev/contextDir: /incubator/java-spring-boot2
    commit.stack.appsody.dev/date: Thu Jun 4 12:03:56 2020 +0100
    commit.stack.appsody.dev/message: 'java-springboot2: define APPSODY_DEBUG_PORT
      (#819)'
    image.opencontainers.org/created: "2021-12-20T15:17:35+09:00"
    stack.appsody.dev/authors: Erin Schnabel <ebullient>, Ozzy Osborne <bardweller>,
      Richard Trotter <richard-trotter>, Harish Yayi <yharish991>
    stack.appsody.dev/configured: docker.io/appsody/java-spring-boot2:0.3
    stack.appsody.dev/created: "2020-06-04T11:06:14Z"
    stack.appsody.dev/description: Spring Boot using OpenJ9 and Maven
    stack.appsody.dev/digest: sha256:b1dc6398a112888f2b4a5e46a517f5f39b13d0712132e2ec53d9382c15be8aaa
    stack.appsody.dev/documentation: https://github.com/appsody/stacks/tree/master/incubator/java-spring-boot2/README.md
    stack.appsody.dev/licenses: Apache-2.0
    stack.appsody.dev/revision: 7417563d37f987bc803711298b29817519cc648d
    stack.appsody.dev/source: https://github.com/appsody/stacks/tree/master/incubator/java-spring-boot2/image
    stack.appsody.dev/tag: docker.io/appsody/java-spring-boot2:0.3.30
    stack.appsody.dev/title: Spring BootR
    stack.appsody.dev/url: https://github.com/appsody/stacks/tree/master/incubator/java-spring-boot2
  creationTimestamp: null
  labels:
    image.opencontainers.org/title: kabanero-test-springboot2
    stack.appsody.dev/id: java-spring-boot2
    stack.appsody.dev/version: 0.3.30
  name: kabanero-test-springboot2
spec:
  applicationImage: {username}/kabanero-test-springboot2
  createKnativeService: true
  expose: true
  livenessProbe:
    failureThreshold: 12
    httpGet:
      path: /actuator/liveness
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 2
  monitoring:
    endpoints:
    - path: /actuator/prometheus
    labels:
      k8s-app: kabanero-test-springboot2
  readinessProbe:
    failureThreshold: 12
    httpGet:
      path: /actuator/health
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 2
    timeoutSeconds: 1
  service:
    annotations:
      prometheus.io/path: /actuator/prometheus
      prometheus.io/scrape: "true"
    port: 8080
    type: NodePort
  stack: java-spring-boot2
  version: 1.0.0
```

* アプリ実行

デプロイが成功したら、以下コマンドでURLを確認してアクセスできるはずです。

```console
$ sudo kubectl get serving
```
と書きましたがちょっと現時点では稼働確認が取れておらず・・・。解決したら更新します・・・。

#まとめ
VS Vodeを使ってテンプレートを元に簡単にクラウドネイティブ・アプリケーションを開発できそうです。