Adding Liveness Probe to an app

This guide shows you how to extend a Great Bear application with a basic liveness probe. Liveness probes can be helpful in case of long running applications which might get into a broken state, when the application is still running but it is not functional anymore and it needs to be restarted. The simplest way to achieve this is to add a healthz endpoint which is served by the application. If this endpoint does not respond, or it responds with an error, the application is restarted.

The following steps extend the Bear Echo application with a basic liveness probe.

Add healthz endpoint to the application

Add a new endpoint to your application. This endpoint will serve the incoming requests checking whether the app is still functional. For the Bear Echo application, add the following lines to the server.py file:

@app.route("/healthz")
def healthz():
    return "OK"

For reference, here’s the extended server.py:

# bear-echo/server.py

import os
import sys
import importlib
from flask import Flask

_HTTP_PORT: int = int(os.getenv('HTTP_PORT') or '11777')
_BASE_URL: str = os.getenv('BASE_URL') or '/'
_ECHO_TEXT: str = os.getenv('ECHO_TEXT') or 'Hello World'

app = Flask(__name__)

@app.route(_BASE_URL, methods=['GET'])
def echo():
    return(f'<hr><center><h1>{_ECHO_TEXT}</h1><center><hr>')

@app.route("/healthz")
def healthz():
    return "OK"

def _main():
    print('ECHO_TEXT: ' + _ECHO_TEXT)
    print(f'Started on 0.0.0.0:{_HTTP_PORT}{_BASE_URL}')
    app.run(host='0.0.0.0', port=_HTTP_PORT)

if __name__ == '__main__':
    _main()

Update deployment with the new liveness probe

Add a new liveness probe to the deployment file of the Helm chart of your application, for example:

livenessProbe:
  httpGet:
    path: /healthz
    port: {{ .Values.ports.http.port }}
  initialDelaySeconds: 5
  timeoutSeconds: 2
  periodSeconds: 10
  failureThreshold: 3

For reference, here’s the extended bear-echo/GBAP/templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "app.fullname" . }}
  labels: {{- include "app.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  strategy:
    type: {{ .Values.strategyType }}
  selector:
    matchLabels: {{- include "app.selector" . | nindent 6 }}
  template:
    metadata:
      labels: {{- include "app.selector" . | nindent 8 }}
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{ toYaml . | indent 8 | trim }}
    {{- end }}
    {{- with .Values.podSecurityContext }}
      securityContext:
        {{ toYaml . | indent 8 | trim }}
    {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          livenessProbe:
            httpGet:
            path: /healthz
            port: {{ .Values.ports.http.port }}
            initialDelaySeconds: 5
            timeoutSeconds: 2
            periodSeconds: 10
            failureThreshold: 3
          env:

        {{- with .Values.config }}
          {{- range $key, $value := . }}
          - name: {{$key}}
            # Some config parameters (GB_SITE_NAME, GB_NODE_NAMES)
            # can contain both quotes "" and '', for example: {"node1": "User's home"}
            # and the value itself should be quoted in order not to be parsed as object
            # so it's required to put it as a text field to make both quotes processable
            value: |
                            {{$value -}}
          {{- end }}
        {{- end }}

        {{- with .Values.ports }}
          ports:
          {{- range $key, $value := . }}
            - name: {{ $key }}
              containerPort: {{ $value.port }}
              protocol: {{ default "TCP" $value.protocol }}
          {{- end }}
        {{- end }}
        {{- with .Values.securityContext }}
          securityContext:
            {{ toYaml . | indent 12 | trim }}
        {{- end }}
        {{- with .Values.resources }}
          resources:
            {{ toYaml . | indent 12 | trim }}
        {{- end }}
    {{- with .Values.nodeSelector }}
      nodeSelector:
        {{ toYaml . | indent 8 | trim }}
    {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{ toYaml . | indent 8 | trim }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{ toYaml . | indent 8 | trim }}
    {{- end }}

Update and redeploy your application for the changes to take effect.

Next steps

By following these steps you should now have an application that is restarted if the liveness probe fails.

To improve you application further, you can Adding Prometheus metrics to an app.