将 Keycloak HA 集群部署到 kubernetes | Pods 没有发现彼此

Deploying a Keycloak HA cluster to kubernetes | Pods are not discovering each other

我正在尝试在 Kubernetes (GKE) 上部署 HA Keycloak 集群(2 个节点)。到目前为止,根据我从日志中推断出的情况,集群节点 (pods) 在所有情况下都无法相互发现。 pods 启动并且服务启动但他们看不到其他节点。

组件

日志片段:

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在这方面。

以下是我在过去几天使用的一些参考资料:

我的 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

重要提示

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 集群。

可以在此处找到基本方法 https://github.com/thomasdarimont/keycloak-project-example/blob/main/deployments/local/cluster/haproxy-database-ispn/cli/0300-onstart-setup-ispn-jdbc-store.cli

我建议生成一个 CLI 脚本,在启动时 运行 或使用以下环境变量。您需要一个数据库来保存数据,节点将在那里注册。它适用于所有环境。

  • JGROUPS_DISCOVERY_PROTOCOL=JDBC_PING

随意使用我生成的 repo,它包含 mysql 下集群环境的全部功能 https://github.com/albertoSoto/keycloak-infinispan-cluster