Keycloak on K8S HA 部署

本文将从 0 到 1 手把手教你如何不依赖 Operator、Helm,纯手动部署 Keycloak HA 至 kubernetes 集群

🎩 转发请标明出处

🍄 环境配置

Keycloak: 23.0.7

MySQL: 8.0

域名:test.keycloak.org

命名空间:keycloak

经测试,本文适用于 Keycloak 23.0.3 ~ 24.0.3 的所有版本,将下文中的容器镜像版本进行替换即可。
差异不大的版本,可以自行测试验证。

💫 前置准备

🍕 容器镜像

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

可以参考如下这个项目:

https://github.com/yangsijie666/keycloak-dingtalk-social-identity-provider

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

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

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 证书)或直接用现有的

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

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:

kubectl create secret -n keycloak tls keycloak-tls-secret --cert certificate.pem --key key.pem

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

🌰 数据库

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

这里使用 MySQL 8.0 数据库。

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

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 中:

kubectl create secret -n keycloak generic keycloak-db-secret \
  --from-literal=username=root \
	--from-literal=password=testpassword

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

在这里插入图片描述

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

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 如下:

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 如下:

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 如下:

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_HOSTNAMEtest.keycloak.org访问 Keycloak 时的域名
KC_HTTP_PORT8080HTTP 端口
KC_HTTPS_PORT8443HTTPS 端口
KC_HTTPS_CERTIFICATE_FILE/mnt/certificates/tls.crt生产环境需通过 HTTPS 访问,这里是之前部署的 SSL 证书
KC_HTTPS_CERTIFICATE_KEY_FILE/mnt/certificates/tls.key生产环境需通过 HTTPS 访问,这里是之前部署的 SSL 证书
KC_DBmysql数据库类型,这里用 mysql
KC_DB_URLjdbc:mysql://mysql-db-service.mysql:3306/keycloak?characterEncoding=UTF-8数据库连接地址,这里通过之前部署的 mysql 的 Service 进行访问
KC_DB_USERNAMErootmysql账号
KC_DB_PASSWORDtestpasswordmysql密码
KC_HEALTH_ENABLEDtrue开启健康检查
KC_METRICS_ENABLEDtrue开启监控指标
KC_CACHEispn
KC_CACHE_STACKkubernetes
KC_PROXYpassthrough
KEYCLOAK_ADMINadmin初始admin账号
KEYCLOAK_ADMIN_PASSWORDchangeme初始admin密码
KC_TRUSTSTORE_PATHS/var/run/secrets/kubernetes.io/serviceaccount/ca.crt用于访问 k8s 集群

部署用的 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 转发至本地:

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 ,看到如下页面就成功了:

image.png

image.png

image.png

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

image.png

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值