【发布时间】:2019-12-19 09:20:50
【问题描述】:
最近,我强化了我的 Keycloak 部署,将专用 Infinispan 集群用作 remote-store,为 Keycloak 的各种缓存提供额外的持久层。更改本身进行得相当顺利,尽管在进行此更改后,由于expired_code 错误消息,我们开始看到很多登录错误:
WARN [org.keycloak.events] (default task-2007) type=LOGIN_ERROR, realmId=my-realm, clientId=null, userId=null, ipAddress=192.168.50.38, error=expired_code, restart_after_timeout=true
此错误消息通常会在很短的时间内重复数十次,并且来自同一个 IP 地址。造成这种情况的原因似乎是最终用户的浏览器在登录时无限重定向,直到浏览器本身停止循环。
我已经看到各种 GitHub 问题 (https://github.com/helm/charts/issues/8355) 也记录了这种行为,并且一致认为这是由于 Keycloak 集群无法通过 JGroups 正确发现其成员造成的。
当您考虑到某些 Keycloak 缓存分布在 standalone-ha.xml 内的默认配置中的 Keycloak 节点时,这种解释是有道理的。但是,我已将这些缓存修改为本地缓存,其中 remote-store 指向我的新 Infinispan 集群,并且我相信我对它的工作原理做出了一些不正确的假设,导致此错误开始发生。
这是我的 Keycloak 缓存的配置方式:
<subsystem xmlns="urn:jboss:domain:infinispan:7.0">
<cache-container name="keycloak" module="org.keycloak.keycloak-model-infinispan">
<transport lock-timeout="60000"/>
<local-cache name="realms">
<object-memory size="10000"/>
</local-cache>
<local-cache name="users">
<object-memory size="10000"/>
</local-cache>
<local-cache name="authorization">
<object-memory size="10000"/>
</local-cache>
<local-cache name="keys">
<object-memory size="1000"/>
<expiration max-idle="3600000"/>
</local-cache>
<local-cache name="sessions">
<remote-store cache="sessions" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<local-cache name="authenticationSessions">
<remote-store cache="authenticationSessions" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<local-cache name="offlineSessions">
<remote-store cache="offlineSessions" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<local-cache name="clientSessions">
<remote-store cache="clientSessions" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<local-cache name="offlineClientSessions">
<remote-store cache="offlineClientSessions" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<local-cache name="loginFailures">
<remote-store cache="loginFailures" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<local-cache name="actionTokens">
<remote-store cache="actionTokens" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</local-cache>
<replicated-cache name="work">
<remote-store cache="work" remote-servers="remote-cache" fetch-state="false" passivation="false" preload="false" purge="false" shared="true">
<property name="rawValues">
true
</property>
<property name="marshaller">
org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory
</property>
</remote-store>
</replicated-cache>
</cache-container>
<cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">
<transport lock-timeout="60000"/>
<replicated-cache name="default">
<transaction mode="BATCH"/>
</replicated-cache>
</cache-container>
<cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan">
<transport lock-timeout="60000"/>
<distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
</cache-container>
<cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan">
<transport lock-timeout="60000"/>
<distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
</cache-container>
<cache-container name="hibernate" module="org.infinispan.hibernate-cache">
<transport lock-timeout="60000"/>
<local-cache name="local-query">
<object-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<invalidation-cache name="entity">
<transaction mode="NON_XA"/>
<object-memory size="10000"/>
<expiration max-idle="100000"/>
</invalidation-cache>
<replicated-cache name="timestamps"/>
</cache-container>
</subsystem>
请注意,与默认的standalone-ha.xml 配置文件相比,此缓存配置的大部分内容都没有改变。我在这里所做的更改是将以下缓存更改为 local 并将它们指向我的远程 Infinispan 集群:
sessionsauthenticationSessionsofflineSessionsclientSessionsofflineClientSessionsloginFailuresactionTokenswork
这是我的remote-cache 服务器的配置:
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<!-- Default socket bindings from standalone-ha.xml are not listed here for brevity -->
<outbound-socket-binding name="remote-cache">
<remote-destination host="${env.INFINISPAN_HOST}" port="${remote.cache.port:11222}"/>
</outbound-socket-binding>
</socket-binding-group>
这是我在 Infinispan 端配置缓存的方式:
<subsystem xmlns="urn:infinispan:server:core:9.4" default-cache-container="clustered">
<cache-container name="clustered" default-cache="default">
<transport lock-timeout="60000"/>
<global-state/>
<replicated-cache-configuration name="replicated-keycloak" mode="SYNC">
<locking acquire-timeout="3000" />
</replicated-cache-configuration>
<replicated-cache name="work" configuration="replicated-keycloak"/>
<replicated-cache name="sessions" configuration="replicated-keycloak"/>
<replicated-cache name="authenticationSessions" configuration="replicated-keycloak"/>
<replicated-cache name="clientSessions" configuration="replicated-keycloak"/>
<replicated-cache name="offlineSessions" configuration="replicated-keycloak"/>
<replicated-cache name="offlineClientSessions" configuration="replicated-keycloak"/>
<replicated-cache name="actionTokens" configuration="replicated-keycloak"/>
<replicated-cache name="loginFailures" configuration="replicated-keycloak"/>
</cache-container>
</subsystem>
我相信我对本地缓存与远程存储的工作方式做出了一些错误的假设,我希望有人能够为我解决这个问题。我的目的是让 Infinispan 集群成为所有 Keycloak 缓存的真实来源。通过使每个缓存都本地化,我假设数据将通过 Infinispan 集群复制到每个 Keycloak 节点,这样对 keycloak-0 上的本地 authenticationSessions 缓存的写入将通过 Infinispan 集群同步持久化到 keycloak-1。
我认为正在发生的事情是,写入 Keycloak 上的本地缓存与将该值持久保存到远程 Infinispan 集群方面并不同步。换句话说,当对authenticationSessions缓存执行写操作时,它在等待将该值写入Infinispan集群时不会阻塞,因此在另一个Keycloak节点上立即读取该数据会导致缓存未命中,在本地和 Infinispan 集群中。
我正在寻找一些帮助来确定我当前的配置导致此问题的原因,并澄清remote-store 的行为 - 有没有办法将缓存写入由@987654348 支持的本地缓存@要同步?如果没有,有没有更好的方法来做我在这里想要完成的事情?
其他一些可能相关的细节:
- Keycloak 和 Infinispan 都部署到 Kubernetes 集群中的同一个命名空间。
- 我正在使用
KUBE_PING进行 JGroups 发现。 - 使用 Infinispan 控制台,我能够验证复制到所有 Infinispan 节点的所有缓存中是否有一定数量的条目 - 它们并非完全未使用。
- 如果我向一个 Keycloak 节点添加一个新领域,它会成功显示在其他 Keycloak 节点上,这让我相信
work缓存正在跨所有 Keycloak 节点传播。 - 如果我登录到一个 Keycloak 节点,我的会话将保留在其他 Keycloak 节点上,这使我相信会话相关的缓存正在跨所有 Keycloak 节点传播。
- 我使用 Keycloak 的粘性会话作为临时解决方案,但我相信修复这些底层缓存问题是一个更持久的解决方案。
提前致谢!
【问题讨论】:
-
嗨迈克尔,你能放 Keycloak 和 Infinispan 的日志吗?他们是否正确连接和注册?
-
嗨 @ArielCarrera,这里是 keycloak 节点之一的日志:gist.github.com/mrparkers/dd65e969b2a364aea589e7b431798d47,这里是 infinispan 节点之一的日志:gist.github.com/mrparkers/fd52fec72fda15cfdb5351b6109d9960 我几乎 100% 确信节点正在发现彼此并且正在使用远程缓存,但是我不确定
authenticatedSessions缓存是否会像我希望的那样快速复制。
标签: keycloak infinispan