将 Keycloak HA 集群部署到 kubernetes | Pods 没有发现彼此
Deploying a Keycloak HA cluster to kubernetes | Pods are not discovering each other
我正在尝试在 Kubernetes (GKE) 上部署 HA Keycloak 集群(2 个节点)。到目前为止,根据我从日志中推断出的情况,集群节点 (pods) 在所有情况下都无法相互发现。 pods 启动并且服务启动但他们看不到其他节点。
组件
- 在默认端口上使用 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
正如我们在上面的日志中看到的那样,该节点将自己视为唯一的 container/pod ID
正在尝试 KUBE_PING 协议
我尝试使用 kubernetes.KUBE_PING 协议进行发现,但它不起作用,并且向下调用 kubernetes API。日志中出现 403 授权错误(下面是其中的一部分):
Server returned HTTP response code: 403 for URL: https://[SERVER_IP]:443/api/v1/namespaces/default/pods
此时,我可以登录到门户并进行更改,但它还不是 HA 集群,因为更改没有被复制并且会话没有被保留,换句话说,如果我删除我正在使用的 pod 我被重定向到另一个新会话(好像它是一个单独的节点)
正在尝试 DNS_PING 协议
当我尝试 DNS_PING 时,情况有所不同,我没有 Kubernetes 向下 API 问题,但我无法登录。
详细地说,我能够正常访问登录页面,但是当我输入我的凭据并尝试登录该页面时尝试加载但让我回到登录页面但没有登录 pods在这方面。
以下是我在过去几天使用的一些参考资料:
- 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 清单文件
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
重要提示
- 我尝试了有和没有数据库设置的两种协议。
- 上面的 yaml 包含我一次尝试的所有发现协议组合(评论的那些)
KUBE_PING 的工作方式类似于 运行 kubectl get pods
在一个 Keycloak pod 中找到另一个 Keycloak pods' IP,然后尝试连接到它们一个。除了 Keycloak 通过直接查询 Kubernetes API 而不是 运行 kubectl
.
来做到这一点
为此,它需要凭据来查询 API,基本上是一个访问令牌。
你可以直接传递你的令牌,如果你有的话,但它不是很安全,也不是很方便(you can check other options and behavior here)。
Kubernetes 有一种非常方便的方法来注入令牌,供 pod(或该 pod 内的软件 运行)使用,以查询 API。 Check the documentation for a deeper look.
该机制是创建一个服务帐户,授予它使用 RoleBinding 调用 API 的权限,并在 pod 配置中设置该帐户。
它的工作原理是将令牌作为文件安装在已知位置,所有 Kubernetes 客户端都对其进行硬编码和期望。当客户端想要调用 API 时,它会在该位置查找令牌。
虽然不是很方便,但您可能会遇到更不方便的情况,即缺少创建 RoleBinding 的权限(在更严格的环境中比较常见)。
然后你可以要求管理员为你创建服务帐户和 RoleBinding 或者只是(非常不安全地)传递你自己的用户令牌(如果你能够在 Keycloak 的命名空间上做 kubectl get pod
你有权限)通过 SA_TOKEN_FILE
环境变量。
使用秘密或 configmap 创建文件,将其安装到 pod 并将 SA_TOKEN_FILE
设置为该文件位置。请注意,此方法特定于 Keycloak。
如果您有权在集群中创建服务帐户和角色绑定:
一个例子(未测试):
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 将在命名空间中找到所有 pods,而不仅仅是 keycloak pods,并将尝试连接到所有这些。当然,如果你的另一个pods不在乎这些,那也没关系。
用了很久,最好用JDBC_PING,在K8s环境下也适用。此过程也适用于 Keycloak 和分离的 infinispan 集群。
我建议生成一个 CLI 脚本,在启动时 运行 或使用以下环境变量。您需要一个数据库来保存数据,节点将在那里注册。它适用于所有环境。
- JGROUPS_DISCOVERY_PROTOCOL=JDBC_PING
随意使用我生成的 repo,它包含 mysql 下集群环境的全部功能
https://github.com/albertoSoto/keycloak-infinispan-cluster
我正在尝试在 Kubernetes (GKE) 上部署 HA Keycloak 集群(2 个节点)。到目前为止,根据我从日志中推断出的情况,集群节点 (pods) 在所有情况下都无法相互发现。 pods 启动并且服务启动但他们看不到其他节点。
组件
- 在默认端口上使用 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
正如我们在上面的日志中看到的那样,该节点将自己视为唯一的 container/pod ID
正在尝试 KUBE_PING 协议
我尝试使用 kubernetes.KUBE_PING 协议进行发现,但它不起作用,并且向下调用 kubernetes API。日志中出现 403 授权错误(下面是其中的一部分):
Server returned HTTP response code: 403 for URL: https://[SERVER_IP]:443/api/v1/namespaces/default/pods
此时,我可以登录到门户并进行更改,但它还不是 HA 集群,因为更改没有被复制并且会话没有被保留,换句话说,如果我删除我正在使用的 pod 我被重定向到另一个新会话(好像它是一个单独的节点)
正在尝试 DNS_PING 协议
当我尝试 DNS_PING 时,情况有所不同,我没有 Kubernetes 向下 API 问题,但我无法登录。
详细地说,我能够正常访问登录页面,但是当我输入我的凭据并尝试登录该页面时尝试加载但让我回到登录页面但没有登录 pods在这方面。
以下是我在过去几天使用的一些参考资料:
- 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 清单文件
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
重要提示
- 我尝试了有和没有数据库设置的两种协议。
- 上面的 yaml 包含我一次尝试的所有发现协议组合(评论的那些)
KUBE_PING 的工作方式类似于 运行 kubectl get pods
在一个 Keycloak pod 中找到另一个 Keycloak pods' IP,然后尝试连接到它们一个。除了 Keycloak 通过直接查询 Kubernetes API 而不是 运行 kubectl
.
为此,它需要凭据来查询 API,基本上是一个访问令牌。
你可以直接传递你的令牌,如果你有的话,但它不是很安全,也不是很方便(you can check other options and behavior here)。
Kubernetes 有一种非常方便的方法来注入令牌,供 pod(或该 pod 内的软件 运行)使用,以查询 API。 Check the documentation for a deeper look.
该机制是创建一个服务帐户,授予它使用 RoleBinding 调用 API 的权限,并在 pod 配置中设置该帐户。
它的工作原理是将令牌作为文件安装在已知位置,所有 Kubernetes 客户端都对其进行硬编码和期望。当客户端想要调用 API 时,它会在该位置查找令牌。
虽然不是很方便,但您可能会遇到更不方便的情况,即缺少创建 RoleBinding 的权限(在更严格的环境中比较常见)。
然后你可以要求管理员为你创建服务帐户和 RoleBinding 或者只是(非常不安全地)传递你自己的用户令牌(如果你能够在 Keycloak 的命名空间上做 kubectl get pod
你有权限)通过 SA_TOKEN_FILE
环境变量。
使用秘密或 configmap 创建文件,将其安装到 pod 并将 SA_TOKEN_FILE
设置为该文件位置。请注意,此方法特定于 Keycloak。
如果您有权在集群中创建服务帐户和角色绑定:
一个例子(未测试):
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 将在命名空间中找到所有 pods,而不仅仅是 keycloak pods,并将尝试连接到所有这些。当然,如果你的另一个pods不在乎这些,那也没关系。
用了很久,最好用JDBC_PING,在K8s环境下也适用。此过程也适用于 Keycloak 和分离的 infinispan 集群。
我建议生成一个 CLI 脚本,在启动时 运行 或使用以下环境变量。您需要一个数据库来保存数据,节点将在那里注册。它适用于所有环境。
- JGROUPS_DISCOVERY_PROTOCOL=JDBC_PING
随意使用我生成的 repo,它包含 mysql 下集群环境的全部功能 https://github.com/albertoSoto/keycloak-infinispan-cluster