TT Lab
开始
学习 学习路径 课程

KCNA — Kubernetes 与云原生入门

已经Running,为什么服务仍然失败

在 TT Lab 中继续学习

目标

通过真实的 kubelet 行为区分 Running、Ready、HTTP 响应和重启,并说明检查含义出错时产生的错觉。

为什么重要

端口已打开并不保证业务成功。如果把暂时不该接收请求的应用和应该重新启动的应用混为一谈,自动恢复会扩大故障。 在保持正常对比组的同时,分别观察就绪失败、进程重启、TCP/HTTP 检查差异和缓慢初始化。 所有实验都仅限于个人 k3s VM 中的合成应用。不要带入生产 kubeconfig、真实机密或外部服务器。 这是 55 分钟的实验,准备工作可能需要几分钟。如果需要更多时间,请在会话到期前延长。结束后,VM 和文件会被回收。

已准备的环境与辅助工具

命名空间 kcna-health 中的 web-a 和 web-b 位于 healthy Service 之后。用于对比的 lying Service 起初没有目标。 两个 Service 都是 ClusterIP:8080,publishNotReadyAddresses=false。应用镜像以固定 digest、Always pull、 non-root、cap drop ALL、seccomp RuntimeDefault、只读根文件系统、不挂载 ServiceAccount 令牌的方式运行。 只有 /state 的 1Mi emptyDir 用于合成故障标记。不要通过改变 workload 安全设置或正常对比组来解决任务。

辅助命令是在 python3 /opt/fixtures/kcna_health_lab.py 之后加上 act、capture、observe、grade、prepare 和步骤编号。 act 会先验证输入 JSON,然后只做指定的实验变更。capture 会等待真实的观测,并保存到 /root/kcna-health。 observe 输出当前的观测。grade 不会改变资源或学员文件。 学员要创建的是 change/probe 输入 JSON。不要自己编造观测 JSON 中的成功、UID 和状态值。 /opt/fixtures/kcna-health-context.json、kcna-health-lab-context.json、kcna-health-warming.json 是由辅助程序管理的内部记录。

步骤

  1. 用 capture 1 把 /root/kcna-health/baseline.json 保存下来。调查 kcna-health 中 web-a、web-b 的 Pod UID、containerID、restartCount、Ready,healthy Service 与 EndpointSlice 的条件,以及真实的 HTTP 200。Pod 以 non-root、cap drop ALL、只读根文件系统、不挂载令牌的方式运行,并且要把正常对比组 B 保留到最后。
  2. 在 /root/kcna-health/not-ready-change.json 中写入 pod=web-a、flag=not-ready、present=true 的 JSON。用 act 2 和 capture 2 保存 not-ready.json。A 必须在保持 Running 的同时变为 Ready=false、直接 HTTP 503、EndpointSlice ready=false,而容器和重启次数保持不变。同时观察 Service 的六个请求是否被转发到正常的 B。
  3. 在 /root/kcna-health/restored-change.json 中写入 pod=web-a、flag=not-ready、present=false。用 act 3 和 capture 3 保存 restored.json。在同一个 Pod、容器和重启次数下,Ready 和正常的 HTTP 响应必须恢复。
  4. 在 /root/kcna-health/restarted-change.json 中写入 pod=web-a、flag=unhealthy、present=true。用 act 4 和 capture 4 保存 restarted.json。在同一个 Pod UID 下,容器 ID 和应用 boot_id 必须改变,并且重启次数必须增加。这个标记是新进程启动时就会被清除的合成故障。
  5. 在 /root/kcna-health/tcp-probe.json 中写入 tcpSocket.port=8080、periodSeconds=2、timeoutSeconds=1、failureThreshold=2、successThreshold=1。act 5 会用这个设置创建 liar Pod 并放入就绪失败标记。用 capture 5 保存 tcp-lie.json。必须同时观测到 TCP Ready=true 和 lying Service 的 HTTP 503。
  6. 在 /root/kcna-health/http-probe.json 中用 httpGet.path=/readyz、httpGet.port=8080 取代 tcpSocket,其余四个数字与第 5 步相同。act 6 会确认原来的 liar UID,并把它替换为新的 HTTP 探针 Pod。用 capture 6 保存 http-excluded.json。请比较直接 HTTP 503、Ready=false、EndpointSlice ready=false、Service 中没有正常 HTTP 响应,以及发生变化的 Pod UID。
  7. 在 /root/kcna-health/startup-probe.json 中写入 httpGet.path=/startupz、httpGet.port=8080、periodSeconds=1、timeoutSeconds=1、failureThreshold=45。act 7 会创建初始化需要 20 秒的应用 slow,并记录刚启动时的 started=false、Ready=false、重启 0 次、/livez 503。用 capture 7 保存 warming.json。即使较晚才点击 capture,真实的初始观测也会被保留。
  8. 用 capture 8 把 /root/kcna-health/started.json 保存下来。slow 必须在与第 7 步相同的 Pod 和容器中,在没有重启的情况下达到 started=true、Ready=true、HTTP 200。A 的恢复、通过 HTTP 探针排除的 liar,以及原来 B 的身份、容器和正常响应也必须保留。

参考

如果失败,请在同一个 VM 中读取 observe 和 kubectl -n kcna-health get pods -o json,调查原因。 在等待新状态期间,不要反复调用 act。已完成的变更和观测要保留,不要把过去的状态重新做成当前的值。 初始化会随时间结束,所以第 7 步的评分会同时查看真实的初始观测与当前的同一容器、重启 0 次。 第 8 步还会额外确认是否真正完成启动。一般评分保持 60 秒,整个步骤准备保持 90 秒的预算。 准备只执行缺失的先前步骤。它不会代替你创建当前的输入和观测,也不会覆盖你写错的已有输入。 实验中的 liveness 标记是重启时会被清除的合成故障,它不是真正的死锁检测器,也不是所有故障的恢复方法。 不要把 liar 的独立 Pod 替换,直接当作生产 Deployment 的零停机部署流程。 观测文件是学习资料,不是控制 root 的远程证明,也不是防作弊装置。 官方依据:探针的作用 · EndpointSlice 条件 · 探针设置。

调查绿灯的基线

用 capture 1 把 /root/kcna-health/baseline.json 保存下来。调查 kcna-health 中 web-a、web-b 的 Pod UID、containerID、restartCount、Ready,healthy Service 与 EndpointSlice 的条件,以及真实的 HTTP 200。Pod 以 non-root、cap drop ALL、只读根文件系统、不挂载令牌的方式运行,并且要把正常对比组 B 保留到最后。

不要只看 Running 这个显示,请分别读取 UID、容器 ID、条件和响应。

把运行中的应用排除出请求分配

在 /root/kcna-health/not-ready-change.json 中写入 pod=web-a、flag=not-ready、present=true 的 JSON。用 act 2 和 capture 2 保存 not-ready.json。A 必须在保持 Running 的同时变为 Ready=false、直接 HTTP 503、EndpointSlice ready=false,而容器和重启次数保持不变。同时观察 Service 的六个请求是否被转发到正常的 B。

就绪失败不是终止命令。即使 EndpointSlice 中还留有地址,ready 条件也可能是 false。

不重启就恢复就绪状态

在 /root/kcna-health/restored-change.json 中写入 pod=web-a、flag=not-ready、present=false。用 act 3 和 capture 3 保存 restored.json。在同一个 Pod、容器和重启次数下,Ready 和正常的 HTTP 响应必须恢复。

这不是创建新 Pod 的恢复。只清除 readiness 标记,保留原来的进程。

同一个 Pod 内的容器重启

在 /root/kcna-health/restarted-change.json 中写入 pod=web-a、flag=unhealthy、present=true。用 act 4 和 capture 4 保存 restarted.json。在同一个 Pod UID 下,容器 ID 和应用 boot_id 必须改变,并且重启次数必须增加。这个标记是新进程启动时就会被清除的合成故障。

比起 Pod 名称相同,更要比较 Pod UID 和容器 ID 发生了怎样的变化。

端口是开的,HTTP 却是 503

在 /root/kcna-health/tcp-probe.json 中写入 tcpSocket.port=8080、periodSeconds=2、timeoutSeconds=1、failureThreshold=2、successThreshold=1。act 5 会用这个设置创建 liar Pod 并放入就绪失败标记。用 capture 5 保存 tcp-lie.json。必须同时观测到 TCP Ready=true 和 lying Service 的 HTTP 503。

接受连接和业务响应是两个不同的问题。HTTP 503 也是在 TCP 连接之上传递的。

检查业务就绪的 HTTP 探针

在 /root/kcna-health/http-probe.json 中用 httpGet.path=/readyz、httpGet.port=8080 取代 tcpSocket,其余四个数字与第 5 步相同。act 6 会确认原来的 liar UID,并把它替换为新的 HTTP 探针 Pod。用 capture 6 保存 http-excluded.json。请比较直接 HTTP 503、Ready=false、EndpointSlice ready=false、Service 中没有正常 HTTP 响应,以及发生变化的 Pod UID。

不要忽略探针,也不要暴露未就绪的地址。替换独立 Pod 的 probe 需要新的 Pod。

保护缓慢的初始化并留下记录

在 /root/kcna-health/startup-probe.json 中写入 httpGet.path=/startupz、httpGet.port=8080、periodSeconds=1、timeoutSeconds=1、failureThreshold=45。act 7 会创建初始化需要 20 秒的应用 slow,并记录刚启动时的 started=false、Ready=false、重启 0 次、/livez 503。用 capture 7 保存 warming.json。即使较晚才点击 capture,真实的初始观测也会被保留。

不要把 startup 和 liveness 的预算混在一起。观测辅助程序会保存初始状态,所以不用比谁点得快。

在没有重启的情况下证明启动完成

用 capture 8 把 /root/kcna-health/started.json 保存下来。slow 必须在与第 7 步相同的 Pod 和容器中,在没有重启的情况下达到 started=true、Ready=true、HTTP 200。A 的恢复、通过 HTTP 探针排除的 liar,以及原来 B 的身份、容器和正常响应也必须保留。

不仅要看当前的 Ready,还要核对初始观测中的容器 ID 和重启次数。