返回首页

Node 排障 — NotReady 九步排查 / Kubelet / 容器运行时 / 资源压力 / 证书 / 预防

📅 创建于 2026-05-08 🔄 更新于 2026-05-11 📝 1449 字

Node 排障

Node 是 Pod 运行的基础底层,Node 不可用会直接影响其上所有 Pod。

排查口诀: Node NotReady 不要慌,Conditions 先看看;kubelet 日志是王道,磁盘空间占比高;容器运行时不能忘,cgroup 一致很重要;证书过期要检查,网络连通少不了。

Kubelet 节点状态机制

kubelet 在每个 nodeStatusReportFrequency 周期(默认 10 秒)内向 API Server 更新节点状态。判断逻辑在 kube-controller-manager 的 nodecontroller 中:

  • kubelet 每 10 秒上报一次节点状态(可通过 --node-status-update-frequency 修改)
  • nodecontroller 在节点连续 40 秒未更新时将状态置为 Unknown
  • 节点处于 Unknown 超过 5 分钟(pod-eviction-timeout)后,触发 Pod 驱逐
  • 如果超过 termination-duration(默认 5 分钟)仍未恢复,节点被彻底标记为 NotReady

kubelet 报告的 Ready 条件是一个组合条件,任何一个检查失败都会导致 NotReady:

Conditions:
  Type                 Status    Reason
  MemoryPressure       False     KubeletHasSufficientMemory
  DiskPressure         True      KubeletHasDiskPressure    ← 磁盘压力
  PIDPressure          False     KubeletHasSufficientPID
  NetworkUnavailable   False     RouteCreated
  Ready                False     KubeletNotReady           ← 综合结果

排查路径总览

  1. 确认 NotReady 现象和受影响范围(kubectl describe node
  2. 检查 kubelet 进程(systemctl status kubelet / journalctl
  3. 检查容器运行时(containerd/docker / crictl)
  4. 检查节点资源(df -h / free -m / uptime
  5. 检查网络连通性(ping / curl / CNI)
  6. 检查证书是否过期(kubeadm certs check-expiration
  7. 常见根因场景与修复
  8. 验证修复结果
  9. 预防措施

Step 1 — 确认 NotReady 现象和受影响范围

# 查看所有节点状态
kubectl get nodes -o wide

# 过滤 NotReady 节点
kubectl get nodes | grep -v Ready

# 查看节点详情(重点关注 Conditions 段最后几行)
kubectl describe node <node-name>

Conditions 含义速查:

Condition True 含义 常见根因
MemoryPressure 内存不足 节点内存耗尽,OOM risk
DiskPressure 磁盘不足 日志/镜像占满磁盘,最常⻅
PIDPressure 进程数超限 进程数接近 pid_max
NetworkUnavailable 网络不可用 CNI 配置错误
Ready=False kubelet 未就绪 以上任一或多个原因的组合

Step 2 — 检查 kubelet 进程

kubelet 是节点上最核心的 K8s 组件。进程挂了 → 节点立即 NotReady。

查看 kubelet 状态

sudo systemctl status kubelet
# Active: active (running) → 正常
# Active: failed (Result: start-limit-hit) → 启动失败,立刻查看日志

查看 kubelet 日志

# 最近 300 行
sudo journalctl -u kubelet -n 300 --no-pager

# 按故障时间过滤
sudo journalctl -u kubelet --since "2026-04-29 09:00:00" --no-pager

# 搜索错误
sudo journalctl -u kubelet -b --no-pager | grep -i error

常见日志错误

错误类型 典型日志 排查方向
容器运行时连接失败 failed to load Kubelet config file / context deadline exceeded 检查 kubelet 的 --container-runtime-endpoint 和 containerd 配置是否一致
证书相关 unable to load client CA file 证书文件缺失或过期,见 Step 6
ETCD 连接超时 request canceled etcd 健康状态和网络连通性
OOM kill 见 dmesg 或 journalctl 中 OOM 记录 节点内存不足,见 Step 4

⚠️ 重启 kubelet

重启 kubelet 会导致该节点上的 Pod 被驱逐和重新调度。生产环境执行前必须确认副本数足够。

# 先评估影响
kubectl get pods -o wide --all-namespaces | grep <node-name>

# 重启
sudo systemctl restart kubelet
sleep 30
kubectl get node <node-name>

Step 3 — 检查容器运行时

containerd 排查

# 查看 containerd 服务状态
sudo systemctl status containerd
sudo journalctl -u containerd -n 200 --no-pager

# 使用 crictl 检查运行时
sudo crictl info
# 报错 "runtime endpoint not connected" → containerd 异常

# 检查 containerd socket 文件
ls -la /run/containerd/containerd.sock

# 查看容器和镜像
sudo crictl ps -a
sudo crictl images

dockerd 排查(如果使用 Docker 运行时)

sudo systemctl status docker
sudo journalctl -u docker -n 200 --no-pager
sudo docker ps -a
sudo docker info

# docker info 中重点关注 Cgroup Driver:
# docker 和 kubelet 的 cgroup driver 必须一致

cgroup driver 一致性检查

不一致会导致资源管理异常,节点出现莫名的内存压力或 CPU 限制失效:

grep SystemdCgroup /etc/containerd/config.toml
# 或
grep cgroupDriver /var/lib/kubelet/config.yaml

Step 4 — 检查节点资源

磁盘空间(最常见原因)

kubelet 的 DiskPressure 检测默认阈值:磁盘空间低于 10% 触发。

# 查看所有挂载点使用率,重点关注 / 和 /var/lib/containerd
df -h

# 查看 /var/lib 目录大小分布
sudo du -sh /var/lib/*

# 清理步骤(注意顺序):
# 1. 清理未使用的镜像(不要直接删 overlay2 目录!)
sudo ctr -n k8s.io images prune -f

# 2. 清理旧的 journal 日志
sudo journalctl --vacuum-time=3d

# 3. 清理已停止的容器
sudo crictl rm -f $(sudo crictl ps -a --state EXITED -q)

# 4. 清理大日志文件
sudo find /var/log -name "*.log" -size +100M -exec ls -lh {} \;

⚠️ 不要盲目删除 /var/lib/containerd/overlay2 下的镜像文件,可能导致正在运行的容器异常。使用 ctr -n k8s.io images prune 清理。

内存压力(MemoryPressure)

free -m
# available 列低于总内存 10% → 内存压力

# 按内存排序查看进程
ps aux --sort=-%mem | head -20

# 查看 kubelet 内存使用
ps -p $(pgrep kubelet) -o pid,vsz,rss,comm

Pod 驱逐顺序: BestEffort → Burstable → Guaranteed。

CPU 负载

uptime
# load average 持续高于 CPU 核心数 → CPU 资源紧张
# CPU 不足一般不会直接导致 NotReady,除非导致 kubelet 心跳超时

PID 压力(PIDPressure)

cat /proc/sys/kernel/pid_max
ps aux | wc -l
# 进程数接近 pid_max 时触发

综合诊断脚本

#!/bin/bash
# node_diag.sh
echo "===== CPU 核心数和负载 ====="
nproc; uptime
echo "===== 内存使用 ====="; free -m
echo "===== 磁盘使用 ====="; df -h | grep -v tmpfs
echo "===== 进程 Top 10 ====="; ps aux --sort=-%cpu | awk 'NR==1{print} NR>1&&NR<=12'
echo "===== kubelet 状态 ====="; systemctl is-active kubelet
echo "===== containerd 状态 ====="; systemctl is-active containerd 2>/dev/null
echo "===== kubelet 最新错误 ====="; journalctl -u kubelet -n 20 --no-pager | grep -i error

Step 5 — 检查网络连通性

# 1. 测试到 API Server 的连通性
APISERVER=$(kubectl config view --raw -o jsonpath='{.clusters[0].cluster.server}')
curl -sk --max-time 5 ${APISERVER}/healthz
# 正常返回: {"status":"ok"}

# 2. 测试 DNS 解析
ping -c 3 kubernetes.default.svc

# 3. 检查网络接口和路由
ip addr
ip route

# 4. 检查 CNI 插件
# Flannel:
ip addr | grep flannel
kubectl logs -n kube-system -l app=flannel --tail=50

# Calico:
kubectl get pods -n kube-system -l k8s-app=calico-node
calicoctl node status
calicoctl get ippool -o wide

# 5. 检查 NetworkPolicy(罕见但需排除)
kubectl get networkpolicy -A

Step 6 — 检查证书是否过期

kubelet 证书默认有效期 1 年(kubeadm 部署的集群)。集群运行超过 1 年后首次出现,或手动调整过系统时间后出现。

# SSH 到目标节点(节点 SSH 安全配置参见 [[ssh-brute-force-protection-guide]])

# 检查控制面证书
sudo kubeadm certs check-expiration --cert-dir /etc/kubernetes/pki

# 检查 kubelet 自身证书(存放在不同位置)
sudo openssl x509 -in /var/lib/kubelet/pki/cert.crt -noout -dates
date  # 对比当前时间

# 续期(推荐方式)
sudo cp -r /etc/kubernetes/pki /etc/kubernetes/pki.bak.$(date +%Y%m%d)
sudo kubeadm alpha certs renew kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf

# 紧急方式:删旧证书让 kubelet 自动重新申请
sudo systemctl stop kubelet
sudo rm -rf /var/lib/kubelet/pki/*
sudo systemctl start kubelet

Step 7 — 常见根因场景与修复

场景一:磁盘空间不足(占比最高)

诊断: df -h 磁盘使用率 > 90%,kubectl describe node 显示 DiskPressure=True

修复:

sudo ctr -n k8s.io images prune -f   # 清理未使用镜像
sudo journalctl --vacuum-size=500M    # 清理 journal
sleep 120 && kubectl get node <node>  # 等待自动恢复

场景二:kubelet 进程 OOM(占比第二)

诊断:

sudo dmesg | grep -i "out of memory" | grep -i "kubelet"
sudo journalctl -xb | grep -i "oom" | tail -20

修复: 设置 kubelet 的 OOM Score Adj 为负数(更不容易被 kill)

cat /proc/$(pgrep kubelet)/oom_score_adj  # 当前值
# 编辑 /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
# 添加: Environment="KUBELET_OPTS=--oom-score-adj=-999"

根本解决: 增加节点内存或减少 Pod 数量。

场景三:容器运行时配置不一致

诊断: containerd 和 kubelet 的 cgroup driver 不匹配。

修复: 修改 /etc/containerd/config.tomlsudo systemctl restart containerdsudo systemctl restart kubelet

⚠️ 重启 containerd 会导致节点上所有容器停止,对有状态应用有严重影响。

场景四:内核问题

诊断: uname -r 查看版本;sudo dmesg -T | grep -iE "error|warn|fail|timeout"

修复: 升级内核或重装节点。

场景五:kubelet 配置文件错误

诊断: ps aux | grep kubelet 查看启动命令;手动执行 kubelet 看报错

常见错误: cgroupDriver 不一致、evictionHard 格式错误、serializeImagePulls 值类型错误。

场景六:etcd 连接超时(影响所有节点)

诊断:

kubectl exec -n kube-system <etcd-pod> -- etcdctl endpoint health \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

修复: 恢复 etcd 集群。etcd 恢复后节点自动恢复。


Step 8 — 验证修复结果

# 1. 节点状态
kubectl get nodes -o wide
kubectl describe node <node-name> | grep -A 20 "Conditions"

# 2. Pod 调度
kubectl get pods -o wide --all-namespaces | grep <node-name>
kubectl get pods --all-namespaces | grep -v Running | grep -v Completed

# 3. 业务功能
kubectl get deployment -A

# 4. 节点资源
ssh <node> "df -h / && free -m && uptime"

Step 9 — 预防措施

9.1 节点健康巡检脚本

#!/bin/bash
# k8s_node_health_check.sh(建议 cron 每日执行)
NODES=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}')
for NODE in $NODES; do
  STATUS=$(kubectl get node $NODE -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')
  [ "$STATUS" != "True" ] && echo "[ALERT] NotReady: $NODE"

  DISK=$(ssh $NODE "df -h / | tail -1 | awk '{print \$5}' | sed 's/%//'")
  [ "$DISK" -gt 85 ] && echo "[ALERT] Disk $DISK%: $NODE"
done

9.2 配置 eviction threshold

# /var/lib/kubelet/config.yaml
evictionHard:
  memory.available: "100Mi"
  nodefs.available: "5%"
  imagefs.available: "10%"
evictionSoft:
  memory.available: "200Mi"
  nodefs.available: "10%"
  imagefs.available: "15%"
evictionSoftGracePeriod:
  memory.available: "2m"
  nodefs.available: "2m"
  imagefs.available: "2m"
evictionPressureTransitionPeriod: "2m"

9.3 证书自动续期

kubeadm 部署的集群 kubelet 会自动续期。手动管理证书时建立 crontab:

0 3 1 * * /usr/bin/kubeadm certs renew all --kubeconfig=/etc/kubernetes/admin.conf

9.4 Prometheus 告警示例

- alert: NodeDiskPressure
  expr: node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.15
  for: 5m
  labels:
    severity: warning

快速定位表

现象 第一步排查命令 常见根因
DiskPressure=True df -h 磁盘占满(日志/镜像)
MemoryPressure=True free -m / kubectl top pod 节点内存耗尽
kubelet 进程挂 systemctl status kubelet OOM、配置错误
容器运行时异常 systemctl status containerd cgroup 不一致、socket 丢失
网络不可达 curl -sk <apiserver>/healthz CNI 异常、防火墙
证书过期 openssl x509 -in ... -noout -dates 超过 1 年未续期

关联页面

页面关联点
k8s-service-access-troubleshooting服务访问排查(Pod 排障前置条件)
pod-troubleshootingPod 排障(节点 NotReady 后 Pod 被驱逐的后续排查)
service-troubleshootingService 排障(节点恢复后的网络排查)
k8s-production-incident-case-studiesK8s 生产 10 大故障复盘(节点 NotReady/etcd 磁盘等实战案例)
k8s-troubleshooting-principles通用排障原则
network-troubleshooting-order服务器网络排障(节点网络排查参考)
server-performance-four-dimensions服务器四维排障(资源深度排查)
linux-load-average-guideLoad Average 解读(CPU 异常分析)
resource-rbac-scheduling-troubleshooting资源配额与调度排障