GKE集群中GCE健康检查与Ingress Nginx Controller部署异常求助
这个问题在GKE环境里用Deployment模式部署ingress-nginx控制器时非常典型,根源在于GCE负载均衡默认会对集群所有节点的NodePort执行健康检查,但你的Deployment只在部分节点上运行Pod,没有Pod的节点没有进程监听那个NodePort,自然会返回503,导致LB把这些节点标记为不健康。
下面是几个不用改成DaemonSet就能解决的方案,按推荐度排序:
方案一:用BackendConfig自定义健康检查(推荐,保留源IP)
GKE提供了BackendConfig资源,可以直接配置GCE LB的健康检查规则,让它跳过节点的NodePort,直接通过Service转发到ingress-nginx Pod的健康端点。
步骤:
- 创建BackendConfig资源,指定健康检查的目标端口和路径(ingress-nginx默认健康端口是8080,路径
/healthz):
apiVersion: cloud.google.com/v1 kind: BackendConfig metadata: name: ingress-nginx-healthcheck spec: healthCheck: checkIntervalSec: 5 port: 8080 type: HTTP requestPath: /healthz
执行kubectl apply -f <filename>.yaml创建这个资源。
- 修改ingress-nginx的Service,添加annotation关联刚才的BackendConfig:
找到你的ingress-nginx控制器的Service(通常叫ingress-nginx-controller),编辑它的配置:
apiVersion: v1 kind: Service metadata: name: ingress-nginx-controller annotations: # 关联BackendConfig,"default"表示对所有端口生效 cloud.google.com/backend-config: '{"default": "ingress-nginx-healthcheck"}' spec: type: NodePort ports: - name: http port: 80 targetPort: http nodePort: 32203 # 你的NodePort - name: https port: 443 targetPort: https selector: # 对应你的ingress-nginx Pod的标签 app.kubernetes.io/name: ingress-nginx app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/component: controller
执行kubectl apply -f <service-file>.yaml更新服务。
原理:
配置后,GCE LB的健康检查请求会先打到Service的ClusterIP,再由kube-proxy转发到任意一个运行中的ingress-nginx Pod,不管当前节点有没有Pod,都会返回200,健康检查自然就正常了。而且这个方案不会影响源IP的保留(如果你的Service之前设置了externalTrafficPolicy: Local)。
方案二:修改Service的externalTrafficPolicy为Cluster
如果不需要保留客户端源IP,这个方案更简单:
步骤:
编辑ingress-nginx的Service,把externalTrafficPolicy改成Cluster:
spec: externalTrafficPolicy: Cluster # 其他配置保持不变
执行kubectl apply -f <service-file>.yaml更新。
原理:
默认如果是Local模式,kube-proxy只会把节点NodePort的流量转发到本地节点的Pod,健康检查请求到无Pod的节点时就会失败。改成Cluster模式后,kube-proxy会把所有节点的NodePort流量转发到集群中任意一个运行的Pod,所以即使节点上没有Pod,访问localhost:32203/healthz也会被转发到有Pod的节点,返回200,健康检查就通过了。
缺点:
这个模式会丢失客户端的真实源IP,因为流量经过kube-proxy转发后,源IP会变成节点的IP,而不是客户端的IP。如果你的业务需要获取客户端真实IP,不建议用这个方案。
方案三:节点亲和性+PodDisruptionBudget(不推荐)
这个方案本质是让Deployment的Pod尽量分布到更多节点,但和DaemonSet的思路类似,只是可以控制副本数,比如你有3个节点就部署3个副本,让每个节点都有一个Pod。但这违背了你“避免资源浪费”的初衷,所以只作为补充选项,不推荐使用。
内容的提问来源于stack exchange,提问作者Diego

