クラスタリング
クラスタリングは、共有リポジトリに対する複数ノード構成を可能にします。コンテンツ(データベース)とブロブストレージを共有しつつ、各ノードはローカルの検索インデックス・一時領域を持ちます。ノード間の協調はデータベースのリース(lock)と、トランザクションを再生するジャーナルで行われます。
構成
リポジトリ
# <repository>/etc/repository.yml
cluster:
enabled: true
nodeId: node-1 # 任意。ノードごとに一意
ノードごとに環境変数 CMS_CLUSTER_NODE_ID、またはフレームワークプロパティ org.mintjams.jcr.cluster.nodeId / org.mintjams.jcr.cluster.enabled で上書きできます。nodeId 省略時はホスト名(取得できなければランダム)。
ワークスペース(共有ストレージ)
# <workspace>/etc/jcr/jcr.yml
datasource:
jdbcURL: jdbc:postgresql://db:5432/jcr_\${workspace.name}
username: jcr
password: secret
driverClassName: org.postgresql.Driver
blobstore:
type: fs
directory: /mnt/shared/cms/blobs/\${workspace.name}
search:
indexPath: /var/lib/cms/search/\${workspace.name} # ノードローカルの高速ストレージ
\${repository.home}、\${workspace.name}、\${cluster.nodeId} などの変数置換に対応します。検索インデックスはノードごとに保持され、空であればコンテンツから自動再構築されます。
永続状態の置き場所
| 状態 | スタンドアロン(既定) | クラスター |
|---|---|---|
| コンテンツ・ACL・ジャーナル | 埋め込み H2 | 共有 DB(PostgreSQL 等)。ワークスペースごとに DB |
| ブロブ(バイナリ) | ローカルファイル | 共有ストレージ(NFS 等) |
| 全文検索インデックス | ローカル | ノードローカル |
全ノードで一致させる必要があるファイル
次の「アイデンティティファイル」は、全ノードで同一でなければなりません(初回起動時に自動生成。2 台目以降は再生成せず、1 台目からコピー)。
secrets/secret-key.yml(保存秘密の暗号鍵)etc/boot.id(リポジトリ識別子。マスク値の鍵導出に使用)etc/idp-keystore.p12/etc/sp-keystore.p12(SAML の鍵)etc/idp.yml/etc/saml2.yml
推奨は、リポジトリディレクトリを共有ストレージに置くことです(
etc/とsecrets/が自動的に共有されます)。一時ディレクトリ(tmp/)は起動時に消去されるため、クラスターでは自動的にtmp/nodes/<nodeId>を使い、共有してはいけません。
ジャーナルと協調
各トランザクションはジャーナルに記録され、各ノードのポーラー(2 秒間隔)が他ノードのトランザクションを再生します。これによりキャッシュ無効化・インデックス更新・OSGi イベント(Camel ルートの再デプロイ、CMS イベント、SSE/GraphQL 購読)がクラスター対応になります。
協調用テーブルが自動作成されます。
jcr_cluster_nodes— ノード登録。30 秒ごとにlast_heartbeatを更新jcr_cluster_locks— リースロック(TTL 付き。クラッシュしても無期限にブロックしない)jcr_cluster_signals— 短命な制御通知のためのシグナルバス
ワークスペース起動・ブロブ掃除・コンテンツデプロイなどの単一ノードで行うべき作業は、リースで直列化されます。
手順(概要)
- ワークスペースごとに PostgreSQL の DB を用意(BPM を使うなら BPM 用も)
- PostgreSQL JDBC ドライバのバンドルを Felix に導入
- リポジトリディレクトリを共有ストレージへ(最低でも
blobstore.directoryを全ノードで共有) - 各ワークスペースの
jcr.yml#datasource(必要ならbpm.yml#jdbcURL)を全ノードで同一に設定 - アイデンティティファイルを全ノードで共有(初回はまず 1 台だけ起動)
cluster.enabledを有効化し、各ノードに一意なnodeIdを付与。ノードの時計を NTP で同期- ロードバランサーの背後に配置(スティッキーセッション推奨)
アプリからの協調
スクリプト API で、特定の処理をクラスター内の 1 ノードだけで実行できます。
def lease = cluster.tryLock("nightly-report", 600000)
if (lease != null) {
try {
// ... 1 ノードでのみ実行 ...
} finally {
lease.close()
}
}
cluster.isClusterEnabled()、cluster.nodeId、cluster.listMembers() も利用できます。スタンドアロンでは即座にロックが付与され、同じコードがそのまま動きます。
監視
GraphQL の cluster クエリ(管理者)や、Dashboard の Operations セクションのクラスターカードで、各ノードのハートビート(死活)を確認できます。3 間隔(約 90 秒)応答がないノードは警告としてログに記録されます。
注意点
- 時計のずれは安定化ウィンドウ(10 秒)を壊します。NTP 同期は必須です。
- 外部 DB・ブロブストレージは自動管理されません。ワークスペース削除後の DB/ブロブの後始末や、同名再作成前の DB クリアは手動です。
- 検索インデックスはノードごとで複製されません(空なら自動再構築)。