如何在服务于 Kubernetes Ingress 的 GCE L7 平衡器中获取自定义健康检查路径?

How to get a custom healthcheck path in a GCE L7 balancer serving a Kubernetes Ingress?

我正在尝试在 GCE 的 Kubernetes(服务器 1.6.4)中部署一个 grafana 实例。我正在使用以下清单:

部署 (full version):

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: grafana
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: grafana
    spec:
      initContainers:
        …                                
      containers:
        - name: grafana
          image: grafana/grafana
          readinessProbe:
            httpGet:
              path: /login
              port: 3000
          …

服务:

apiVersion: v1
kind: Service
metadata:
  name: grafana
spec:
  selector:
    name: grafana
  ports:
    - protocol: TCP
      port: 3000
  type: NodePort

入口:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: grafana
spec:
  tls:
    - secretName: grafana.example.com
  backend:
    serviceName: grafana
    servicePort: 3000

事实证明,grafana 在 / 上提供 302,但默认的 GCE 入口健康检查期望在 / 上提供 200(source). As you can see, there is a custom readinessProbe in the Deployment(第 22 行)。

一旦我post这些资源到kube-apiserver,一切都创建无误。具体来说,Ingress 获得了一个 public ip4 地址,但是 healtcheck 设置了默认的 / 路径,而不是在中配置的自定义路径readinessProbe。因此,如果我 curl Ingress 的 public ip4 地址,我会得到 502。

通过在 GCE 控制台中手动将探测路径更改为 /login 可以解决此问题。

自定义健康检查

使用 GLBC 插件

从你的问题来看不是很清楚,但是如果你使用的是 GCE Load-Balancer Controller (GLBC) Cluster Addon, you can customize the health check path

Currently, all service backends must satisfy either of the following requirements to pass the HTTP(S) health checks sent to it from the GCE loadbalancer:

  • Respond with a 200 on '/'. The content does not matter.
  • Expose an arbitrary url as a readiness probe on the pods backing the Service.

The Ingress controller looks for a compatible readiness probe first, if it finds one, it adopts it as the GCE loadbalancer's HTTP(S) health check. If there's no readiness probe, or the readiness probe requires special HTTP headers, the Ingress controller points the GCE loadbalancer's HTTP health check at '/'. This is an example of an Ingress that adopts the readiness probe from the endpoints as its health check.

GLBC 插件页面在 Limitations section:

中提到了这一点

All Kubernetes services must serve a 200 page on '/', or whatever custom value you've specified through GLBC's --health-check-path argument.

没有 GLBC 插件

如果您没有使用该插件,目前 Kubernetes 确实要求您在 / 路径上为 GET 请求服务 200 以成功进行健康检查,否则后端将无法获取任何流量。

这个 bug.

中有一些背景知识

Google 容器引擎 (GKE)

如果您使用的是 Google 容器引擎 (GKE),则相同的默认 Kubernetes 健康检查要求 apply there too

Services exposed through an Ingress must serve a response with HTTP 200 status to the GET requests on / path. This is used for health checking. If your application does not serve HTTP 200 on /, the backend will be marked unhealthy and will not get traffic.

回答你真正的问题

说了这么多,正如您 (@mmoya) 在您的回答中指出的那样,添加用于就绪探测的相同端口作为 pod 中的端口之一解决了您的问题,因为端口否则本身不会暴露在吊舱外。这导致 Kubernetes 改为依赖 / 的健康检查。

引用自here

The GLBC requires that you define the port (in your case 3000) within the Pod specification.

除了添加自定义 readinessProbe:

之外,解决方案是在 ports 中声明用于健康检查的端口
containers:
  - name: grafana
    readinessProbe:
      httpGet:
        path: /login
        port: 3000
    ports:
      - name: grafana
        containerPort: 3000
    …

推断 Ingress 创建健康检查的条件是

an Ingress backend.servicePort references a Service port, matching a Pods spec.containers[].readinessProbe.httpGet.port and the Service's targetPort references the serving Pod's containers[].spec.ports.containerPort.

2020 年年中,GKE 引入了注解和自定义资源定义 BackendConfig 来显式配置运行状况检查,请参阅 concepts/ingress#health_checks

警告:如果您再次更改 readinessProbe,在推断的健康检查中 GKE 不会保持就绪探针和健康检查同步。它只会在 Ingress(重新)创建时再次推断。

要直接编辑外部负载均衡器健康检查(对于自定义 http 路径),请使用

gcloud compute backend-services list
gcloud compute backend-services get-health BACKEND_SERVICE_NAME --global
gcloud compute health-checks describe
gcloud compute health-checks update http BACKEND_SERVICE_NAME --request-path=/api/health

这适用于 1.9。设置 httpHeaders 也消除了在 ALLOWED_HOSTS 设置中添加额外主机名的必要性。

readinessProbe:
  httpGet:
    path: /login
    port: 3000 # Must be same as containerPort
    httpHeaders:
    - name: Host
      value: yourdomain.com