株式会社 日立製作所 茂木昂士
日立グループOSS Advent Calendar 2018 1日目は、近年注目されているSingle Sign OnのOSSであるKeycloakのクラスタリングで使われているプロトコルについてです。
Keycloak の クラスタリング
Keycloak では、JGroups, Infinispan というライブラリを利用してクラスタリングを実現しています。これらのライブラリは Keycloak のベースとなる Wildfly で利用されていますが、利用される Wildfly のバージョンが上がってきていることで様々な機能が追加されています。
筆者が、Keycloak 超入門 (4) : Keycloak のクラスタ環境を構成してみよう という記事で Keycloak のクラスタ構成について解説しましたが、記事執筆時点と比べてバージョンが上がり、機能が増えてきて様々な選択肢が取れるようになっています。
今回は Keycloak のクラスタ機能のなかでも、 JGroups の Discovery プロトコルに絞って解説します。
JGroups の Discovery プロトコル
JGroups はクラスタのメンバーとなるホストを見つけるために、Discovery プロトコルというものを利用しています。JGroups では、この Discovery プロトコルとして様々なプロトコルを提供しており、環境や要件に合わせて適切なプロトコルを選ぶことができるようになっています。
ここでは、いくつかのプロトコルについて簡単に説明していきます。なお、より詳しく知りたい場合はJGroups のマニュアル (6.4 Initial membership Discovery)を参照してください。
PING
PING
は IP マルチキャストを利用してクラスタのメンバーを見つけるプロトコルです。Keycloak のデフォルトではPING
が使われています。IP マルチキャストが利用可能な環境であれば、特別な設定もなく利用することができます。ですが、IaaS 等の環境ではマルチキャストが使えない環境も多いため注意が必要です。
TCPPING
TCPPING
は TCP 通信を使ってクラスタメンバーを見つけるプロトコルです。下記のようにinitial_hosts
というパラメータで各ホストのアドレスを指定します。
<protocol type="TCPPING">
<property name="initial_hosts">192.168.10.1[7600],192.168.10.2[7600]</property>
</protocol>
クラスタのメンバーが固定されているため、クラスタホスト数の増減がある状況や、IP アドレスが変わるような環境では利用しづらいですが、それ以外の環境では設定がシンプルであるため問題が起こりづらいプロトコルです。
JDBC_PING
JDBC_PING
はデータベースを利用してクラスタのメンバーを見つけるプロトコルです。クラスタの各メンバーが自身の情報をデータベースに書き込み、その情報をもとにクラスタを構築します。
ほとんどの環境では共有のデータベースを利用するので、このプロトコルが使われることが一般的のようです。
DNS_PING
DNS_PING
は DNS の A レコードや SVC レコードを利用してクラスタのメンバーを見つけるプロトコルです。もともとは Kubernetes や OpenShift などの環境向けに作られたのですが、DNS Discovery が利用できる環境であればどこでも利用できます。
KUBE_PING
KUBE_PING
は Kubernetes の API を利用してクラスタのメンバーを見つけるプロトコルです。KUBE_PING
は JGroups Extra というプロジェクトで提供されているものですが、Keycloak では 4.4.0.Final から同梱されるようになっています。
DNS_PING
との大きな違いは、Kubernetes の API を利用するために追加の権限(View)が必要になる点です。そのため、Keycloak の公式でも特別な権限のいらないDNS_PING
の利用が推奨されています。
今回はこれらのプロトコルのうち、DNS_PING
を試してみたいと思います。
DNS_PING の設定
今回は OKD 3.11 上で設定を行います。Keycloak Docker イメージの GitHub リポジトリ(https://github.com/jboss-dockerfiles/keycloak)のopenshift-examples
というディレクトリにテンプレート用の Json ファイル(keycloak-https.json)があるのでこれを利用します。
テンプレートのデプロイ
keycloak-https.json
ファイルを取得し、下記のコマンドを実行します。
# oc new-app -p NAMESPACE=`oc project -q` -f keycloak-https.json
これで Keycloak の DeploymentConfig が作成され、Pod が作成されます。クラスタの確認をするためoc scale --replicas=3 dc keycloak
として、スケールします。
一見うまくいっているように見えますが、実はクラスタが構築されておらずそれぞれの Pod が独自に動いています。これには下記二つの理由があります。
問題 1: Headless Service が構築されていない
JGroups ドキュメントのDNS_PING
の部分に Kubernetes/OpenShift では Headless Service が必要と書かれています。Headless Serviceとは clusterIP の値を持たない Service で、名前解決をすると Service のアドレスではなく、各 Pod のアドレスが得られるというものです。ですが今回のテンプレートには Headless Service を定義している部分がありません。
問題 2: Protocol Stack が udp で動いている
Keycloak の Docker イメージでは、JGroups で利用する Protocol Stack のデフォルトが udp になっています。ですが、udp のままだとクラスタ構築時にタイムアウトしていまい Keycloak が起動しません。
これらの問題を修正していきます。
Docker イメージの作成
まずは Docker のイメージを修正していきます。Keycloak の Docker イメージは、JGROUPS_DISCOVERY_PROTOCOL
で指定した値と同名の jboss-cli ファイルがあればそれを実行し、なければdefault.cli
というファイルを実行して Discovery プロトコルの設定を行います。なので、dns_ping.cli
というファイルを作成し、下記の内容を記載します。
embed-server --server-config=standalone-ha.xml --std-out=echo
batch
/subsystem=jgroups/channel=ee:write-attribute(name="stack", value="tcp")
/subsystem=jgroups/stack=tcp/protocol=MPING:remove()
/subsystem=jgroups/stack=tcp/protocol=dns.DNS_PING:add(add-index=0, properties=$keycloak_jgroups_discovery_protocol_properties)
run-batch
stop-embedded-server
ここでは、下記の処理を指定ます。
- Protocol Stack を tcpに変更
- Discovery Protocol に dns.DNS_PING を設定
そして、上記のファイルを追加した Keycloak の Docker イメージを作成します。
FROM jboss/keycloak-openshift:latest
ADD dns_ping.cli /opt/jboss/tools/cli/jgroups/discovery/
作成した Docker イメージは OKD の Docker レジストリに Push しておきます。
Headless Service の作成
次に Headless Service を作成します。下記のようにclusterIP: None
という設定を入れて Service を作成します。
apiVersion: v1
kind: Service
metadata:
name: keycloak-discovery
spec:
clusterIP: None
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
deploymentconfig: keycloak
Service の作成が完了すると、下記のようにCLUSTER-IP
の値がNone
という値になっているkeycloak-discvery
というサービスが作成されます。
# oc get svc keycloak-discovery
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
keycloak-discovery ClusterIP None <none> 8080/TCP 2m
DeploymentConfig の修正
最後に DeploymentConfig の内容を修正していきます。修正する内容は下記の通りです。
- Pod に利用するイメージを自身で作成したものに変更
-
JGROUPS_DISCOVERY_POROTOCOL
の値をdns.DNS_PING
からdns_ping
に変更 -
JGROUPS_DISCOVERY_PROPERTIES
の値をkeycloak-discovery.keycloak.svc.cluster.local
に変更
上記の変更をが反映されると、Keycloak のクラスターが正常に動作するようになります。正常に起動していると各 Pod のログに下記のようなログが出てクラスターが構築されていることが確認できます。
INFO [org.infinispan.CLUSTER] (thread-11,ejb,keycloak-2-sgns7) ISPN000094: Received new cluster view for channel ejb: [keycloak-2-sgns7|2] (3) [keycloak-2-sgns7, keycloak-2-69ksp, keycloak-2-z7l9b]
...
これで DNS_PING の動作が確認できました。
まとめ
Keycloak や Wildfly は、Kubernetes/OpenShift 上で簡単に Cluster を組むことができるようになってきていますが、今回はまだ発展途上な感じがしました。これからどんどん使われていくことによって、よりよいものに発展していくでしょう。