配置存活探针与就绪探针

KubernetesBeginner
立即练习

介绍

日志、事件、exec 和端口转发可以帮助你检查应用程序。而探针(Probes)则允许 Kubernetes 对应用程序进行持续的健康检查。就绪探针回答的问题是:「该 Pod 是否应该接收流量?」;存活探针回答的问题是:「该容器是否应该被重启?」

在本实验中,你将分别配置就绪探针和存活探针。首先,你将创建一个只有在创建了 /ready 端点后才会变为就绪状态的应用程序。然后,你将创建另一个带有存活探针的应用程序,通过 kubectl exec 删除其健康检查文件,并观察 Kubernetes 如何重启该容器。

准备探针练习命名空间

在此步骤中,你将准备一个干净的命名空间用于探针练习。

从 /home/labex/project 开始。你将把探针的清单文件保存在此目录中。

mkdir -p /home/labex/project
cd /home/labex/project

检查 Kubernetes API 服务器是否可达。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。

kubectl get nodes --request-timeout=5s

预期输出包含:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

等待集群网络 Pod 就绪。探针和 Service 依赖于健康的 Pod 网络。

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

预期输出:

pod/calico-node-... condition met

创建一个名为 ckad-prep-probes 的命名空间,并将其设置为当前命名空间:

kubectl create namespace ckad-prep-probes
kubectl config set-context --current --namespace=ckad-prep-probes

确认当前命名空间:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

预期输出:

ckad-prep-probes

配置就绪探针

在此步骤中,你将配置一个就绪探针,并观察 Service 如何仅使用就绪的 Pod。

该容器会立即启动 HTTP 服务器,但在几秒钟后才会创建 /www/ready 文件。就绪探针会检查端口 8080 上的 GET /ready 请求。在该路径存在之前,Pod 可以处于 Running 状态但并非 Ready,此时 Service 不应向其发送流量。

创建 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

应用清单文件,并在创建后立即查看 Pod:

kubectl apply -f readiness-web.yaml
sleep 3
kubectl get pods -l app=readiness-web

预期输出可能显示 Pod 处于 Running 但尚未 Ready 的状态:

NAME                             READY   STATUS    RESTARTS   AGE
readiness-web-...                0/1     Running   0          ...

等待 Deployment 变为可用状态:

kubectl rollout status deployment/readiness-web --timeout=120s

预期输出:

deployment "readiness-web" successfully rolled out

通过 EndpointSlice 检查 Service 端点。在就绪探针通过后,Service 应该有两个就绪的端点。

kubectl get endpointslice -l kubernetes.io/service-name=readiness-web

预期输出包含:

NAME                ADDRESSTYPE   PORTS   ENDPOINTS
readiness-web-...   IPv4          8080    ... + 1 more...

创建一个客户端 Pod 并测试 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

预期输出:

readiness-ok

配置存活探针

在此步骤中,你将配置一个检查健康文件的存活探针。

存活探针决定了 Kubernetes 是否应该重启容器。此示例使用了一个 exec 探针,它会在容器内运行 test -f /tmp/healthy。容器在启动时会创建该文件,因此它最初是健康的。

创建 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

应用清单文件并等待 Deployment 就绪:

kubectl apply -f liveness-web.yaml
kubectl rollout status deployment/liveness-web --timeout=120s

预期输出包含:

deployment.apps/liveness-web created
deployment "liveness-web" successfully rolled out

存储 Pod 名称并确认健康文件存在:

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"

预期输出包含 Pod 名称以及:

health file exists

描述 Pod 并查看存活探针配置:

kubectl describe pod "$LIVE_POD" | grep -A3 'Liveness:'

预期输出包含:

Liveness:  exec [test -f /tmp/healthy] delay=3s timeout=1s period=3s

触发存活重启

在此步骤中,你将删除健康文件,并观察存活探针如何重启容器。

Pod 对象可以保持不变,而其中的容器会重启。这就是为什么你要观察容器的重启次数,而不是期望出现一个新的 Pod 名称。

存储当前的 Pod 名称:

LIVE_POD="$(kubectl get pods -l app=liveness-web -o jsonpath='{.items[0].metadata.name}')"
echo "$LIVE_POD"

从容器内部删除健康文件:

kubectl exec "$LIVE_POD" -- rm /tmp/healthy

观察重启次数,直到它大于零。存活探针每三秒运行一次,因此这可能需要一点时间。

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

预期输出最终包含:

restart-count=1

容器重启后,启动命令会再次创建 /tmp/healthy。确认文件已恢复:

kubectl exec "$LIVE_POD" -- test -f /tmp/healthy
echo "health file recreated"

预期输出:

health file recreated

读取 Pod 事件并查看存活探针失败记录:

kubectl describe pod "$LIVE_POD" | sed -n '/Events:/,$p'

预期输出包含:

Warning  Unhealthy  ...  Liveness probe failed
Normal   Killing    ...  Container web failed liveness probe

总结

你配置了两种主要的应用程序探针。就绪探针控制了 Pod 何时加入 Service 端点,而存活探针在健康文件被删除后重启了容器。你还检查了 EndpointSlice、Pod 描述、重启次数和事件,从而将探针配置与可见的 Kubernetes 行为关联了起来。