Liveness & Readiness
Make sure you are in the correct namespace:
kubectl config set-context --current --namespace=myspace
Make sure nothing else is deployed:
kubectl get all
No resources found in myspace namespace.
Now we’re going to deploy our application with a Liveness and Readiness probe set. Take a look at myboot-deployment-live-ready.yml
If you’re running this from within VSCode you can use CTRL+p (or CMD+p on Mac OSX) to quickly open |
apiVersion: apps/v1
kind: Deployment
metadata:
name: myboot
spec:
replicas: 1
selector:
matchLabels:
app: myboot
template:
metadata:
labels:
app: myboot
env: dev
spec:
containers:
- name: myboot
image: quay.io/rhdevelopers/myboot:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
requests:
memory: "300Mi"
cpu: "250m" # 1/4 core
limits:
memory: "400Mi"
cpu: "1000m" # 1 core
livenessProbe:
httpGet:
port: 8080
path: /alive
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 3
Now apply this deployment with the following command
kubectl apply -f apps/kubefiles/myboot-deployment-live-ready.yml
Describe the deployment:
kubectl describe deployment myboot
...
Image: quay.io/rhdevelopers/myboot:v1
Port: 8080/TCP
Host Port: 0/TCP
Limits:
cpu: 1
memory: 400Mi
Requests:
cpu: 250m
memory: 300Mi
Liveness: http-get http://:8080/ delay=10s timeout=2s period=5s #success=1 #failure=3
Readiness: http-get http://:8080/health delay=10s timeout=1s period=3s #success=1 #failure=3
...
Deploy a Service:
kubectl apply -f apps/kubefiles/myboot-service.yml
IP=$(minikube ip -p devnation)
PORT=$(kubectl get service/myboot -o jsonpath="{.spec.ports[*].nodePort}")
If using a hosted Kubernetes cluster like OpenShift then use curl and the EXTERNAL-IP address with port 8080
or get it using kubectl
:
IP=$(kubectl get service myboot -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
PORT=$(kubectl get service myboot -o jsonpath="{.spec.ports[*].port}")
If you are in AWS, you need to get the hostname instead of ip.
|
IP=$(kubectl get service myboot -o jsonpath="{.status.loadBalancer.ingress[0].hostname}")
Curl the Service:
curl $IP:$PORT
And run loop script:
while true
do curl $IP:$PORT
sleep 0.8
done
Change the image:
kubectl set image deployment/myboot myboot=quay.io/rhdevelopers/myboot:v2
And notice the error free rolling update:
Aloha from Spring Boot! 131 on myboot-845968c6ff-k4rvb
Aloha from Spring Boot! 134 on myboot-845968c6ff-9wvt9
Aloha from Spring Boot! 122 on myboot-845968c6ff-9824z
Bonjour from Spring Boot! 0 on myboot-8449d5468d-m88z4
Bonjour from Spring Boot! 1 on myboot-8449d5468d-m88z4
Aloha from Spring Boot! 135 on myboot-845968c6ff-9wvt9
Aloha from Spring Boot! 133 on myboot-845968c6ff-k4rvb
Aloha from Spring Boot! 137 on myboot-845968c6ff-9wvt9
Bonjour from Spring Boot! 3 on myboot-8449d5468d-m88z4
Look at the Endpoints to see which pods are part of the Service:
kubectl get endpoints myboot -o json | jq '.subsets[].addresses[].ip'
These are the Pod IPs that have passed their readiness probes:
"10.129.2.40"
"10.130.2.37"
"10.130.2.38"
Readiness Probe
Exec into a single Pod and change its readiness flag:
kubectl exec -it myboot-845968c6ff-k5lcb /bin/bash
curl localhost:8080/misbehave
exit
See that the pod is no longer Ready:
NAME READY STATUS RESTARTS AGE
myboot-845968c6ff-9wshg 1/1 Running 0 11m
myboot-845968c6ff-k5lcb 0/1 Running 0 12m
myboot-845968c6ff-zsgx2 1/1 Running 0 11m
Now check the Endpoints:
kubectl get endpoints myboot -o json | jq '.subsets[].addresses[].ip'
And that pod is now missing from the Service’s loadbalancer:
"10.130.2.37"
"10.130.2.38"
Which is also self-evident in the curl loop:
Aloha from Spring Boot! 845 on myboot-845968c6ff-9wshg
Aloha from Spring Boot! 604 on myboot-845968c6ff-zsgx2
Aloha from Spring Boot! 846 on myboot-845968c6ff-9wshg
Liveness Probe
kubectl set image deployment/myboot myboot=quay.io/rhdevelopers/myboot:v3
Let the rollout finish to completion across all 3 replicas:
watch kubectl get pods
NAME READY STATUS RESTARTS AGE
myboot-56659c9d69-6sglj 1/1 Running 0 2m2s
myboot-56659c9d69-mdllq 1/1 Running 0 97s
myboot-56659c9d69-zjt6q 1/1 Running 0 72s
And as seen in the curl loop/poller:
Jambo from Spring Boot! 40 on myboot-56659c9d69-mdllq
Jambo from Spring Boot! 26 on myboot-56659c9d69-zjt6q
Jambo from Spring Boot! 71 on myboot-56659c9d69-6sglj
Edit the Deployment to point to the /alive URL:
If you’re running this tutorial from within VSCode or would like to use VSCode to edit the resource targeted, make sure you set the following environment variable before issuing the
|
kubectl edit deployment myboot
And change the liveness probe:
...
spec:
containers:
- image: quay.io/rhdevelopers/myboot:v3
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
httpGet:
path: /alive
port: 8080
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 2
name: myboot
...
Save and close the editor, allowing that change to rollout:
watch kubectl get pods
NAME READY STATUS RESTARTS AGE
myboot-558b4f8678-nw762 1/1 Running 0 59s
myboot-558b4f8678-qbrgc 1/1 Running 0 81s
myboot-558b4f8678-z7f9n 1/1 Running 0 36s
Now pick one of the pods, exec
into it and shoot it:
kubectl exec -it myboot-558b4f8678-qbrgc /bin/bash
curl localhost:8080/shot
And you will see it get restarted:
NAME READY STATUS RESTARTS AGE
myboot-558b4f8678-nw762 1/1 Running 0 4m7s
myboot-558b4f8678-qbrgc 1/1 Running 1 4m29s
myboot-558b4f8678-z7f9n 1/1 Running 0 3m44s
Plus, your exec will be terminated:
kubectl exec -it myboot-558b4f8678-qbrgc /bin/bash
curl localhost:8080/shot
I have been shot in the head1000610000@myboot-558b4f8678-qbrgc:/app$ command terminated with exit code 137
And your end-users will not see any errors:
Jambo from Spring Boot! 174 on myboot-558b4f8678-z7f9n
Jambo from Spring Boot! 11 on myboot-558b4f8678-qbrgc
Jambo from Spring Boot! 12 on myboot-558b4f8678-qbrgc
Jambo from Spring Boot! 206 on myboot-558b4f8678-nw762
Jambo from Spring Boot! 207 on myboot-558b4f8678-nw762
Jambo from Spring Boot! 175 on myboot-558b4f8678-z7f9n
Jambo from Spring Boot! 176 on myboot-558b4f8678-z7f9n
Startup Probe
Some applications require an additional startup time on their first initialization.
It might be tricky to fit this scenario into the liveness/readiness probes as you need to configure them for their normal behaviour to detect abnormalities during the running time and moreover covering the long start up time.
For instance, what if we had an application that might deadlock and we want to catch such issues immediately, we might have liveness and readiness probes that look like in apps/kubefiles/myboot-deployment-live-ready-aggressive.yml
If you’re running this from within VSCode you can use CTRL+p (or CMD+p on Mac OSX) to quickly open |
apiVersion: apps/v1
kind: Deployment
metadata:
name: myboot
spec:
replicas: 1
selector:
matchLabels:
app: myboot
template:
metadata:
labels:
app: myboot
env: dev
spec:
containers:
- name: myboot
image: quay.io/rhdevelopers/myboot:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
requests:
memory: "300Mi"
cpu: "250m" # 1/4 core
limits:
memory: "400Mi"
cpu: "1000m" # 1 core
livenessProbe:
httpGet:
port: 8080
path: /alive
periodSeconds: 2
timeoutSeconds: 2
failureThreshold: 2
readinessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 3
Then apply that deployment
kubectl apply -f apps/kubefiles/myboot-deployment-live-ready-aggressive.yml
As we’ll see from the pod watch, the pods are continually getting restarted, sometimes after it successfully boots up (because kubelet schedules for restart) and this is due to the startup time of SpringBoot.
kubectl describe pods
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 96s default-scheduler Successfully assigned myspace/myboot-849ccd6948-8vrfq to devnation
Normal Pulled 92s kubelet Successfully pulled image "quay.io/rhdevelopers/myboot:v1" in 3.295180194s
Normal Created 55s (x2 over 92s) kubelet Created container myboot
Normal Started 55s (x2 over 92s) kubelet Started container myboot
Normal Pulled 55s kubelet Successfully pulled image "quay.io/rhdevelopers/myboot:v1" in 3.289395484s
Warning Unhealthy 52s (x4 over 90s) kubelet Liveness probe failed: Get "http://172.17.0.4:8080/alive": dial tcp 172.17.0.4:8080: connect: connection refused
Normal Killing 52s (x2 over 88s) kubelet Container myboot failed liveness probe, will be restarted
Normal Pulling 22s (x3 over 95s) kubelet Pulling image "quay.io/rhdevelopers/myboot:v1"
Warning Unhealthy 19s (x10 over 88s) kubelet Readiness probe failed: Get "http://172.17.0.4:8080/health": dial tcp 172.17.0.4:8080: connect: connection refused
Startup probes fix this problem, as once the startup probe has succeeded, the rest of the probes take over, but until the startup probe passes, neither the liveness nor the readiness probes can run.
myboot-deployment-startup-live-ready.yml
is an example of a deployment with just such a probe
If you’re running this from within VSCode you can use CTRL+p (or CMD+p on Mac OSX) to quickly open |
apiVersion: apps/v1
kind: Deployment
metadata:
name: myboot
spec:
replicas: 1
selector:
matchLabels:
app: myboot
template:
metadata:
labels:
app: myboot
env: dev
spec:
containers:
- name: myboot
image: quay.io/rhdevelopers/myboot:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
requests:
memory: "300Mi"
cpu: "250m" # 1/4 core
limits:
memory: "400Mi"
cpu: "1000m" # 1 core
livenessProbe:
httpGet:
port: 8080
path: /alive
periodSeconds: 2
timeoutSeconds: 2
failureThreshold: 2
readinessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 3
startupProbe:
httpGet:
path: /alive
port: 8080
failureThreshold: 6
periodSeconds: 5
timeoutSeconds: 1
You’ll see the difference is this section
startupProbe:
httpGet:
path: /alive
port: 8080
failureThreshold: 6
periodSeconds: 5
timeoutSeconds: 1
Then apply that deployment
kubectl apply -f apps/kubefiles/myboot-deployment-startup-live-ready.yml
The startup probe waits for 30 seconds (5 * 6
) to startup the application. Notice, too, that the delay on the liveness and readiness checks has gone down to 0.
watch kubectl get pods
NAME READY STATUS RESTARTS AGE
myboot-579cc5cc47-2bk5p 0/1 Running 0 67s
Eventually your curl loop should show the pod running
Aloha from Spring Boot! 18 on myboot-849ccd6948-8vrfq Aloha from Spring Boot! 19 on myboot-849ccd6948-8vrfq Aloha from Spring Boot! 20 on myboot-849ccd6948-8vrfq Aloha from Spring Boot! 21 on myboot-849ccd6948-8vrfq
Let’s show that the liveness probe has taken over.
Now pick one of the pods, exec
into it and shoot it:
kubectl exec -it myboot-558b4f8678-qbrgc /bin/bash
curl localhost:8080/shot
And you will see it get restarted.
Describe the pod to get the statistics of probes:
kubectl describe pod myboot-579cc5cc47-2bk5p
Limits:
cpu: 1
memory: 400Mi
Requests:
cpu: 250m
memory: 300Mi
Liveness: http-get http://:8080/ delay=10s timeout=2s period=5s #success=1 #failure=3
Readiness: http-get http://:8080/health delay=10s timeout=1s period=3s #success=1 #failure=3
Startup: http-get http://:8080/alive delay=0s timeout=1s period=5s #success=1 #failure=12
Environment: <none>
Mounts: