Introduction
Logs, events, exec, and port-forwarding help you inspect an application. Probes let Kubernetes inspect the application continuously. A readiness probe answers, "Should this Pod receive traffic?" A liveness probe answers, "Should this container be restarted?"
In this lab, you will configure readiness and liveness separately. First, you will create an application that only becomes Ready after it creates a /ready endpoint. Then you will create another application with a liveness probe, remove its health file with kubectl exec, and observe Kubernetes restart the container.
Prepare a Probe Practice Namespace
In this step, you will prepare a clean namespace for probe practice.
Start in /home/labex/project. You will keep the probe manifests in this directory.
mkdir -p /home/labex/project
cd /home/labex/project
Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.
kubectl get nodes --request-timeout=5s
Expected output includes:
NAME STATUS ROLES AGE VERSION
labex-v135 Ready control-plane ... v1.35...
Wait until the cluster networking Pod is Ready. Probes and Services depend on healthy Pod networking.
kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s
Expected output:
pod/calico-node-... condition met
Create a namespace named ckad-prep-probes and set it as the current namespace:
kubectl create namespace ckad-prep-probes
kubectl config set-context --current --namespace=ckad-prep-probes
Confirm the current namespace:
kubectl config view --minify --output 'jsonpath={..namespace}'; echo
Expected output:
ckad-prep-probes
Configure a Readiness Probe
In this step, you will configure a readiness probe and observe that the Service only uses ready Pods.
The container starts an HTTP server immediately, but it creates /www/ready a few seconds later. The readiness probe checks GET /ready on port 8080. Until that path exists, the Pod can be Running but not Ready, and the Service should not send traffic to it.
Create readiness-web.yaml:
cat > readiness-web.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: readiness-web
spec:
replicas: 2
selector:
matchLabels:
app: readiness-web
template:
metadata:
labels:
app: readiness-web
spec:
terminationGracePeriodSeconds: 0
containers:
- name: web
image: busybox:1.36
command:
- /bin/sh
- -c
- |
mkdir -p /www
echo readiness-ok > /www/index.html
(sleep 12; echo ready > /www/ready) &
httpd -f -p 8080 -h /www
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 1
periodSeconds: 2
failureThreshold: 1
---
apiVersion: v1
kind: Service
metadata:
name: readiness-web
spec:
selector:
app: readiness-web
ports:
- port: 80
targetPort: 8080
EOF
Apply the manifest and look at the Pods shortly after creation:
kubectl apply -f readiness-web.yaml
sleep 3
kubectl get pods -l app=readiness-web
Expected output may show Pods that are Running but not yet Ready:
NAME READY STATUS RESTARTS AGE
readiness-web-... 0/1 Running 0 ...
Wait for the Deployment to become available:
kubectl rollout status deployment/readiness-web --timeout=120s
Expected output:
deployment "readiness-web" successfully rolled out
Inspect the Service endpoints through EndpointSlice. After the readiness probe passes, the Service should have two ready endpoints.
kubectl get endpointslice -l kubernetes.io/service-name=readiness-web
Expected output includes:
NAME ADDRESSTYPE PORTS ENDPOINTS
readiness-web-... IPv4 8080 ... + 1 more...
Create a client Pod and test the Service:
kubectl run probe-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=Ready pod/probe-client --timeout=120s
kubectl exec probe-client -- wget -qO- http://readiness-web
Expected output:
readiness-ok
Configure a Liveness Probe
In this step, you will configure a liveness probe that checks a health file.
A liveness probe decides whether Kubernetes should restart a container. This example uses an exec probe that runs test -f /tmp/healthy inside the container. The container creates that file when it starts, so it begins healthy.
Create liveness-web.yaml:
cat > liveness-web.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: liveness-web
spec:
replicas: 1
selector:
matchLabels:
app: liveness-web
template:
metadata:
labels:
app: liveness-web
spec:
terminationGracePeriodSeconds: 0
containers:
- name: web
image: busybox:1.36
command:
- /bin/sh
- -c
- |
touch /tmp/healthy
mkdir -p /www
echo liveness-ok > /www/index.html
httpd -f -p 8080 -h /www
ports:
- containerPort: 8080
livenessProbe:
exec:
command:
- test
- -f
- /tmp/healthy
initialDelaySeconds: 3
periodSeconds: 3
failureThreshold: 1
EOF
Apply the manifest and wait for the Deployment:
kubectl apply -f liveness-web.yaml
kubectl rollout status deployment/liveness-web --timeout=120s
Expected output includes:
deployment.apps/liveness-web created
deployment "liveness-web" successfully rolled out
Store the Pod name and confirm the health file exists:
LIVE_POD="$(kubectl get pods -l app=liveness-web -o jsonpath='{.items[0].metadata.name}')"
echo "$LIVE_POD"
kubectl exec "$LIVE_POD" -- test -f /tmp/healthy
echo "health file exists"
Expected output includes the Pod name and:
health file exists
Describe the Pod and look for the liveness probe configuration:
kubectl describe pod "$LIVE_POD" | grep -A3 'Liveness:'
Expected output includes:
Liveness: exec [test -f /tmp/healthy] delay=3s timeout=1s period=3s
Trigger a Liveness Restart
In this step, you will remove the health file and watch the liveness probe restart the container.
The Pod object can stay the same while the container inside it restarts. That is why you will watch the container restart count instead of expecting a new Pod name.
Store the current Pod name:
LIVE_POD="$(kubectl get pods -l app=liveness-web -o jsonpath='{.items[0].metadata.name}')"
echo "$LIVE_POD"
Remove the health file from inside the container:
kubectl exec "$LIVE_POD" -- rm /tmp/healthy
Watch the restart count until it becomes greater than zero. The liveness probe runs every three seconds, so this may take a short moment.
for i in $(seq 1 20); do
RESTARTS="$(kubectl get pod "$LIVE_POD" -o jsonpath='{.status.containerStatuses[0].restartCount}')"
echo "restart-count=$RESTARTS"
if [ "$RESTARTS" != "0" ]; then
break
fi
sleep 3
done
Expected output eventually includes:
restart-count=1
After the container restarts, the startup command creates /tmp/healthy again. Confirm the file is back:
kubectl exec "$LIVE_POD" -- test -f /tmp/healthy
echo "health file recreated"
Expected output:
health file recreated
Read the Pod events and look for the liveness failure:
kubectl describe pod "$LIVE_POD" | sed -n '/Events:/,$p'
Expected output includes:
Warning Unhealthy ... Liveness probe failed
Normal Killing ... Container web failed liveness probe
Summary
You configured both major application probes. The readiness probe controlled when Pods joined Service endpoints, while the liveness probe restarted a container after its health file was removed. You also inspected EndpointSlices, Pod descriptions, restart counts, and events to connect probe configuration to visible Kubernetes behavior.


