Controllo di Sanità e Probes

Kubernetes fornisce due tipi di sonde (probes) per testare l'usabilità di un applicativo: Liveness Probes e Readiness Probes.

Un Liveness Probe testa se l'applicativo è vivo e raggiungibile. Se non lo è, il pod che lo implementa viene terminato e ristartato.

Un Readiness Probe testa se l'applicativo può ricevere traffico del tipo programmato, Se non può, non viene inviato traffico al pod che lo implementa.

Vi sono tre tipi di probe; HTTP, Comando e TCP.

  • HTTP - Kubernetes accede ad un path HTTP del pod, e se il responso ha i codici tra il 200 e il 300 lo considera attivo. Un pod può implementare un miniserver HTTP a questo solo scopo.
  • Command - viene eseguito un comando nel pod, e se ritorna un codice di ritorno 0 viene considerato attivo.
  • TCP - viene aperta una connessione al pod, e se ha successo il pod è considerato attivo.

Command Liveness

Esempio di Liveness Probe:

vim ~/scripts/liveness.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 40; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

I parametri sono:

  • initialDelaySeconds - quanto tempo attendere prima di inviare il primo probe
  • periodSeconds - ogni quanto tempo inviare i probe

Sottomettere il manifest:

kubectl apply -f ~/scripts/liveness.yml

Ispezionare gli eventi del pod:

kubectl describe pod liveness-exec

Kubernetes inizia subito ad inviare liveness probes, anche quando il pod non è in stato di Running. Quindi possono esservi degli eventi di fallimento iniziali. Possono essere gestiti aumentando il parametro initialDelaySeconds.

Dopo un certo periodo di tempo il pod è stabile e il rapporto da:

kubectl get pod
NAME            READY   STATUS    RESTARTS      AGE
liveness-exec   1/1     Running   7 (78s ago)   20m

In caso di fallimento si vede un esempio di questo tipo:

  Warning  Unhealthy  7m17s (x19 over 16m)    kubelet            Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
  Warning  BackOff    2m27s (x22 over 9m17s)  kubelet            Back-off restarting failed container liveness in pod liveness-exec_default(0726f57d-8dd9-489d-b0c5-ff352bfdc084)

E il pod è:

kubectl get pod
NAME            READY   STATUS             RESTARTS        AGE
liveness-exec   0/1     CrashLoopBackOff   7 (5m11s ago)   17m

Terminare il pod:

kubectl delete -f ~/scripts/liveness.yml

HTTP Liveness

Esempio:

vim ~/scripts/http-liveness.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

L'immagine registry.k8s.io/liveness è appositamente didattica.

E' stata scritta in linguaggio Go e contiene il route handler:

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

Il responso è appositamente accurato per i primi 10 secondi, ma poi fallisce. Con un Liveness Probe il pod viene ristartato.

Sottomettiamo il manifest:

kubectl apply -f ~/scripts/http-liveness.yml

E ben presto abbiamo una situazione di questo tipo:

kubectl get pod
NAME            READY   STATUS    RESTARTS      AGE
liveness-http   1/1     Running   2 (13s ago)   75s

Con l'evento dedotto da:

kubectl describe pod liveness-http
.....
  Warning  Unhealthy  1s (x4 over 28s)   kubelet            Liveness probe failed: HTTP probe failed with statuscode: 500

Terminiamo l'esercizio:

kubectl delete -f ~/scripts/http-liveness.yml

HTTP Readiness

Una leggera modifica alle specifiche di liveness:

vim ~/scripts/http-readiness.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: readiness
  name: readiness-http
spec:
  containers:
  - name: readiness
    image: registry.k8s.io/liveness
    args:
    - /server
    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

Sottomettere il manifest:

kubectl apply -f ~/scripts/http-readiness.yml

Inizialmente il pod si comporta come prima, e dopo 10 secondi fallisce. Kubernetes lo dichiara fallito e non lo ristarta.

Il rapporto di eventi è:

kubectl describe pod readiness-http
.....
  Warning  Unhealthy  18s (x21 over 75s)  kubelet            Readiness probe failed: HTTP probe failed with statuscode: 500

E il pod:

kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
readiness-http   0/1     Running   0          2m5s

Pulire l'esercizio:

kubectl delete -f ~/scripts/http-readiness.yml

TCP Liveness e Readiness

Prepariamo un manifest:

vim ~/scripts/tcp-liveness-readiness.yml
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10

Ha entrambi i tipi di probe, a scopo didattico per notare che non vi è differenza sintattica ma solo di comportamento. Basta uno o l'altro probe.

Kubernetes prova un'apertura di connessione alla porta 8080. Se ha successo il pod è alive/ready.

Sottomettiamo il manifest:

kubectl apply -f ~/scripts/tcp-liveness-readiness.yml

Non vi sono eventi di errore dopo tempo ragguardevole. Il pod sta bene.

Terminiamo l'esercizio:

kubectl delete -f ~/scripts/tcp-liveness-readiness.yml