Keycloak 集群设置

2019年5月10日 作者:张立强 liqiang@fit2cloud.com

这篇文章分享了一些在各种场景下(例如跨数据中心、Docker跨主机、Kubernetes)搭建 Keycloak 集群的解决方案。

如果你想搭建 Keycloak 集群,这篇博客可能会给你一些参考。

根据指南,两个 cli 脚本文件已添加到 Keycloak 镜像 中。

Dockerfile 如下所示,这两个文件是这篇博客最重要的内容,你可以从 TCPPING.cliJDBC_PING.cli 中找到它们。

FROM jboss/keycloak:latest

ADD cli/TCPPING.cli /opt/jboss/tools/cli/jgroups/discovery/
ADD cli/JDBC_PING.cli /opt/jboss/tools/cli/jgroups/discovery/

首先,我们应该知道,对于 Keycloak 集群,所有 Keycloak 实例应该使用相同的数据库,这非常简单。另一件事是关于缓存(通常 Keycloak 中有两种缓存,第一种是从数据库读取的持久性数据缓存,旨在提高性能,例如 realm/client/user;第二种是非持久性数据缓存,例如 sessions/clientSessions,第二种对于集群来说非常重要),缓存的配置稍微复杂一些,我们必须确保集群视图中缓存的一致性。

总共有 3 种集群解决方案,所有这些解决方案都基于 JGroups 的发现协议(Keycloak 使用 Infinispan 缓存,而 Infinispan 使用 JGroups 来发现节点)。

1. PING

PING 是 Keycloak 默认启用的集群解决方案,它使用 UDP 协议,你不需要为此做任何配置。

但是 PING 仅在启用多播网络且端口 55200 应该暴露时可用,例如裸机、虚拟机、同一主机中的 Docker 容器。

我们在同一主机中的两个 Keycloak 容器中对此进行了测试。

日志显示,两个 Keycloak 实例相互发现并形成了集群。

2. TCPPING

TCPPING 使用 TCP 协议和 7600 端口。这可以在多播不可用时使用,例如跨数据中心的部署、跨主机的容器。

我们在两个跨主机的 Keycloak 容器中对此进行了测试。

在此解决方案中,我们需要为容器设置以下三个环境变量。

#IP address of this host, please make sure this IP can be accessed by the other Keycloak instances
JGROUPS_DISCOVERY_EXTERNAL_IP=172.21.48.39
#protocol
JGROUPS_DISCOVERY_PROTOCOL=TCPPING
#IP and Port of all host
JGROUPS_DISCOVERY_PROPERTIES=initial_hosts="172.21.48.4[7600],172.21.48.39[7600]"

日志显示,两个 Keycloak 实例相互发现并形成了集群。

3. JDBC_PING

JDBC_PING 使用 TCP 协议和 7600 端口,这与 TCPPING 类似,但它们之间的区别在于,TCPPING 需要你配置所有实例的 IP 和端口,而对于 JDBC_PING,你只需要配置当前实例的 IP 和端口,这是因为在 JDBC_PING 解决方案中,每个实例都会将其自身的信息插入到数据库中,并且实例通过从数据库读取的 ping 数据来发现对等节点。

我们在两个跨主机的 Keycloak 容器中对此进行了测试。

在此解决方案中,我们需要为容器设置以下两个环境变量。

#IP address of this host, please make sure this IP can be accessed by the other Keycloak instances
JGROUPS_DISCOVERY_EXTERNAL_IP=172.21.48.39
#protocol
JGROUPS_DISCOVERY_PROTOCOL=JDBC_PING

所有实例的 ping 数据在实例启动后已被保存在数据库中。

日志显示,两个 Keycloak 实例相互发现并形成了集群。

还有一件事

上述解决方案适用于大多数场景,但对于其他一些场景仍然不够,例如 Kubernetes。

Kubernetes 上的典型部署是一个 Deployment/ReplicateSet/StatefulSet 包含多个 Keycloak Pod,这些 Pod 非常动态,因为它们可以向上和向下扩展或故障转移到另一个节点,这要求集群能够发现和移除这些动态成员。

在 Kubernetes 上,我们可以使用 DNS_PINGKUBE_PING,它们在 实践 中效果很好。

除了 DNS_PING 和 KUBE_PING 之外,JDBC_PING 是 Kubernetes 的另一个选择。

在 Kubernetes 上,多播仅适用于同一节点中的容器,并且 pod 没有可用于配置 TCPPING 或 JDBC_PING 的静态 IP。但是在上面提到的 JDBC_PING.cli 中,我们已经处理了这个问题,如果你不设置 JGROUPS_DISCOVERY_EXTERNAL_IP 环境变量,将使用 pod IP,这意味着在 Kubernetes 上,你只需设置 JGROUPS_DISCOVERY_PROTOCOL=JDBC_PING,你的 Keycloak 集群就可以了。

讨论

建议和意见可以通过 Keycloak 用户邮件列表此 GitHub 仓库 进行讨论。