Red Hat OpenShift on IBM CloudでのOpen Libertyオペレーターの利用

# はじめに
AzureのOpenShift/Kubernetesサービスでは、Open Libertyオペレーターと連携してコンテナ化したアプリケーションをデプロイすることができるようです。

https://techcommunity.microsoft.com/t5/apps-on-azure/run-websphere-open-liberty-on-openshift-and-kubernetes/ba-p/2910258

この記事では、「Red Hat OpenShift on IBM Cloud(バージョン4.8.18)」でOpen Libertyオペレーターを使用してみます。
Azureのようにクラウド管理画面との連携まではできません。

https://github.com/OpenLiberty/open-liberty-operator

なお、名称こそ「Open Libertyオペレーター」ですが、WebSphere Libertyにも対応しています。
> The Open Liberty Operator can be used to deploy and manage applications running on either Open Liberty or WebSphere Liberty into Kubernetes-based platforms, such as Red Hat OpenShift. 

# 1. 事前準備
## 1.1. Open Libertyオペレーターの導入
OpenShiftコンソールからOpen Libertyオペレーターを導入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/37ec3f35-1391-4ad8-c604-43bcf4d88e88.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/6c07a2bb-af6a-1d79-5142-9afba7bfc678.png)

# 2. Open Libertyオペレーターの使用
## 2.1. コンテナイメージのビルド
OpenShiftでコンテナイメージをビルドします。結果としてイメージストリームが作成されます。

```bash:classicノード
ls -l
### 標準出力
-rw-r--r--. 1 root root      139  8月  7 10:22 Dockerfile
-rw-r--r--. 1 root root      737  9月 18 10:36 server.xml
-rw-r--r--. 1 root root 13139005  9月 18 16:40 spring-liberty-plain.war

oc new-project spring-liberty
oc new-build --name=spring-liberty --strategy=docker --binary
oc start-build spring-liberty --from-dir=. --follow
### 標準出力↓
Uploading directory "." as binary input for the build ...
.
Uploading finished
build.build.openshift.io/spring-liberty-1 started
Receiving source from STDIN as archive ...
Caching blobs under "/var/cache/blobs".

Pulling image open-liberty:21.0.0.7-full-java11-openj9 ...
･･･
STEP 1: FROM open-liberty:21.0.0.7-full-java11-openj9
STEP 2: COPY server.xml /config/
--> 1ce86755aaa
STEP 3: COPY spring-liberty-plain.war /config/apps/
--> 0ebeedd7a1d
STEP 4: USER 1001
--> 8f1a193ebf3
STEP 5: EXPOSE 9080
--> 2fcad284cea
･･･
Successfully pushed image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty@sha256:62bffa1c6839eec7c69a2c7bd187999a84597191b572
b3a4a76e4ec862ec4cc8
Push successful

oc get is
### 標準出力↓
NAME             IMAGE REPOSITORY                                                                 TAGS     UPDATED
spring-liberty   image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty   latest   25 seconds ago
```

Dockerfile等資材に関しては以下の記事を参照ください。

https://qiita.com/y-akio/items/155ae44188a6df02138c#31-%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E3%83%93%E3%83%AB%E3%83%89

## 2.2. コンテナのデプロイ(Deployment)
「OpenLibertyApplication」のマニフェストによりコンテナをデプロイします。
Open LibertyオペレーターによりDeployment、Service、Routeが作成されます。

```bash:classicノード
oc apply -f spring-liberty-deploy.yaml
oc get pod,deploy
### 標準出力↓
NAME                                  READY   STATUS      RESTARTS   AGE
pod/spring-liberty-1-build            0/1     Completed   0          8m19s
pod/spring-liberty-56c54596f5-hprkg   1/1     Running     0          46s
pod/spring-liberty-56c54596f5-ncf6w   1/1     Running     0          46s
pod/spring-liberty-56c54596f5-zr9gq   1/1     Running     0          46s

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/spring-liberty   3/3     3            3           46s

oc get svc,route
### 標準出力↓
NAME                     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/spring-liberty   ClusterIP   172.21.26.32   <none>        9080/TCP   61s

NAME                                      HOST/PORT                           PATH   SERVICES         PORT       TERMINATION   WILDCARD
route.route.openshift.io/spring-liberty   spring-liberty.apps.ocp.cloud.vpc          spring-liberty   9080-tcp                 None
```

```yaml:spring-liberty-deploy.yaml
apiVersion: openliberty.io/v1beta1
kind: OpenLibertyApplication
metadata:
  name: spring-liberty
spec:
  applicationImage: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:latest
  replicas: 3
  readinessProbe:
    httpGet:
      path: /healthz
      port: 9080
  service:
    type: ClusterIP
    port: 9080
  expose: true
  route:
    host: spring-liberty.apps.ocp.cloud.vpc
```

以下の記事では、Deployment、Service、Ingressのマニフェストを個別に用意していました。

https://qiita.com/y-akio/items/155ae44188a6df02138c#32-%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4

## 2.3. コンテナのデプロイ(StatefulSet)
「OpenLibertyApplication」のマニフェストで『storage』を指定すると、DeploymentではなくStatefulSetとしてデプロイされます。StorageClassは指定していないため、Persistent Volumeは「Red Hat OpenShift on IBM Cloud」デフォルトのStorageClassから作成されています。

```bash:classicノード
oc apply -f spring-liberty-sts.yaml
oc get pod,sts
### 標準出力↓
NAME                         READY   STATUS      RESTARTS   AGE
pod/spring-liberty-0         1/1     Running     0          5m9s
pod/spring-liberty-1         1/1     Running     0          4m19s
pod/spring-liberty-1-build   0/1     Completed   0          16m
pod/spring-liberty-2         1/1     Running     0          2m35s

NAME                              READY   AGE
statefulset.apps/spring-liberty   3/3     5m9s

oc get pv,pvc,sc
### 標準出力↓
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM
STORAGECLASS                 REASON   AGE
persistentvolume/pvc-2f92b7d3-c64f-4ae9-8804-506497a03df1   10Gi       RWO            Delete           Bound    spring-liberty/pvc-spring-liberty-2
ibmc-vpc-block-10iops-tier            2m21s
persistentvolume/pvc-b7d1512c-8fff-43b6-b0be-ca2ca2b3c083   10Gi       RWO            Delete           Bound    spring-liberty/pvc-spring-liberty-1
ibmc-vpc-block-10iops-tier            4m16s
persistentvolume/pvc-d48f7511-ebb3-49ca-9859-ecf0196b2090   10Gi       RWO            Delete           Bound    spring-liberty/pvc-spring-liberty-0
ibmc-vpc-block-10iops-tier            6m13s

NAME                                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS
  AGE
persistentvolumeclaim/pvc-spring-liberty-0   Bound    pvc-d48f7511-ebb3-49ca-9859-ecf0196b2090   10Gi       RWO            ibmc-vpc-block-10iops-tier
  6m38s
persistentvolumeclaim/pvc-spring-liberty-1   Bound    pvc-b7d1512c-8fff-43b6-b0be-ca2ca2b3c083   10Gi       RWO            ibmc-vpc-block-10iops-tier
  4m35s
persistentvolumeclaim/pvc-spring-liberty-2   Bound    pvc-2f92b7d3-c64f-4ae9-8804-506497a03df1   10Gi       RWO            ibmc-vpc-block-10iops-tier
  2m51s

NAME                                                                      PROVISIONER            RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXP
ANSION   AGE
storageclass.storage.k8s.io/ibmc-vpc-block-10iops-tier (default)          vpc.block.csi.ibm.io   Delete          Immediate              false
         21d
…
```

```yaml:spring-liberty-sts.yaml
apiVersion: openliberty.io/v1beta1
kind: OpenLibertyApplication
metadata:
  name: spring-liberty
spec:
  applicationImage: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:latest
  replicas: 3
  readinessProbe:
    httpGet:
      path: /healthz
      port: 9080
  service:
    type: ClusterIP
    port: 9080
  expose: true
  route:
    host: spring-liberty.apps.ocp.cloud.vpc
  storage:
    size: 2Gi
    mountPath: "/logs"
```

# 3. Javaダンプ
## 3.1. コンテナイメージ再作成とコンテナ再デプロイ(Deployment)
「OpenLibertyDump」のマニフェストによりJavaダンプを出力することが可能です。
DockerfileにJavaダンプ出力先ディレクトリ/serviceabilityを追加してコンテナイメージを再作成後、「OpenLibertyApplication」のマニフェストにannotationsを追加してコンテナを再デプロイしました。

※ /serviceability用PVを設定可能ですが、複数Podから読み書きできる(ReadWriteMany)必要があります。

```bash:classicノード
oc start-build spring-liberty --from-dir=. --follow
oc apply -f spring-liberty-dump.yaml
oc get pod
### 標準出力↓
NAME                     READY   STATUS      RESTARTS   AGE
spring-liberty-1-build           0/1     Completed   0          21m
spring-liberty-2-build           0/1     Completed   0          109s
spring-liberty-76f56dbc7-7w9gb   1/1     Running     0          30s
spring-liberty-76f56dbc7-9tk46   1/1     Running     0          31s
spring-liberty-76f56dbc7-gszv5   1/1     Running     0          30s
```

```Dockerfile:Dockerfile
FROM open-liberty:21.0.0.7-full-java11-openj9

COPY server.xml /config/
COPY spring-liberty-plain.war /config/apps/

USER root

RUN mkdir /serviceability \
 && chmod 775 /serviceability

USER 1001

EXPOSE 9080
```

```yaml:spring-liberty-dump.yaml
apiVersion: openliberty.io/v1beta1
kind: OpenLibertyApplication
metadata:
  name: spring-liberty
annotations:
  openliberty.io/day2operations: OpenLibertyTrace,OpenLibertyDump
spec:
  applicationImage: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:latest
  replicas: 3
  readinessProbe:
    httpGet:
      path: /healthz
      port: 9080
  service:
    type: ClusterIP
    port: 9080
  expose: true
  route:
    host: spring-liberty.apps.ocp.cloud.vpc
#  serviceability:
#    size: 2G
```

## 3.2. Javaダンプ出力
「OpenLibertyDump」のマニフェストを適用してJavaダンプを出力します。

```bash:classicノード
oc apply -f liberty-dump.yaml
oc get OpenLibertyDump
### 標準出力↓
NAME           STARTED   COMPLETED   DUMP FILE
liberty-dump   True      True        /serviceability/spring-liberty/spring-liberty-76f56dbc7-7w9gb/2021-11-23_09:55:07.zip
```

```yaml:liberty-dump.yaml
apiVersion: openliberty.io/v1beta1
kind: OpenLibertyDump
metadata:
  name: liberty-dump
spec:
  podName: spring-liberty-76f56dbc7-7w9gb
  include:
    - thread
    - heap
```

## 3.3. Javaダンプ内容確認
zipファイルを展開すると、Javaダンプ(heapdump/javacore)だけでなく、Open Libertyの構成ファイルも含まれていました。なお、「apps」ディレクトリは存在しますが、アプリケーションのクラスファイルは含まれません。

```bash:classicノード
oc cp spring-liberty-76f56dbc7-7w9gb:serviceability/spring-liberty/spring-liberty-76f56dbc7-7w9gb/2021-11-23_09:55:07.zip liberty-dump.zip
unzip liberty-dump.zip
ls -l
### 標準出力↓
合計 20172
drwxr-xr-x. 3 root root     4096 11月 23 18:45 apps
drwxr-x---. 3 root root     4096 11月 23 09:55 autopdzip
drwxr-xr-x. 4 root root     4096 11月 23 18:45 configDropins
drwxr-x---. 5 root root     4096 11月 23 09:55 dump_21.11.23_09.55.08
-rw-r-----. 1 root root 12439816 11月 23 09:55 heapdump.20211123.095508.1.0002.phd
-rw-r-----. 1 root root  1567866 11月 23 09:55 javacore.20211123.095508.1.0001.txt
-rw-r--r--. 1 root root  6609454 11月 23 18:45 liberty-dump.zip
drwxrwx---. 3 root root     4096 11月 23 09:54 logs
-rw-rw----. 1 root root       41  7月  8 19:25 server.env
-rw-------. 1 root root      737 11月 23 09:55 server.xml
drwxrwx---. 5 root root     4096 11月 23 09:54 workarea
```

# 4. Open Libertyトレース
## 4.1. トレース出力
「OpenLibertyTrace」のマニフェストにより動的にトレースを開始することも可能です。

```bash:classicノード
oc apply -f liberty-trace.yaml
oc get OpenLibertyTrace
### 標準出力↓
NAME            PODNAME                           TRACING
liberty-trace   spring-liberty-7694cd676c-74gcn   True
```

```yaml:liberty-trace.yaml
apiVersion: openliberty.io/v1beta1
kind: OpenLibertyTrace
metadata:
  name: liberty-trace
spec:
  podName: spring-liberty-76f56dbc7-7w9gb
  traceSpecification: "*=info:com.ibm.ws.webcontainer*=all"
  maxFileSize: 20
  maxFiles: 5
```

## 4.2. トレース内容確認
トレースもJavaダンプと同じディレクトリ/serviceability以下に作成されます。
トレース出力中だったためか、「oc cp」コマンド実行時に『unexpected EOF』エラーが発生しました。

```bash:classicノード
oc rsh spring-liberty-76f56dbc7-7w9gb ls -l /serviceability/spring-liberty/spring-liberty-76f56dbc7-7w9gb
### 標準出力↓
total 6912
-rw-r-----. 1 1000670000 root 6609454 Nov 23 09:55 2021-11-23_09:55:07.zip
-rw-r-----. 1 1000670000 root     682 Nov 23 09:59 messages.log
-rw-r-----. 1 1000670000 root  443086 Nov 23 10:00 trace.log

oc cp spring-liberty-76f56dbc7-7w9gb:serviceability/spring-liberty/spring-liberty-76f56dbc7-7w9gb/messages.log messages.log
oc cp spring-liberty-76f56dbc7-7w9gb:serviceability/spring-liberty/spring-liberty-76f56dbc7-7w9gb/trace.log trace.log

cat messages.log
### 標準出力
cat messages.log
********************************************************************************
product = Open Liberty 21.0.0.7 (wlp-1.0.54.cl210720210629-1900)
wlp.install.dir = /opt/ol/wlp/
server.output.dir = /opt/ol/wlp/output/defaultServer/
java.home = /opt/java/openjdk
java.version = 11.0.11
java.runtime = OpenJDK Runtime Environment (11.0.11+9)
os = Linux (3.10.0-1160.45.1.el7.x86_64; amd64) (en_US)
process = 1@172.17.57.140
********************************************************************************
[11/23/21, 9:59:43:656 GMT] 00000031 com.ibm.ws.config.xml.internal.ConfigRefresher               A CWWKG0017I: The server configuration was successfu
lly updated in 2.160 seconds.

head trace.log
### 標準出力↓
********************************************************************************
product = Open Liberty 21.0.0.7 (wlp-1.0.54.cl210720210629-1900)
wlp.install.dir = /opt/ol/wlp/
server.output.dir = /opt/ol/wlp/output/defaultServer/
java.home = /opt/java/openjdk
java.version = 11.0.11
java.runtime = OpenJDK Runtime Environment (11.0.11+9)
os = Linux (3.10.0-1160.45.1.el7.x86_64; amd64) (en_US)
process = 1@172.17.35.184
trace.specification = *=info:com.ibm.ws.webcontainer*=all
```

# 5. その他
特徴的な挙動として、コンテナイメージを再作成したときに、Open Libertyオペレーターがコンテナを自動的に再デプロイしていました。「IMAGE REFERENCE」を参照しているようです。DeploymentでもStatefulSetでも同様の挙動でした。

> "msg":"Updating status.imageReference" ～

```bash:classcノード
oc logs open-liberty-operator-67cf5884d9-lnlq2 -n openshift-operators
### 標準出力↓
{"level":"info","ts":1637661932.6835837,"logger":"controller_openlibertyapplication","msg":"Reconcile OpenLibertyApplication - starting","Request.Namespace":"spring-liberty","Request.Name":"spring-liberty"}
{"level":"info","ts":1637661932.7600996,"logger":"controller_openlibertyapplication","msg":"Updating status.imageReference","Request.Namespace":"spring-liberty","Request.Name":"spring-liberty","status.imageReference":"image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty@sha256:c0782b52a2f5b632f5f48be4c70a692c4f5825a606a90e986169f50a9c8e22fb"}
{"level":"info","ts":1637661932.8776283,"logger":"utils","msg":"Reconciled","Kind":"ServiceAccount","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661932.9335113,"logger":"utils","msg":"Reconciled","Kind":"Service","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661932.9897504,"logger":"utils","msg":"Reconciled","Kind":"Deployment","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661933.0669138,"logger":"utils","msg":"Reconciled","Kind":"Route","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661933.0883825,"logger":"controller_openlibertyapplication","msg":"Reconcile OpenLibertyApplication - completed","Request.Namespace":"spring-liberty","Request.Name":"spring-liberty"}
{"level":"info","ts":1637661933.1198273,"logger":"controller_openlibertyapplication","msg":"Reconcile OpenLibertyApplication - starting","Request.Namespace":"spring-liberty","Request.Name":"spring-liberty"}
{"level":"info","ts":1637661933.265982,"logger":"utils","msg":"Reconciled","Kind":"ServiceAccount","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661933.3053844,"logger":"utils","msg":"Reconciled","Kind":"Service","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661933.3651812,"logger":"utils","msg":"Reconciled","Kind":"Deployment","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661933.416343,"logger":"utils","msg":"Reconciled","Kind":"Route","Namespace":"spring-liberty","Name":"spring-liberty","Status":"updated"}
{"level":"info","ts":1637661933.4378707,"logger":"controller_openlibertyapplication","msg":"Reconcile OpenLibertyApplication - completed","Request.Namespace":"spring-liberty","Request.Name":"spring-liberty"}

oc get istag
### 標準出力↓
NAME                    IMAGE REFERENCE                                                                                                                                          UPDATED
spring-liberty:latest   image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty@sha256:c0782b52a2f5b632f5f48be4c70a692c4f5825a606a90e986169f50a9c8e22fb   About a minute ago
```

OpenShift独自のDeploymentConfigであれば、イメージストリーム更新をトリガーに自動更新できることは認識していました。今回、DeploymentやStatefulSetであってもオペレーターで同様の機能を実現可能なことが分かりました。ソースコードも公開されているので、そのような要件がある場合は参考になりそうです。

https://github.com/OpenLiberty/open-liberty-operator/blob/master/controllers/openlibertyapplication_controller.go

# 参考
https://github.com/OpenLiberty/open-liberty-operator/blob/master/doc/user-guide.adoc

https://www.ibm.com/support/pages/how-generate-javacoresthread-dumps-heapdumps-and-system-cores-websphere-application-server-liberty-profile