Why does a Kubernetes Ingress create *two* healthchecks by default on its Load Balancers?

1/1/2020

After I create a very basic Ingress (yaml below) with no special definition of health checks (nor any in other Kubernetes objects), the Ingress creates a GCP Load Balancer.

Why does this LB have two health checks ("backend services") defined against different nodePorts, one against root path / and one against /healthz? I would expect to see only one.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress1
spec:
  rules:
  - host: example.com
    http:
      paths:
      - backend:
          serviceName: myservice
          servicePort: 80
-- Joshua Fox
google-kubernetes-engine
health-monitoring
kubernetes

1 Answer

1/1/2020

Reason for / health check

One of the limitation of GKE ingress controller is below:

For the GKE ingress controller to use your readinessProbes as health checks, the Pods for an Ingress must exist at the time of Ingress creation. If your replicas are scaled to 0 or Pods don't exist when the ingress is created, the default health check using / applies.

Because of the above it created two health checks.

There are lot of caveats for the health-check from readiness probe to work:

  • The pod's containerPort field must be defined
  • The service's targetPort field must point to the pod port's containerPort value or name. Note that the targetPort defaults to the port value if not defined
  • The pods must exist at the time of ingress creation
  • The readiness probe must be exposed on the port matching the servicePort specified in the Ingress
  • The readiness probe cannot have special requirements like headers The probe timeouts are translated to GCE health check timeouts

Based on above it makes sense to have a default fallback health check using /

Reason for /healthz health check

As per this FAQ all GCE URL maps require at least one default backend, which handles all requests that don't match a host/path. In Ingress, the default backend is optional, since the resource is cross-platform and not all platforms require a default backend. If you don't specify one in your yaml, the GCE ingress controller will inject the default-http-backend Service that runs in the kube-system namespace as the default backend for the GCE HTTP lb allocated for that Ingress resource.

Some caveats concerning the default backend:

  • It is the only Backend Service that doesn't directly map to a user specified NodePort Service
  • It's created when the first Ingress is created, and deleted when the last Ingress is deleted, since we don't want to waste quota if the user is not going to need L7 loadbalancing through Ingress
  • It has a HTTP health check pointing at /healthz, not the default /, because / serves a 404 by design

So GKE ingress deploys a default backend service using below yaml and setup a /healthz healthcheck for that service.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: l7-default-backend
  namespace: kube-system
  labels:
    k8s-app: glbc
    kubernetes.io/name: "GLBC"
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: glbc
  template:
    metadata:
      labels:
        k8s-app: glbc
        name: glbc
    spec:
      containers:
      - name: default-http-backend
        # Any image is permissible as long as:
        # 1. It serves a 404 page at /
        # 2. It serves 200 on a /healthz endpoint
        image: k8s.gcr.io/defaultbackend-amd64:1.5
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
  # This must match the --default-backend-service argument of the l7 lb
  # controller and is required because GCE mandates a default backend.
  name: default-http-backend
  namespace: kube-system
  labels:
    k8s-app: glbc
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "GLBCDefaultBackend"
spec:
  # The default backend must be of type NodePort.
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    k8s-app: glbc
-- Arghya Sadhu
Source: StackOverflow