隔离边界:只操作本机 kind 集群的 worker 容器,不要在任何真实节点上执行 systemctl stop kubelet,也不要修改宿主机 systemd。准备 Docker、kind、kubectl,预留约 2 GB 内存。
cat > /tmp/kind-notready.yaml <<'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
EOF
kind create cluster --name lab-notready --config /tmp/kind-notready.yaml
kubectl get nodes -o wide
等两个节点都为 Ready 再继续。若使用 minikube,可执行 minikube start -p lab-notready --nodes 2,后续把 worker 容器名替换为 lab-notready-m02。
先部署一个带短驱逐容忍时间的 Deployment,并固定到 worker:
kubectl create deployment notready-lab --image=nginx:1.27-alpine --replicas=2
kubectl patch deployment notready-lab --type=strategic -p '
{"spec":{"template":{"spec":{
"tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":10}],
"nodeSelector":{"kubernetes.io/hostname":"lab-notready-worker"}
}}}}'
kubectl rollout status deployment/notready-lab --timeout=120s
kubectl get pods -l app=notready-lab -o wide
确认两个 Pod 都在 lab-notready-worker 上 Running。然后停止该节点的 kubelet,模拟节点失联:
docker exec lab-notready-worker systemctl stop kubelet
sleep 45
kubectl get nodes
kubelet 停止后,节点状态需要经过多个心跳周期才会变为 NotReady,通常 40 秒左右。
kubectl get nodes -o wide
kubectl describe node lab-notready-worker | sed -n '/Conditions:/,/Addresses:/p'
kubectl describe node lab-notready-worker | grep -A3 Taints
kubectl get pods -l app=notready-lab -o wide
kubectl get events --sort-by=.lastTimestamp | tail -20
预期 worker 的 Ready Condition 变成 False,Reason 为 KubeletNotReady 或 NodeStatusUnknown;节点自动加上 node.kubernetes.io/not-ready:NoSchedule 和 NoExecute 污点。Pod 在 tolerationSeconds=10 到期后进入 Terminating 或被删除,事件中出现 TaintManagerEviction / Deleting pod for node condition。由于只有 worker 能满足 nodeSelector,Pod 会重建为 Pending,不会自动跑到 control-plane。
kubectl get nodes 与 describe node 的 Conditions。Ready=False 且 KubeletNotReady 多半是 kubelet 自身;Ready=Unknown 更像心跳中断或网络分区。MemoryPressure、DiskPressure、PIDPressure=True。资源压力触发的 NotReady 通常伴随驱逐和 kubelet 日志中的 imageGC/eviction 记录。docker exec <node> systemctl status kubelet、journalctl -u kubelet -n 50;真实节点同样先看 kubelet 日志,再检查容器运行时和磁盘。kubectl get nodes 显示 NotReady 不代表节点上所有容器都停了。kubelet 停止后静态 Pod 和已运行容器可能继续工作,业务甚至还能短暂提供服务;但调度、探针和驱逐会失效。另一个误判是把 NodeStatusUnknown 当成 kubelet 一定挂了,网络分区也会产生同样状态。重新启动 worker 的 kubelet,等待节点恢复 Ready,再解除 nodeSelector:
docker exec lab-notready-worker systemctl start kubelet
kubectl wait --for=condition=Ready node/lab-notready-worker --timeout=180s
kubectl get nodes
kubectl get pods -l app=notready-lab -o wide
节点 Ready 后自动污点会被移除。把 Deployment 的 nodeSelector 去掉,让副本回到可调度状态:
kubectl patch deployment notready-lab --type=json \
-p '[{"op":"remove","path":"/spec/template/spec/nodeSelector"}]'
kubectl rollout status deployment/notready-lab --timeout=180s
kubectl get pods -l app=notready-lab -o wide
两个副本都 Running 且分布合理即恢复。生产中还要确认 kubelet 停止的根因(OOM、证书过期、配置错误、磁盘满),否则重启后可能再次 NotReady。
docker exec lab-notready-worker systemctl start kubelet 2>/dev/null || true
kubectl delete deployment notready-lab --ignore-not-found
kind delete cluster --name lab-notready
rm -f /tmp/kind-notready.yaml
不要对真实节点执行上述 systemctl 命令;本页所有节点操作都只针对 kind 创建的 Docker 容器。
Ready=Unknown 与 Ready=False 分别更可能指向什么问题?