QuarkusアプリをIBM Cloud Kubernetes Service(IKS)で動かしてみる

# はじめに

Quarkusは、Red Hatが主導で開発するKubernetesネイティブのオープンソースJavaフレームワークです。マイクロサービスをJavaで実装する際のフレームワークとしてSpring Bootが選択肢に上がることが多いと思いますが、Quarkusはその新たな選択肢として2019年に登場しました。

Quarkusの特徴は、GraalVMのコンパイラにより、JavaのコードからLinuxネイティブのアプリケーションを生成できることです。この特徴から、省メモリで、通常のJavaアプリケーションよりも瞬時に起動するアプリケーションを実現することができます。

また、IBM Cloudでは、Quarkusが対象としているKubernetes関連のサービスとして以下のようなものを提供しています。（これら以外に、Red Hat OpenShiftのマネージドサービスもありますが、それはまた別の機会に触れたいと思います）

- IBM Cloud Kubernetes Service（IKS）：　マネージドKubernetesサービス
- IBM Cloud Container Registry　　　　：　コンテナイメージのレジストリサービス

当記事では、**Quarkusのサンプルアプリをネイティブ化し、それをコンテナ化したものをIKS上で稼働**させてみます。


# 前提事項

当記事の執筆環境は以下になります。**コマンド類はmacOSを前提**にしています。Windowsの場合は、各項に記載の参考ページを参照ください。

- マシン　：　MacBook Pro （16インチ, Late 2019）
- OS　　：　macOS Catalina　（10.15.5）


# 全体の流れ

以降、以下の流れで進めていきます。

1. 事前準備
2. Quarkusサンプルアプリの準備・ネイティブ化・コンテナイメージ化
3. IBM Cloud Container Registryの準備
4. IBM Cloud Container Registryへのコンテナイメージのpush
5. IKSクラスターの作成
6. IKSへのマニフェストファイルのapply


# 事前準備

当記事の内容を実施するにあたり、以下のツールが必要となりますのでそれぞれ導入してください。


## IBM Cloud CLI、Container Registryプラグイン、Kubernetes Serviceプラグイン

以下のコマンドにより、IBM Cloud CLIを導入してください。Container Registryプラグイン、Kubernetes Serviceプラグインも同時に導入されます。

```bash
curl -sL https://ibm.biz/idt-installer | bash
```


参考：[IBM Cloud CLIの概説](https://cloud.ibm.com/docs/cli?topic=cli-getting-started&locale=ja)


## Docker

未導入の場合、上記のIBM Cloud CLIの導入により、一緒に導入されます。

## JDK

JDK8以上を導入し、JAVA_HOMEを適切に設定してください。


## Maven

[Maven](https://maven.apache.org/)のサイトから最新のMavenを入手し、導入してください。
導入手順は[Installing Apache Maven](https://maven.apache.org/install.html)を参照ください。

Homebrewが導入済みであれば、以下のコマンドで導入できます。

```
brew install maven
```


## GraalVM

GraalVMの[Getting Started](https://www.graalvm.org/getting-started/)を参考に、GraalVMを導入します。
Homebrewが導入済みであれば、以下のコマンドで導入できます。

```
# JDK 8の場合
brew cask install graalvm/tap/graalvm-ce-java8

# JDK 11の場合
brew cask install graalvm/tap/graalvm-ce-java11
```

環境変数GRAALVM_HOMEを設定します。

```
# GraalVMのインストールディレクトリの配下の「/Contents/Home/」を設定
export GRAALVM_HOME=<GraalVMのインストールディレクトリ>/Contents/Home/
```

GraalVMのnative-image toolをインストールします。

```
${GRAALVM_HOME}/bin/gu install native-image
```

macOS Catalinaで、guコマンドでのインストールでエラー(警告)が出た場合は、以下のコマンドを実行して再度実行してください。

```
xattr -r -d com.apple.quarantine ${GRAALVM_HOME}/../..
```


参考：[QUARKUS - BUILDING A NATIVE EXECUTABLE -> Configuring GraalVM](https://quarkus.io/guides/building-native-image#configuring-graalvm)



# Quarkusサンプルアプリの準備・ネイティブ化・コンテナイメージ化

事前準備ができたので、ここからQuarkusのサンプルアプリの準備を進めていきます。
参考：[QUARKUS - CREATING YOUR FIRST APPLICATION](https://quarkus.io/guides/getting-started)

MavenでQuarkusアプリを新規作成します。

```
mvn io.quarkus:quarkus-maven-plugin:1.6.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.getting.started.GreetingResource" \
    -Dpath="/hello"
```

上記により、コマンドを実行したディレクトリ配下の「getting-started」ディレクトリに、Quarkusのサンプルアプリが作成されます。

このサンプルアプリには、「http://<ホスト名>:<ポート番号>/hello」というURLにアクセスすると「hello」という文字列を返すJAX-RSのAPIが含まれています。

以下のコマンドの実行により、サンプルアプリがコンパイルされ、Quarkusに内蔵されているサーバによりアプリが起動します。

```
# getting-startedディレクトリで実行する
./mvnw compile quarkus:dev
```

起動後、別のターミナルを起動して以下のコマンドをし、「hello」という文字列が返ることを確認してください。

```
curl -w "\n" http://localhost:8080/hello
```

確認できたら、上記の「mvnw」の実行をCtrl+Cで終了させてください。


次に、Quarkusのサンプルアプリをネイティブビルドします。
以下のコマンドを実行します。

```
./mvnw package -Pnative -Dquarkus.native.container-build=true
```

ネイティブビルドには数分程度の時間がかかるのでしばらく待ちましょう。

作成されたバイナリは64bit Linux用なのでこのままでは実行できません。
そのためDockerイメージ化し、Dockerコンテナとして実行します。

以下のコマンドを実行します。
getting-started の src/main/docker/Dockerfile.native という Dockerfile によりDockerイメージをビルドします。

```
docker build -f src/main/docker/Dockerfile.native -t quarkus-quickstart/getting-started .
```

Dockerイメージがビルドできたら、以下のコマンドでコンテナを実行します。
実行ログの「getting-started 1.0-SNAPSHOT native」という箇所から、ネイティブ化されて実行されていることが分かります。

```
❯ docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2020-08-10 05:03:56,672 INFO  [io.quarkus] (main) getting-started 1.0-SNAPSHOT native (powered by Quarkus 1.6.1.Final) started in 0.018s. Listening on: http://0.0.0.0:8080
2020-08-10 05:03:56,672 INFO  [io.quarkus] (main) Profile prod activated. 
2020-08-10 05:03:56,672 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
```

# IBM Cloud Container Registryの準備

ここからは、上記で作成したDockerイメージをpushし、IKSからpullできるようにするため、Container Registryを準備していきます。

IBM Cloud CLIで、IBM Cloudアカウントにログインします。
ユーザ・パスワードが聞かれますので入力してください。
また、地域の選択では「jp-tok」を選択してください。

```
ibmcloud login -a https://cloud.ibm.com
```

以下のコマンドを実行し、Container Registryの対象地域を設定します。

```
ibmcloud cr region-set ap-north
```

以下のコマンドを実行し、名前空間を新規に作成します。

```
ibmcloud cr namespace-add <任意の名前空間名>
```


# IBM Cloud Container Registryへのコンテナイメージのpush

IBM Cloud Container Registryにコンテナイメージをpushする前に、Container Registryにログインしておきます。

```
ibmcloud cr login
```

コンテナイメージをpushできるよう、Quarkusサンプルアプリのイメージのタグを設定します。

```
docker tag quarkus-quickstart/getting-started:latest jp.icr.io/<名前空間名>/quarkus-getting-started:1.0
```

コンテナイメージをpushします。

```
docker push jp.icr.io/<名前空間名>/quarkus-getting-started:1.0
```

正常にpushされていることを確認します。

```
❯ ibmcloud cr image-list
イメージをリストしています...

リポジトリー                                 タグ   ダイジェスト   名前空間   作成日           サイズ   セキュリティー状況   
jp.icr.io/<名前空間名>/quarkus-getting-started   1.0    a19e4a0c4950   imanishi   17 minutes ago   45 MB    24 件の問題   

OK
```


# IKSクラスターの作成

[IKSクラスター作成ページ](https://cloud.ibm.com/kubernetes/catalog/create)にアクセスします。

「プランの選択」で「無料」を選択します。
その他はデフォルトのままで、「作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/2534f329-b2f3-1448-9d99-d04bc743c7ab.png)


以下の画面の上部のクラスター名（mycluster-free）の右側にクラスター作成の進行状況が表示されるので、「正常」となるまで待ちます（クラスターの作成には数十分程度かかります）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/6ffff50a-4233-60b6-f95a-75a07f0a0c5b.png)

クラスターの作成が完了後、クラスターにkubectlでアクセスできるように設定します。

以下のコマンドを実行し、再度ログインします。
（無料のIKSクラスターはus-southに作成されるので、リージョンをus-southに変更）

```
ibmcloud login -a cloud.ibm.com -r us-south -g Default
```

上記のクラスター作成完了画面に表示されている「ibmcloud ks cluster config ...」コマンドを実行します。

```
ibmcloud ks cluster config --cluster <画面に表示されている値を入力>
```

kubeconfigにクラスターの情報が設定されるので、以下のコマンドでコンテキストを切り替えます。

```
kubectl config use-context <クラスター作成時に設定したクラスター名(デフォルトは mycluster-free)>/<ibmcloud ks cluster config 実行時の--clusterオプションの値>
```

以下のコマンドを実行し、IKSクラスターのノードが取得できることを確認します。

```
kubectl get nodes
```


# IKSへのマニフェストファイルのapply

最後に、Kubernetesのマニフェストファイルを作成し、IKSにapplyすることで、QuarkusサンプルアプリをIKSにデプロイします。

以下のコマンドを実行し、IKSのWorkerノードの「パブリックIPアドレス」を確認します。

```
ibmcloud ks worker ls --cluster <クラスター作成時に設定したクラスター名(デフォルトは mycluster-free)>
```

「quarkus-getting-started.yaml」というファイルを作成し、以下の内容をコピペします。
image で指定している <名前空間名> は、Container Registryの該当の名前空間名に置き換えてください。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: quarkus-getting-started
spec:
  replicas: 1
  selector:
    matchLabels:
      app: quarkus-getting-started
  template:
    metadata:
      labels:
        app: quarkus-getting-started
    spec:
      containers:
      - name: quarkus
        image: jp.icr.io/<名前空間名>/quarkus-getting-started:1.0
        ports:
          - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: quarkus-getting-started
  labels:
    app: quarkus-getting-started
spec:
  ports:
  - port: 8080
  selector:
    app: quarkus-getting-started
  type: NodePort
```

以下のコマンドで、上記のマニフェストファイルをapplyします。

```
kubectl apply -f quarkus-getting-started.yaml
```

以下のコマンドで、QuarkusサンプルアプリのPodが稼働している（STATUSがRunningである）ことを確認します。

```
❯ kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
quarkus-getting-started-85cf4884f-lnxg8   1/1     Running   0          13s
```

以下のコマンドを実行し、QuarkusサンプルアプリのNodePortサービスのポート番号を確認します。（下記の例では 32423）

```
❯ kubectl get service
NAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes                ClusterIP   xxx.xxx.xxx.xxx   <none>        443/TCP          167m
quarkus-getting-started   NodePort    xxx.xxx.xxx.xxx   <none>        8080:32423/TCP   24s
```

ここまでで確認したWorkerノードのパブリックIPアドレスと、NodePortのポート番号を組み合わせ、以下のcurlコマンドを実行します。

```
curl -w "\n" http://<WorkerノードのパブリックIPアドレス>:<NodePortのポート番号>/hello
```

「hello」という文字列が返れば成功です。

これでQuarkusのネイティブアプリをIKSにデプロイすることができました。