我正在尝试在 Kubernetes (GKE) 上部署 HA Keycloak 集群(2 个节点)。到目前为止,根据我从日志中推断出的情况,集群节点(pods)在所有情况下都无法相互发现。 Pod 启动且服务已启动但无法看到其他节点的位置。
组件
- 在默认端口上使用 clusterIP 服务部署 PostgreSQL 数据库。
- Keycloak 部署 2 个具有所需端口容器端口 8080、8443、相关 clusterIP 和 LoadBalancer 类型服务的节点以将服务公开到互联网
日志片段:
INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-4) ISPN000078: Starting JGroups channel ejb
INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-4) ISPN000094: Received new cluster view for channel ejb: [keycloak-567575d6f8-c5s42|0] (1) [keycloak-567575d6f8-c5s42]
INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-1) ISPN000094: Received new cluster view for channel ejb: [keycloak-567575d6f8-c5s42|0] (1) [keycloak-567575d6f8-c5s42]
INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-3) ISPN000094: Received new cluster view for channel ejb: [keycloak-567575d6f8-c5s42|0] (1) [keycloak-567575d6f8-c5s42]
INFO [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-4) ISPN000079: Channel ejb local address is keycloak-567575d6f8-c5s42, physical addresses are [127.0.0.1:55200]
.
.
.
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 15.0.2 (WildFly Core 15.0.1.Final) started in 67547ms - Started 692 of 978 services (686 services are lazy, passive or on-demand)
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
正如我们在上面的日志中看到的,节点将自己视为唯一的容器/pod ID
尝试 KUBE_PING 协议(protocol)
我尝试使用 kubernetes.KUBE_PING 协议(protocol)进行发现,但它没有工作,并且调用了 kubernetes 向下 API。日志中出现 403 授权错误(下面是其中的一部分):
Server returned HTTP response code: 403 for URL: https://[SERVER_IP]:443/api/v1/namespaces/default/pods
此时,我能够登录到门户并进行更改,但它还不是 HA 集群,因为更改没有被复制并且 session 没有被保留,换句话说,如果我删除了 pod我正在使用我被重定向到另一个新 session (好像它是一个单独的节点)
尝试 DNS_PING 协议(protocol)
当我尝试 DNS_PING 时,情况有所不同,我没有 Kubernetes 向下 API 问题,但我无法登录。
详细来说,我能够正常访问登录页面,但是当我输入我的凭据并尝试登录页面时尝试加载,但让我回到登录页面,在这方面 pod 中没有日志。
以下是我在过去几天使用的一些引用资料:
- https://github.com/keycloak/keycloak-containers/blob/main/server/README.md#openshift-example-with-dnsdns_ping
- https://github.com/keycloak/keycloak-containers/blob/main/server/README.md#clustering
- https://www.youtube.com/watch?v=g8LVIr8KKSA
- https://www.keycloak.org/2019/05/keycloak-cluster-setup.html
- https://www.keycloak.org/docs/latest/server_installation/#creating-a-keycloak-custom-resource-on-kubernetes
我的 Yaml list 文件
Postgresql 部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
value: "postgres"
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
---
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
Keycloak HA集群部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: jboss/keycloak
env:
- name: KEYCLOAK_USER
value: admin
- name: KEYCLOAK_PASSWORD
value: admin123
- name: DB_VENDOR
value: POSTGRES
- name: DB_ADDR
value: "postgres"
- name: DB_PORT
value: "5432"
- name: DB_USER
value: "postgres"
- name: DB_PASSWORD
value: "postgres"
- name: DB_SCHEMA
value: "public"
- name: DB_DATABASE
value: "keycloak"
# - name: JGROUPS_DISCOVERY_PROTOCOL
# value: kubernetes.KUBE_PING
# - name: JGROUPS_DISCOVERY_PROPERTIES
# value: dump_requests=true,port_range=0,namespace=default
# value: port_range=0,dump_requests=true
- name: JGROUPS_DISCOVERY_PROTOCOL
value: dns.DNS_PING
- name: JGROUPS_DISCOVERY_PROPERTIES
value: "dns_query=keycloak"
- name: CACHE_OWNERS_COUNT
value: '2'
- name: CACHE_OWNERS_AUTH_SESSIONS_COUNT
value: '2'
- name: PROXY_ADDRESS_FORWARDING
value: "true"
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
---
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
app: keycloak
---
apiVersion: v1
kind: Service
metadata:
name: keycloak-np
labels:
app: keycloak
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
app: keycloak
重要提示
- 无论是否设置数据库,我都尝试了这两种协议(protocol)。
- 上面的 yaml 包含我一次尝试过的所有发现协议(protocol)组合(评论的那些)
最佳答案
更新 2022-08-01:此配置适用于旧版 keycloak(或 16 及以下版本)。从 17 开始,Keycloak 迁移到 Quarkus 发行版,配置有所不同。
默认情况下,这些版本使用 DNS_PING 作为 JGroups 的发现机制(底层集群机制),但您仍然需要激活它。
你需要:
- 一个 headless service 指向你的 keycloak pod( headless 服务只是一个普通服务,但有
ClusterIP: none
) - env
KC_CACHE_STACK=kubernetes
(激活 kubernetes jgroup 配置)和JAVA_OPTS_APPEND=-Djgroups.dns.query=<name-of-headless-service>
(告诉它如何找到其他 keycloak pod)。
这样,在启动时,jgroups 将发出一个 dns 查询(例如:keycloak-headless.my_namespace.svc.cluster.local),响应将是与 headless 服务关联的所有 pod 的 IP。
然后 JGroups 将联系通信端口中的每个 IP 并建立集群。
KUBE_PING 的工作方式类似于在一个 Keycloak pod 中运行 kubectl get pods
以查找其他 Keycloak pod 的 IP,然后尝试一个接一个地连接到它们。除了 Keycloak 通过直接查询 Kubernetes API 而不是运行 kubectl
来做到这一点。
为此,它需要凭据来查询 API,基本上是一个访问 token 。
您可以直接传递您的 token ,如果您有它,但它不是很安全也不是很方便 (you can check other options and behavior here)。
Kubernetes 有一种非常方便的方法来注入(inject) token ,供 pod(或在该 pod 内运行的软件)使用以查询 API。 Check the documentation for a deeper look 。
该机制是创建一个服务帐户,授予它使用 RoleBinding 调用 API 的权限,并在 pod 配置中设置该帐户。
它的工作原理是将 token 作为文件安装在已知位置,硬编码并为所有 Kubernetes 客户端所期望。当客户端想要调用 API 时,它会在该位置查找 token 。
虽然不是很方便,但您可能会遇到更不方便的情况,即缺少创建 RoleBinding 的权限(在更严格的环境中有些常见)。
然后您可以要求管理员为您创建服务帐户和 RoleBinding,或者只是(非常不安全地)通过 kubectl get pod
环境变量传递您自己的用户 token (如果您能够在 Keycloak 的命名空间上执行 SA_TOKEN_FILE
,则您拥有权限) .
使用 secret 或 configmap 创建文件,将其挂载到 pod 并将 SA_TOKEN_FILE
设置为该文件位置。请注意,此方法特定于 Keycloak。
如果您确实有权在集群中创建服务帐户和角色绑定(bind):
一个例子(未测试):
export TARGET_NAMESPACE=default
# convenient method to create a service account
kubectl create serviceaccount keycloak-kubeping-service-account -n $TARGET_NAMESPACE
# No convenient method to create Role and RoleBindings
# Needed to explicitly define them.
cat <<EOF | kubectl apply -f -
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: keycloak-kubeping-pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: keycloak-kubeping-api-access
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: keycloak-kubeping-pod-reader
subjects:
- kind: ServiceAccount
name: keycloak-kubeping-service-account
namespace: $TARGET_NAMESPACE
EOF
在部署时,您设置 serviceAccount:
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
spec:
template:
spec:
serviceAccount: keycloak-kubeping-service-account
serviceAccountName: keycloak-kubeping-service-account
containers:
- name: keycloak
image: jboss/keycloak
env:
# ...
- name: JGROUPS_DISCOVERY_PROTOCOL
value: kubernetes.KUBE_PING
- name: JGROUPS_DISCOVERY_PROPERTIES
value: dump_requests=true
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
# ...
dump_requests=true
将帮助您调试 Kubernetes 请求。最好在生产环境中使用 false
。您可以使用 namespace=<yournamespace
而不是 KUBERNETES_NAMESPACE
,但这是 Pod 必须自动检测其运行所在的命名空间的便捷方式。
请注意,KUBE_PING 将找到命名空间中的所有 pod,而不仅仅是 keycloak pod,并将尝试连接到所有这些 pod。当然,如果你的其他 pod 不关心这个,也没关系。
关于kubernetes - 将 Keycloak HA 集群部署到 kubernetes | Pod 没有发现彼此,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70286956/