Kubernetes Readiness Probes - Examples & Common Pitfalls

Levent Ogut
Jan 11, 2021
8 Minute Read

Kubernetes is a great platform to deploy our microservices and applications to. One of the excellent features is that pods are restarted or removed from a service when they don’t work correctly. Kubernetes needs our help to understand if a pod is working or not. This is configured via Container Probes.

Any application can be in an unhealthy state due to bugs, missing dependencies on external resources, and so on. To make sure our traffic flows correctly, we need to make sure probes are configured.

#Probes

Probes are kubelet’s answer to the health checks, there are three handlers:

  • ExecAction: Command execution check, if the command’s exit status is 0, it is considered a success.
  • TCPSocketAction: TCP check to determine if the port is open, if open, it is considered a success.
  • HTTPGetAction: HTTP check to determine if the status code is equal to or above 200 and below 400.

Each type of probe has common configurable fields:

  • initialDelaySeconds: Probes start running after initialDelaySeconds after container is started (default: 0)
  • periodSeconds: How often probe should run (default: 10)
  • timeoutSeconds: Probe timeout (default: 1)
  • successThreshold: Required number of successful probes to mark container healthy/ready (default: 1)
  • failureThreshold: When a probe fails, it will try failureThreshold times before deeming unhealthy/not ready (default: 3)

These parameters need to be configured per your application’s spec.

#Exec Probe

Exec action has only one field, and that is command. Exit status of the command is checked, and the status of zero (0) means it is healthy, and other value means it is unhealthy.

        readinessProbe:
          initialDelaySeconds: 1
          periodSeconds: 5
          timeoutSeconds: 1
          successThreshold: 1
          failureThreshold: 1
          exec:
            command:
            - cat
            - /etc/nginx/nginx.conf

#TCP Probe

We need to define host and port parameters, host parameter defaults to the cluster-internal pod IP.

        readinessProbe:
          initialDelaySeconds: 1
          periodSeconds: 5
          timeoutSeconds: 1
          successThreshold: 1
          failureThreshold: 1
          tcpSocket:
            host:
            port: 80

#HTTP Probe

HTTP Probe has additional options to configure.

  • host: Host/IP to connect to (default: pod IP)
  • scheme: Scheme to use when making the request (default: HTTP)
  • path: Path
  • httpHeaders: An array of headers defined as header/value map
  • port: Port to connect to

Tip: If you need to set the Host header of the HTTP, please do so on httpHeaders, instead of the host parameter.

        readinessProbe:
          initialDelaySeconds: 1
          periodSeconds: 2
          timeoutSeconds: 1
          successThreshold: 1
          failureThreshold: 1
          httpGet:
            host:
            scheme: HTTP
            path: /
            httpHeaders:
            - name: Host
              value: myapplication1.com
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

#Readiness Probes in Kubernetes

Kubernetes runs readiness probes to understand when it can send traffic to a pod, i.e., to transition the pod to Ready state. For example, if a pod is used as a backend endpoint for a service, a readiness probe will determine if the pod will receive traffic or not. The readiness probe is executed throughout the pod’s lifetime; this means that readiness probes continue to be executed after reaching the Ready state. Also, our application can make itself unavailable for maintenance or for some background tasks as well by responding with different values to the probe.

A pod transitions the following states:

  • PodScheduled: Pod is scheduled on an available node
  • ContainersReady: All containers in Ready state
  • Initialized: All init containers are started
  • Ready: Pod is ready to receive traffic (readiness probe)

#Common Pitfalls for Readiness Probes

Readiness probes check the configured action for the duration of the container’s lifetime; therefore, any disruption or delay in answering can cause service disruption. Sometimes the application needs to read a large amount of data experiencing latency issues or make an expensive calculation, which might prevent such an answer from being delivered. This behavior should be considered when configuring readiness probes. Testing thoroughly before going live with a readiness probe is highly recommended.

#Cascading Failures

Consider this, we have configured a readiness probe with HTTPGet, and our application checks the database connection before answering the probe. Let’s suggest there is a database problem; in this case, all pods will be unreachable unless the database is back. Do we want this behavior? Certainly no, we could feed the database error to the front end and notify the user of the unavailability; even if we increase failureThreshold we might not be able to avoid a cascading failure. Avoiding external dependencies on readiness probe answers is what I would recommend in any case.

#External Dependencies

Let’s assume that you have a key/value store used for caching, and your readiness probe endpoint checks this connection as well. The application is capable of running without the key/value store as it will only slow down but it will run a full database query every time it needs to access data. In this case, if the readiness probe fails because of the key/value store not being available, your whole application will be down for some time -until you fix the key/value store which is certainly not desirable.

You need to clearly design your probes considering how your application behaves and how the application responds when there is a failure. You might want to restart the pod (via liveness checks) or remove it from service with a readiness probe.

#Example: Sample Nginx Deployment

To see readiness probes in action, we can configure a sample app, a simple NGINX web server in this case. Here we have defined a simple deployment configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-probes
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        readinessProbe:
          initialDelaySeconds: 1
          periodSeconds: 2
          timeoutSeconds: 1
          successThreshold: 1
          failureThreshold: 1
          httpGet:
            host:
            scheme: HTTP
            path: /
            httpHeaders:
            - name: Host
              value: myapplication1.com
            port: 80

Save this configuration to a file called k8s-probes-deployment.yaml and apply it with kubectl apply -f k8s-probes-deployment.yaml.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  ports:
  - name: nginx-http-port
    port: 80
  selector:
    app: nginx
  sessionAffinity: None
  type: NodePort

Save this configuration to a file called k8s-probes-svc.yaml and apply it with kubectl apply -f k8s-probes-svc.yaml.

There is no separate endpoint for readiness probes, but we can access events using the kubectl describe pods <POD_NAME> command, for example, to get the current status.

Use kubectl get pods command to see the pods' status.

kubectl get pods

Pods and their status and ready states will be displayed, our pod is running as expected.

NAME                          READY   STATUS    RESTARTS   AGE
k8s-probes-7d57f897dd-td2q2   1/1     Running   0          1m23s

With kubectl describe pod <POD_NAME>

kubectl describe pod k8s-probes-755b4bf57c-jgcqw

At the bottom of the output Events will be displayed:

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  15m   default-scheduler  Successfully assigned default/k8s-probes-755b4bf57c-jgcqw to k8s-probes
  Normal  Pulling    15m   kubelet            Pulling image "nginx"
  Normal  Pulled     15m   kubelet            Successfully pulled image "nginx" in 1.418647753s
  Normal  Created    15m   kubelet            Created container nginx
  Normal  Started    15m   kubelet            Started container nginx

Let’s look at the endpoints with kubectl get endpoints command.

kubectl get endpoints

We can see Nginx service has an endpoint.

NAME         ENDPOINTS           AGE
kubernetes   10.132.0.42:16443   8h
nginx        10.1.85.203:80      6m11s

To see more detail we can use kubectl describe endpoints nginx command.

Name:         nginx
Namespace:    default
Labels:       app=nginx
Annotations:  endpoints.kubernetes.io/last-change-trigger-time: 2020-11-13T20:34:13Z
Subsets:
  Addresses:          10.1.85.203
  NotReadyAddresses:  <none>
  Ports:
    Name             Port  Protocol
    ----             ----  --------
    nginx-http-port  80    TCP

Configure the readiness probe’s port parameter to 81 and apply the configuration.

Again let’s check the pods status

kubectl get pods

As we have updated our deployment by default, a new pod is created, and as you can see, it is not in the ready state.

NAME                          READY   STATUS    RESTARTS   AGE
k8s-probes-755b4bf57c-jgcqw   1/1     Running   0          13m
k8s-probes-7d57f897dd-td2q2   0/1     Running   0          2m49s

Here status is running however READY state determines our pod is not ready yet. Let’s explore the status and events with kubectl describe pods <POD_NAME> command.

kubectl describe pod k8s-probes-7d57f897dd-td2q2

We can see that the readiness probe failed due to the connection being refused therefore pod will not receive any traffic.

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  58s                default-scheduler  Successfully assigned default/k8s-probes-7d57f897dd-td2q2 to k8s-probes
  Normal   Pulling    57s                kubelet            Pulling image "nginx"
  Normal   Pulled     56s                kubelet            Successfully pulled image "nginx" in 1.211289526s
  Normal   Created    56s                kubelet            Created container nginx
  Normal   Started    56s                kubelet            Started container nginx
  Warning  Unhealthy  4s (x10 over 49s)  kubelet            Readiness probe failed: Get "http://10.1.85.202:81/": dial tcp 10.1.85.202:81: connect: connection refused

Let’s check the status directly:

kubectl get pods k8s-probes-7d57f897dd-td2q2 -o jsonpath='{.status.containerStatuses[0].state}'

The state is “running”.

{"running":{"startedAt":"2020-11-17T11:47:21Z"}}

Let’s get the ready status:

kubectl get pods k8s-probes-7d57f897dd-td2q2 -o jsonpath='{.status.containerStatuses[0].ready}'

As you can see, the container is not ready.

false

Let’s look at endpoints again:

kubectl get endpoints

Services and related endpoints are listed:

NAME         ENDPOINTS           AGE
kubernetes   10.132.0.42:16443   8h
nginx                            11m

Here we can see that our Nginx service doesn’t have any endpoints as the container is not ready to take the traffic.

#Conclusion

We have seen the effects of the readiness probe and explored the parameters we can configure. Although we worked on the HTTP check, the concepts shown here apply to the other checks as well. You need to understand your application’s architecture and dependencies to be able to configure and operate readiness probes. Knowing your application is crucial as badly configured readiness probes can render your application unavailable. And please do not forget that readiness probes are run throughout the container’s lifetime.

#Further Reading

Photo by Ildefonso Polo on Unsplash

Kubernetes Insights

Do you want early access to great articles like this one?

Thank you for subscribing!
Oops! Something went wrong while submitting the form.

Explore Other Articles