Liberty の HTTP セッション・キャッシュを Hazelcast の Client-Server 構成で利用する

# 初めに
WebSphere Liberty の HTTP セッション・キャッシュを使用すると、HTTP セッション・データを JCache プロバイダーに保存することができます。これにより、Liberty 間で HTTP セッション・データを共有し、HTTP セッション・データの可用性を確保することができます。
JCache プロバイダーとしては、Infinispan や Hazelcast などの JCache (JSR 107) 仕様に準拠したものが利用できます。
構成としては、Client-Server 構成や Embedded 構成(Peer-to-Peer 構成) が一般的になります。

* Client-Server 構成
  * JCache プロバイダーを専用のサーバーや Pod で稼働させ、Liberty から利用する形態
* Embedded 構成（Peer-to-Peer 構成）
  * JCache プロバイダーを Liberty と同じ JVM 内で 稼働させ、複数の Liberty サーバー間で相互にデータを保持する形態

今回は、Kubernetes 環境で稼働する Liberty の Pod で、JCache プロバイダーとして **Hazelcast を使用**し、**Client-Server 構成**で利用してみます。

# Hazelcast の Server を準備
まずは、Hazelcast の Server (Cluster) を準備します。
今回は、Hazelcast のドキュメント「[Deploying Hazelcast Open Source on Kubernetes with Helm](https://docs.hazelcast.com/hazelcast/5.4/kubernetes/helm-hazelcast-chart)」を参照して、素の Kubernetes 環境で helm を利用して、専用のネームスペース hazelcast にデプロイしました。

デフォルトでは、メンバー数が３の Hazelcast クラスターとマネージメント・センターがデプロイされます。（マネージメント・センターは PersistentVolume を要求します。）

今回は、`--set cluster.memberCount=2,mancenter.enabled=false` を指定し、メンバー数を２に変更し、マネージメント・センターをデプロイしないようにしました。
(参照：[github - hazelcast/charts -stable/hazelcast/values.yaml](https://github.com/hazelcast/charts/blob/master/stable/hazelcast/values.yaml))

```
# helm repo add hazelcast https://hazelcast-charts.s3.amazonaws.com/
"hazelcast" has been added to your repositories

# helm repo update
Hang tight while we grab the latest from your chart repositories...
(省略)
...Successfully got an update from the "hazelcast" chart repository
Update Complete. ?Happy Helming!?

# helm install hz-cluster hazelcast/hazelcast --set cluster.memberCount=2,mancenter.enabled=false --namespace hazelcast
NAME: hz-cluster
LAST DEPLOYED: Thu Aug 15 11:17:40 2024
NAMESPACE: hazelcast
STATUS: deployed
REVISION: 1
NOTES:
** Hazelcast cluster is being deployed! **
-------------------------------------------------------------------------------
To access Hazelcast within the Kubernetes cluster:
- Use Hazelcast Client with Kubernetes Discovery Strategy. Read more at: https://github.com/hazelcast/hazelcast-kubernetes.
-------------------------------------------------------------------------------
To access Hazelcast from outside the Kubernetes cluster:
- Use Hazelcast Client with Smart Routing disabled:
  *) Forward port from POD:
     $ export POD=$(kubectl get pods --namespace hazelcast -l "app.kubernetes.io/name=hazelcast,role=hazelcast" -o jsonpath="{.items[0].metadata.name}")
     $ kubectl port-forward --namespace hazelcast $POD 5701:5701
  *) In Hazelcast Client configure:
     clientConfig.getNetworkConfig().setSmartRouting(false);
     clientConfig.getNetworkConfig().addAddress("127.0.0.1:5701");
-------------------------------------------------------------------------------
```

デプロイすると、StatefulSet と Service に加え、ClusterRole と ClusterRoleBinding も作成されます。
作成された Service は、StatefulSet 用の Headless Serivce (CLUSTER-IP が None の Service)で、DNS Lookup により Hazelcast の Pod の IP アドレスのリストが取得できるものです。

```
# kubectl get all -n hazelcast
NAME                         READY   STATUS    RESTARTS   AGE
pod/hz-cluster-hazelcast-0   1/1     Running   0          79s
pod/hz-cluster-hazelcast-1   1/1     Running   0          39s

NAME                           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/hz-cluster-hazelcast   ClusterIP   None         <none>        5701/TCP   79s

NAME                                    READY   AGE
statefulset.apps/hz-cluster-hazelcast   2/2     79s

# kubectl get clusterrole | grep hz-cluster
hz-cluster-hazelcast-hazelcast          2024-08-15T02:17:40Z

# kubectl get clusterrolebindings | grep hz-cluster
hz-cluster-hazelcast-hazelcast          ClusterRole/hz-cluster-hazelcast-hazelcast     2m25s
```

Pod が Ready になるのを待ち、ログを確認すると、２つのメンバーが相互に認識していることが確認できます。

```
# kubectl logs hz-cluster-hazelcast-0 -n hazelcast | grep Members -A3
Members {size:1, ver:1} [
        Member [10.39.0.1]:5701 - 813281ae-6004-44c4-a264-0def0a633328 this
]

--
Members {size:2, ver:2} [
        Member [10.39.0.1]:5701 - 813281ae-6004-44c4-a264-0def0a633328 this
        Member [10.42.0.2]:5701 - d51d55cd-dce4-4b74-a8cb-b168fadaccdd
]

# kubectl logs hz-cluster-hazelcast-1 -n hazelcast| grep Members -A3
Members {size:2, ver:2} [
        Member [10.39.0.1]:5701 - 813281ae-6004-44c4-a264-0def0a633328
        Member [10.42.0.2]:5701 - d51d55cd-dce4-4b74-a8cb-b168fadaccdd this
]
```

以上で、Hazelcast の Server (Cluster) の準備は完了です。

# Liberty の HTTP セッション・キャッシュを構成
続いて、Liberty の HTTP セッション・キャッシュを Hazelcast の Client として構成します。

必要な作業は以下の４つで、以前執筆した「[Liberty の HTTP セッション・キャッシュを Hazelcast の Embedded 構成で利用する](https://qiita.com/splendormy/items/c480669e06041861c16c)」とほとんど同じになります。

1. Liberty の HTTP セッション・キャッシュを構成する
1. Hazelcast の構成ファイルを準備する
1. Hazelcast の設定を Liberty の bootstrap.properties および jvm.options に を追加する
1. イメージをビルドする（Hazelcast の jar と構成ファイルを組み込む）

## Liberty の HTTP セッション・キャッシュを構成する
以下のような定義を `server.xml` に追加します。
追加する内容は、「[Liberty の HTTP セッション・キャッシュを Hazelcast の Embedded 構成で利用する](https://qiita.com/splendormy/items/c480669e06041861c16c#)」と全く同じです。説明なども、こちらの文書を参照して下さい。

```xml: HTTP セッション・キャッシュを構成
    <featureManager>
        <feature>sessionCache-1.0</feature>
    </featureManager>
    <httpSessionCache libraryRef="JCacheLib" uri="file:/opt/hazelcast/hazelcast-config.yaml"/>
    <library id="JCacheLib">
        <fileset dir="/opt/hazelcast/lib/" includes="*.jar"/>
    </library>
```

## Hazelcast の構成ファイルを準備する
次に、Hazelcast の構成ファイル `hazelcast-config.yaml` を準備します。
Hazelcast の Client として機能させますので、以下のような内容を指定します。`cluster-members` で指定しているのは、Hazelcast Server の Headless Service の FQDN です。
詳細は、「[Kubernetes Auto Discovery - Discovering Members from Hazelcast Clients](https://docs.hazelcast.com/hazelcast/latest/kubernetes/kubernetes-auto-discovery#discovering-members-from-hazelcast-clients)」を参照してください。

```yaml: hazelcast-config.yaml
hazelcast-client:
  network:
    cluster-members:
      - hz-cluster-hazelcast.hazelcast.svc.cluster.local
```

## bootstrap.properties および jvm.options に設定を追加する
Hazelcast の Client として機能させるので、`bootstrap.properties` に以下の指定を追加します。（`jvm.options` 内に `-D` オプションで指定することもできます。）
但し、デフォルトが Client なので、この指定は省略することが可能です。

```sh:bootstrap.properties
hazelcast.jcache.provider.type=client
```

Embedded 構成の場合、Java 17 以降の Java Platform Module System が強制される環境では、所定のオプションを追加しました。（詳細は、「[Liberty の HTTP セッション・キャッシュを Hazelcast の Embedded 構成で利用する](https://qiita.com/splendormy/items/c480669e06041861c16c)」を参照）

Hazelcast の Client として機能させる場合に、このオプションの指定が必要なのか分かりませんでしたが、特に警告メッセージ等が出力されていないので、このオプションは指定しないことにしました。（念のため、指定しておいても良いかもしれません。）

## イメージをビルドする
Hazelcast の jar と構成ファイルをイメージに組み込みます。
手順等は「[Liberty の HTTP セッション・キャッシュを Hazelcast の Embedded 構成で利用する](https://qiita.com/splendormy/items/c480669e06041861c16c)」と全く同じです。詳細は、こちらの文書を参照して下さい。

## Liberty の Pod を起動して動作を確認
Pod (Deployment) を起動して、正しく構成されているか確認します。
Pod 起動後に `messages.log` に以下のような出力があり、全ての Hazelcast Server がリストされていれば、まずは、正しく構成されていることが分かります。

```
Members [2] {
        Member [10.39.0.2]:5701 - 047095e2-4862-4738-ae42-9dff1024fa90
        Member [10.42.0.2]:5701 - 3d2f5c4a-2981-4827-93d9-b6e1ee36a69e
}
......
Client Connectivity [2] {
        Member [10.39.0.2]:5701 - 047095e2-4862-4738-ae42-9dff1024fa90 - connected
        Member [10.42.0.2]:5701 - 3d2f5c4a-2981-4827-93d9-b6e1ee36a69e - connected
}
```

確認結果等は省略しますが、アクセスしている Pod を強制停止したり、Pod 数の増減により割り振り先の Pod が変わったりしても、HTTP セッション・データが継続的に利用できることが確認できます。

# 終わりに
Kubernetes 環境で稼働する Liberty の Pod で、HTTP セッション・キャッシュを Hazelcast の Client-Server 構成で利用してみましたが、Embedded 構成に比べると、Liberty の構成は少し楽な印象を受けました。

Client-Server 構成にすると、Hazelcast Server に接続できない環境では Liberty が正しく起動しないなどの問題が発生します。HTTP セッション・キャッシュの定義を Kubernetes の ConfigMap で注入するなどの対応を行うことで、イメージの可搬性が向上すると考えらえます。