Keycloak on K8S HA 部署

🎩 转发请标明出处

🍄 环境配置

Keycloak: 23.0.7

MySQL: 8.0

域名:test.keycloak.org

命名空间:keycloak

💫 前置准备

🍕 容器镜像

🗣 仅当需要自定义容器镜像时才需要,否则直接使用官方镜像即可

可以参考如下这个项目:

github.com/yangsijie66...

该项目实现了自定义 Keycloak IDP,用于支持钉钉登陆。

看下该项目的容器镜像打包 Dockerfile:

docker 复制代码
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml /app
COPY ./settings.xml /usr/share/maven/conf/settings.xml
RUN mvn verify --fail-never -s /usr/share/maven/conf/settings.xml
COPY . /app
RUN mvn clean install -s /usr/share/maven/conf/settings.xml

FROM quay.io/keycloak/keycloak:23.0.7 as kcbuilder

# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true

WORKDIR /opt/keycloak
COPY --from=builder --chown=keycloak:keycloak /app/target/*.jar /opt/keycloak/providers/
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:23.0.7
COPY --from=kcbuilder /opt/keycloak/ /opt/keycloak/
ENTRYPOINT [ "/opt/keycloak/bin/kc.sh" ]

其中 1 ~ 7 行是在构建插件 jar 包,不用管;

12 ~ 13 行开启 HA 环境下需要用到的 /health/ready/health/live/health/started/metrics ,分别为存活探测和暴露监控指标;

16 行将构建的 jar 包放入 Keycloak 指定目录中;

17 行执行 build 命令;

19 ~ 21 行将 build 后生成的所有文件覆盖至当前目录下,形成运行用的容器。

🤨 19 ~ 21 行为何需要多此一举,没搞懂

后续就是将容器构建,推送至镜像仓库,这里就不赘述了。

🍎 SSL 证书

🗣 有条件的可以申请现成的(腾讯云可以申请免费 SSL 证书)或直接用现有的

没有条件的,可以通过以下命令直接生成即可:

bash 复制代码
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout key.pem -out certificate.pem -subj "/CN=test.keycloak.org/O=test.keycloak.org" -addext "subjectAltName = DNS:test.keycloak.org"

这里生成了 key.pemcertificate.pem 文件。

通过以下命令,部署 SSL 证书的 TLS 类型的 Secret:

bash 复制代码
kubectl create secret -n keycloak tls keycloak-tls-secret --cert certificate.pem --key key.pem

至此,K8S 中的资源视图如下:

🌰 数据库

❗️ 生产环境下请使用独立维护的数据库

这里使用 MySQL 8.0 数据库。

这里直接在 K8S 中创建一个测试用的 MySQL 实例:

yaml 复制代码
apiVersion: v1
kind: Namespace
metadata:
  name:  mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-db
  namespace: mysql
spec:
  selector:
    matchLabels:
      app: mysql-db
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql-db
    spec:
      containers:
        - name: mysql-db
          image: mysql:8.0
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: testpassword
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-db-service
  namespace: mysql
spec:
  selector:
    app: mysql-db
  type: ClusterIP
  ports:
  - port: 3306
    targetPort: 3306

通过以下命令,部署数据库访问账号密码至 Secret 中:

bash 复制代码
kubectl create secret -n keycloak generic keycloak-db-secret \
  --from-literal=username=root \
	--from-literal=password=testpassword

至此,K8S 中的资源视图如下:

在数据库中创建名为 keycloak 的逻辑库:

bash 复制代码
mysql> create database keycloak;
Query OK, 1 row affected (0.01 sec)

🧲 域名

🗣 有条件的可以直接用现有的

没条件的,可以通过改本机的 /etc/hosts 来实现,增加如下条目:

复制代码
x.x.x.x  test.keycloak.org

🛠️ 正式部署

☁️ 部署服务发现 Service

在 K8S 环境中,Keycloak 支持通过 Service 使用 7800 接口进行服务发现,因此部署该用途的 Service。

创建的 YAML 如下:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  labels:
    app: keycloak
  name: keycloak-discovery
  namespace: keycloak
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 7800
    protocol: TCP
    targetPort: 7800
  publishNotReadyAddresses: true
  selector:
    app: keycloak
  sessionAffinity: None
  type: ClusterIP

至此,K8S 中的资源视图如下:

🌀 部署服务访问 Service

在 K8S 部署用于访问服务的 Service,这里映射 Pod 的 8443 端口。

创建的 YAML 如下:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  labels:
    app: keycloak
  name: keycloak-service
  namespace: keycloak
spec:
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: https
    port: 8443
    protocol: TCP
    targetPort: 8443
  selector:
    app: keycloak
  sessionAffinity: None
  type: ClusterIP

至此,K8S 中的资源视图如下:

🎃 部署 Ingress(Optional)

若集群中部署了 Nginx Ingress Controller,则可以创建 Ingress,后端为上一步创建的 Keycloak 的服务访问 Service keycloak-service,同时绑定 TLS 配置(之前创建的 keycloak-tls-secret)。

创建的 YAML 如下:

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: HTTPS
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
  name: keycloak-ingress
  namespace: keycloak
spec:
  ingressClassName: nginx
  rules:
  - host: test.keycloak.org
    http:
      paths:
      - backend:
          service:
            name: keycloak-service
            port:
              number: 8443
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - test.keycloak.org
    secretName: keycloak-tls-secret

查看创建结果如下:

至此,K8S 中的资源视图如下:

🌈 部署 Keycloak

开始部署 Keycloak,涉及到的核心的环境变量如下表所示:

环境变量 取值 说明
KC_HOSTNAME test.keycloak.org 访问 Keycloak 时的域名
KC_HTTP_PORT 8080 HTTP 端口
KC_HTTPS_PORT 8443 HTTPS 端口
KC_HTTPS_CERTIFICATE_FILE /mnt/certificates/tls.crt 生产环境需通过 HTTPS 访问,这里是之前部署的 SSL 证书
KC_HTTPS_CERTIFICATE_KEY_FILE /mnt/certificates/tls.key 生产环境需通过 HTTPS 访问,这里是之前部署的 SSL 证书
KC_DB mysql 数据库类型,这里用 mysql
KC_DB_URL jdbc:mysql://mysql-db-service.mysql:3306/keycloak?characterEncoding=UTF-8 数据库连接地址,这里通过之前部署的 mysql 的 Service 进行访问
KC_DB_USERNAME root mysql账号
KC_DB_PASSWORD testpassword mysql密码
KC_HEALTH_ENABLED true 开启健康检查
KC_METRICS_ENABLED true 开启监控指标
KC_CACHE ispn
KC_CACHE_STACK kubernetes
KC_PROXY passthrough
KEYCLOAK_ADMIN admin 初始admin账号
KEYCLOAK_ADMIN_PASSWORD changeme 初始admin密码
KC_TRUSTSTORE_PATHS /var/run/secrets/kubernetes.io/serviceaccount/ca.crt 用于访问 k8s 集群

部署用的 YAML 如下:

yaml 复制代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: keycloak
  name: keycloak
  namespace: keycloak
spec:
  podManagementPolicy: OrderedReady
  replicas: 3
  selector:
    matchLabels:
      app: keycloak
  serviceName: ""
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
      - args:
        - -Djgroups.dns.query=keycloak-discovery.keycloak
        - --verbose
        - start
        env:
        - name: KC_HOSTNAME
          value: test.keycloak.org
        - name: KC_FEATURES
          value: multi-site
        - name: KC_TRANSACTION_XA_ENABLED
          value: "false"
        - name: KC_HTTP_PORT
          value: "8080"
        - name: KC_HTTPS_PORT
          value: "8443"
        - name: KC_HTTPS_CERTIFICATE_FILE
          value: /mnt/certificates/tls.crt
        - name: KC_HTTPS_CERTIFICATE_KEY_FILE
          value: /mnt/certificates/tls.key
        - name: KC_DB
          value: mysql
        - name: KC_DB_USERNAME
          valueFrom:
            secretKeyRef:
              key: username
              name: keycloak-db-secret
        - name: KC_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              key: password
              name: keycloak-db-secret
        - name: KC_DB_URL
          value: jdbc:mysql://mysql-db-service.mysql:3306/keycloak?characterEncoding=UTF-8
        - name: KC_DB_POOL_INITIAL_SIZE
          value: "30"
        - name: KC_DB_POOL_MIN_SIZE
          value: "30"
        - name: KC_DB_POOL_MAX_SIZE
          value: "30"
        - name: KC_HEALTH_ENABLED
          value: "true"
        - name: KC_CACHE
          value: ispn
        - name: KC_CACHE_STACK
          value: kubernetes
        - name: KC_PROXY
          value: passthrough
        - name: KC_HTTP_MAX_QUEUED_REQUESTS
          value: "1000"
        - name: KC_LOG_CONSOLE_OUTPUT
          value: json
        - name: KC_METRICS_ENABLED
          value: "true"
        - name: KC_HTTP_POOL_MAX_THREADS
          value: "66"
        - name: KEYCLOAK_ADMIN
          value: "admin"
        - name: KEYCLOAK_ADMIN_PASSWORD
          value: "changeme"
        - name: KC_TRUSTSTORE_PATHS
          value: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        image: quay.io/keycloak/keycloak:23.0.7
        imagePullPolicy: Always
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /health/live
            port: 8443
            scheme: HTTPS
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: keycloak
        ports:
        - containerPort: 8443
          name: https
          protocol: TCP
        - containerPort: 8080
          name: http
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /health/ready
            port: 8443
            scheme: HTTPS
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          limits:
            cpu: "6"
            memory: 2250M
          requests:
            cpu: "2"
            memory: 1250M
        startupProbe:
          failureThreshold: 600
          httpGet:
            path: /health/started
            port: 8443
            scheme: HTTPS
          periodSeconds: 1
          successThreshold: 1
          timeoutSeconds: 1
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /mnt/certificates
          name: keycloak-tls-certificates
      restartPolicy: Always
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: keycloak-tls-certificates
        secret:
          defaultMode: 420
          optional: false
          secretName: keycloak-tls-secret
  updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate

至此,K8S 中的资源视图如下:

🤖 验证

根据如下步骤进行验证。

😺 映射服务至本地端口

❗️ 若能直接访问(例如使用了 LoadBalancer 或 Ingress 等方式),忽略本步骤

通过如下命令,将 Keycloak 的服务访问 Service 转发至本地:

bash 复制代码
sudo kubectl port-forward -n keycloak svc/keycloak-service 443:8443

💕 配置 DNS 解析

👀 有合法域名

🗣 注意将上文中测试用的域名 test.keycloak.org 替换为合法域名 your.domain.com

在域名服务商处,配置 DNS 解析条目即可,将合法域名 your.domain.com 解析至部署的对外暴露的服务地址(LoadBalancer 或 Ingress 等)。

🦴 无合法域名

🗣 这里用上文中的测试域名 test.keycloak.org

通过修改本机的 hosts 文件,本地解析该域名至部署的对外暴露的服务地址(LoadBalancer 或 Ingress 等)。

通过改本机的 /etc/hosts ,增加如下条目:

复制代码
x.x.x.x  test.keycloak.org

若已将服务映射至本地,则上述的 x.x.x.x 则为 127.0.0.1

💯 访问 Keycloak

在浏览器输入 https://域名 即可,若使用的是上文中的,则访问路径为 https://test.keycloak.org ,看到如下页面就成功了:

至此,访问请求链路如下图:

相关推荐
㳺三才人子2 小时前
初探 Flask
后端·python·flask·html
星栈独行2 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
开发语言·后端·rust·前端框架·开源·github·web
Java爱好狂.2 小时前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
陈随易3 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
装不满的克莱因瓶3 小时前
SpringBoot 如何将 lib 目录中jar包打包进最终的jar包里面
spring boot·后端·maven·jar·mvn
ltl4 小时前
Transformer 原论文实验结果:为什么 28.4 BLEU 足以改写路线图
后端
excel4 小时前
为什么我推荐使用 Termius:现代 SSH 工具的完整体验
前端·后端
卷毛的技术笔记5 小时前
Java后端硬核实战:用Spring AI Alibaba+Redis给LLM装上“超强记忆中枢”
java·人工智能·redis·后端·spring·ai·系统架构
IT_陈寒6 小时前
Java的Optional差点让我掉坑里,这几个坑你别踩
前端·人工智能·后端
子兮曰6 小时前
Harness 驾驭工程深度教程:从 AGENTS.md 到全链路 AI 编码基础设施
前端·后端·ai编程