GKE集群Keycloak域名访问失败,Ingress后端异常求助
问题背景
在GKE集群通过StatefulSet部署Keycloak时,使用LoadBalancer类型服务可正常通过域名访问,但改用Ingress+Headless Service配置后,域名访问提示"Safari无法找到服务器",本地端口转发则完全正常。已确认TLS证书有效。
相关配置
初始异常配置(Ingress+Headless Service)
StatefulSet配置
apiVersion: apps/v1 kind: StatefulSet metadata: name: keycloak namespace: default labels: app: keycloak spec: replicas: 1 selector: matchLabels: app: keycloak serviceName: "keycloak-headless" template: metadata: labels: app: keycloak spec: serviceAccountName: keycloak-sa containers: - name: keycloak image: quay.io/keycloak/keycloak:latest args: ["start"] ports: - containerPort: 8080 name: http - containerPort: 8443 name: https volumeMounts: - name: keycloak-tls-volume mountPath: /etc/x509/https readOnly: true env: - name: KC_BOOTSTRAP_ADMIN_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_USERNAME - name: KC_BOOTSTRAP_ADMIN_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_PASSWORD - name: KC_DB valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_DB - name: KC_DB_URL valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_DB_URL - name: KC_HOSTNAME valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HOSTNAME - name: KC_HEALTH_ENABLED valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HEALTH_ENABLED - name: KC_DB_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_USERNAME - name: KC_DB_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_PASSWORD - name: KC_HTTPS_CERTIFICATE_FILE value: "/etc/x509/https/tls.crt" - name: KC_HTTPS_CERTIFICATE_KEY_FILE value: "/etc/x509/https/tls.key" readinessProbe: httpGet: scheme: HTTPS path: /health/ready port: 9000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 livenessProbe: httpGet: scheme: HTTPS path: /health/live port: 9000 initialDelaySeconds: 60 periodSeconds: 20 timeoutSeconds: 5 resources: requests: memory: "2Gi" cpu: "1000m" limits: memory: "4Gi" cpu: "2000m" volumes: - name: keycloak-tls-volume secret: secretName: keycloak-tls tolerations: - key: "keycloak" operator: "Exists" effect: "NoSchedule"
Service配置
apiVersion: v1 kind: Service metadata: name: keycloak-service labels: app: keycloak spec: ports: - name: http port: 8080 targetPort: 8080 - name: https port: 8443 targetPort: 8443 - name: jgroup port: 7600 targetPort: 7600 selector: app: keycloak type: ClusterIP clusterIP: None
Ingress配置
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: keycloak-ingress annotations: kubernetes.io/ingress.class: "gce" networking.gke.io/enable-global-access: "true" spec: tls: - hosts: - houseofllm.com secretName: keycloak-tls rules: - host: houseofllm.com http: paths: - path: / pathType: Prefix backend: service: name: keycloak-service port: number: 8080
正常配置(LoadBalancer Service)
StatefulSet配置
apiVersion: apps/v1 kind: StatefulSet metadata: name: keycloak namespace: default labels: app: keycloak spec: replicas: 1 selector: matchLabels: app: keycloak serviceName: "keycloak-service" template: metadata: labels: app: keycloak spec: serviceAccountName: keycloak-sa securityContext: fsGroup: 1000 containers: - name: keycloak image: quay.io/keycloak/keycloak:latest args: ["start","--cache-stack=kubernetes", "--spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true"] ports: - containerPort: 8080 name: http - containerPort: 8443 name: https - name: jgroups containerPort: 7600 securityContext: runAsUser: 1000 allowPrivilegeEscalation: false capabilities: drop: - ALL volumeMounts: - name: keycloak-tls-volume mountPath: /etc/x509/https readOnly: true env: - name: KC_BOOTSTRAP_ADMIN_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_USERNAME - name: KC_BOOTSTRAP_ADMIN_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_PASSWORD - name: KC_DB valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_DB - name: KC_DB_URL valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_DB_URL - name: KC_HOSTNAME valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HOSTNAME - name: KC_HEALTH_ENABLED valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HEALTH_ENABLED - name: KC_DB_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_USERNAME - name: KC_DB_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_PASSWORD - name: KC_HTTPS_CERTIFICATE_FILE valueFrom: secretKeyRef: name: keycloak-secrets key: KC_HTTPS_CERTIFICATE_FILE - name: KC_HTTPS_CERTIFICATE_KEY_FILE valueFrom: secretKeyRef: name: keycloak-secrets key: KC_HTTPS_CERTIFICATE_KEY_FILE - name: KC_PROXY valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_PROXY - name: jgroups.dns.query valueFrom: configMapKeyRef: name: keycloak-configmap key: JGROUPS_DNS_QUERY - name: PROXY_ADDRESS_FORWARDING valueFrom: configMapKeyRef: name: keycloak-configmap key: PROXY_ADDRESS_FORWARDING - name: KC_METRICS_ENABLED valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_METRICS_ENABLED - name: KC_HTTP_ENABLED valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HTTP_ENABLED - name: KC_HTTP_RELATIVE_PATH valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HTTP_RELATIVE_PATH - name: KC_HOSTNAME_URL valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HOSTNAME_URL - name: KC_HOSTNAME_ADMIN_URL valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_HOSTNAME_URL - name: JAVA_OPTS_APPEND valueFrom: configMapKeyRef: name: keycloak-configmap key: JAVA_OPTS_APPEND - name: KC_LOG_LEVEL valueFrom: configMapKeyRef: name: keycloak-configmap key: KC_LOG_LEVEL resources: requests: memory: "2Gi" cpu: "1000m" limits: memory: "4Gi" cpu: "2000m" volumes: - name: keycloak-tls-volume secret: secretName: keycloak-tls tolerations: - key: "keycloak" operator: "Exists" effect: "NoSchedule"
Service配置
apiVersion: v1 kind: Service metadata: name: keycloak-service labels: app: keycloak spec: ports: - name: http port: 8080 targetPort: 8080 - name: https port: 8443 targetPort: 8443 - name: jgroup port: 7600 targetPort: 7600 selector: app: keycloak type: LoadBalancer
Ingress配置(备用)
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: keycloak-ingress annotations: nginx.ingress.kubernetes.io/proxy-body-size: 2500m nginx.ingress.kubernetes.io/proxy-buffer-size: 12k nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: ingressClassName: nginx rules: - host: houseofllm.com http: paths: - backend: service: name: keycloak-service port: number: 8080 path: /keycloak/(.*) pathType: Prefix
场景对比
- 正常场景:执行
kubectl port-forward keycloak-0 8443:8443 -n default后,可正常访问https://localhost:8443,功能完全正常 - 异常场景:通过域名(如
https://houseofllm.com或https://houseofllm.com:8443)访问时,提示"Safari无法找到服务器"
核心问题
- 本地端口转发正常但域名访问失败的原因是什么?
- Keycloak服务或负载均衡有哪些必须检查的特定配置?
排查思路与解决方案
1. 初始配置核心问题分析
问题1:Headless Service与GCE Ingress兼容性不足
初始Service设置为clusterIP: None(Headless Service),GCE Ingress无法正确将流量转发到Headless Service关联的Pod,因为GCE Ingress依赖Service提供的稳定端点进行流量分发,Headless Service仅用于StatefulSet的DNS解析,不具备流量转发能力。
问题2:Ingress与后端协议不匹配
Ingress配置了TLS(443端口),但将流量转发到Keycloak的8080(HTTP)端口,而Keycloak容器内已通过KC_HTTPS_CERTIFICATE_FILE启用HTTPS,导致协议不匹配,请求被拒绝。
问题3:Keycloak代理配置缺失
初始StatefulSet未配置KC_PROXY和PROXY_ADDRESS_FORWARDING环境变量,Keycloak无法识别Ingress转发的请求头(如X-Forwarded-For、X-Forwarded-Proto),导致无法生成正确的重定向URL或响应。
2. 修复步骤
步骤1:替换Headless Service为普通ClusterIP Service
修改Service配置,移除clusterIP: None,保持type: ClusterIP:
apiVersion: v1 kind: Service metadata: name: keycloak-service labels: app: keycloak spec: ports: - name: http port: 8080 targetPort: 8080 - name: https port: 8443 targetPort: 8443 - name: jgroup port: 7600 targetPort: 7600 selector: app: keycloak type: ClusterIP
步骤2:调整GCE Ingress配置适配HTTPS转发
添加networking.gke.io/app-protocols注解,指定后端端口使用HTTPS协议,并将Ingress转发到Keycloak的8443端口:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: keycloak-ingress annotations: kubernetes.io/ingress.class: "gce" networking.gke.io/enable-global-access: "true" networking.gke.io/app-protocols: '{"https":"HTTPS"}' spec: tls: - hosts: - houseofllm.com secretName: keycloak-tls rules: - host: houseofllm.com http: paths: - path: / pathType: Prefix backend: service: name: keycloak-service port: number: 8443
步骤3:补充Keycloak代理配置
在StatefulSet的env字段中添加以下配置:
- name: KC_PROXY value: "edge" - name: PROXY_ADDRESS_FORWARDING value: "true"
KC_PROXY=edge:告知Keycloak运行在反向代理之后,由代理处理TLS终止PROXY_ADDRESS_FORWARDING:启用后,Keycloak会从请求头中获取真实客户端IP和域名,生成正确的响应URL
步骤4:验证配置有效性
- 检查域名解析:执行
nslookup houseofllm.com,确认域名指向GCE Ingress的外部IP - 检查Ingress状态:执行
kubectl get ingress keycloak-ingress,确保ADDRESS字段有值且状态为READY - 检查后端健康:执行
kubectl describe ingress keycloak-ingress,查看后端服务是否处于健康状态
3. 备选方案:使用Nginx Ingress Controller
如果选择使用Nginx Ingress,需同步调整Keycloak路径配置:
修正Ingress路径与Keycloak相对路径
若要通过/keycloak路径访问,需在StatefulSet中添加KC_HTTP_RELATIVE_PATH配置:
- name: KC_HTTP_RELATIVE_PATH value: "/keycloak"
同时修改Ingress配置,移除重定向规则,匹配正确路径:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: keycloak-ingress annotations: nginx.ingress.kubernetes.io/proxy-body-size: 2500m nginx.ingress.kubernetes.io/proxy-buffer-size: 12k nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: ingressClassName: nginx tls: - hosts: - houseofll

