Lifecycle Hooks

Lifecycle Hooks

kubelet can run code triggered by Container Lifecycle Hooks. This allows the user to run specific code during specific events of a containers lifecycle.

For example: running a graceful shutdown script before a container is terminated.

There are two hooks which are exposed:

Containers Hooks

  • PostStart : This hook gets executed upon container creation but there is no guarantee that it will run after the container ENTRYPOINT.
  • PreStop : This hook gets executed just before a container is terminated. This is a blocking call which means the hook execution must complete before the call to delete a container can be sent.

Both hooks mentioned above do not take any parameters. There are two types of handlers which can be implemented in the hook implementation:

  • Exec : runs a specific command inside the container and the resources consumed by the command are counted against the container.
  • HTTP : executes an HTTP request against a specific endpoint on the container.

To understand when this hook starts, you will create a pod with a shared volume and you will update the /lifecycle/timing file, for each hook you will print the seconds counter and a text.

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-1
  labels:
    app: app1
spec:
  initContainers:
  - name:           init
    image:          r.deso.tech/library/alpine
    command:       ['sh', '-c', 'echo $(date +%s): INIT >> /lifecycle/timing']
    volumeMounts:
    - mountPath:    /lifecycle
      name:         timing
  containers:
  - name:           main
    image:          r.deso.tech/library/alpine
    command:       ['sh', '-c', 'echo $(date +%s): START >> /lifecycle/timing; sleep 10; echo $(date +%s): END >> /lifecycle/timing;']
    volumeMounts:
    - mountPath:    /lifecycle
      name:         timing
    livenessProbe:
      exec:
        command:   ['sh', '-c', 'echo $(date +%s): LIVENESS >> /lifecycle/timing']
    readinessProbe:
      exec:
        command:   ['sh', '-c', 'echo $(date +%s): READINESS >> /lifecycle/timing']
    lifecycle:
      postStart:
        exec:
          command:   ['sh', '-c', 'echo $(date +%s): POST-START >> /lifecycle/timing']
      preStop:
        exec:
          command:  ['sh', '-c', 'echo $(date +%s): PRE-HOOK >> /lifecycle/timing']
  volumes:
  - name:           timing
    hostPath:
      path:         /tmp/desotech

The next command will cat the contents of /lifecycle/timing and you will see the output of the text written by the hook.

kubectl exec -it lifecycle-1 -- cat /lifecycle/timing

Your output will be similar to this:

1607340288: INIT
1607340290: START
1607340290: POST-START
1607340295: LIVENESS
1607340296: READINESS
1607340300: END
1607340302: START
1607340302: POST-START
1607340305: LIVENESS
1607340306: READINESS
1607340312: END
1607340330: START
1607340330: POST-START
1607340335: LIVENESS
1607340336: READINESS

As you can see you will have 3 cycle, because the pod will be restarted and for each cycle you will see these steps:

  • INIT
  • START
  • POST-START
  • LIVENESS
  • READINESS
  • END

You can add your script using EXEC or HTTP hooks. We will see use of HTTP during Probing exercise.

Liveness

Let’create this pod:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: r.deso.tech/library/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

Apply the file.

kubectl apply -f liveness-exec.yaml

Within 30 seconds, view the Pod events:

kubectl describe pod liveness-exec

The output indicates that no liveness probes have failed yet.

After 35 seconds, view the Pod events again:

kubectl describe pod liveness-exec

At the bottom of the output, there are messages indicating that the liveness probes have failed, and the containers have been killed and recreated

Wait another 30 seconds, and verify that the container has been restarted:

kubectl get pod liveness-exec
NAME            READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m

Readiness

Readiness and liveness probes can be used in parallel for the same container. Using both can ensure that traffic does not reach a container that is not ready for it, and that containers are restarted when they fail.

Let’s create pod manifest:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: readiness
  name: readiness-exec
spec:
  containers:
  - name: readiness
    image: r.deso.tech/library/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    readinessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

Apply the file:

kubectl apply -f readiness-exec.yaml

Now, take a look about your pod staus:

kubectl get pod

and take a look about events:

kubectl describe pod readiness-exec
Conclusion about Readiness Probe: Sometimes, applications are temporarily unable to serve traffic. For example, an application might need to load large data or configuration files during startup, or depend on external services after startup. In such cases, you don't want to kill the application, but you don't want to send it requests either. Kubernetes provides readiness probes to detect and mitigate these situations. A pod with containers reporting that they are not ready does not receive traffic through Kubernetes Services.

Clean Up

kubectl delete pods lifecycle-1 liveness-exec readiness-exec
pod "lifecycle-1" deleted
pod "liveness-exec" deleted
pod "readiness-exec" deleted