kubelet 的 /healthz 不够可靠,因其仅检查进程存活,不验证 containerd、cadvisor、磁盘、内存等真实状态;agent 需直连底层探测并严格限制自身资源以防误判。

为什么用 kubelet 的 /healthz 不够可靠
因为 /healthz 只检查 kubelet 进程是否存活,不反映节点真实资源状态或 cAdvisor、containerd 等关键组件是否可响应。线上常见现象是 /healthz 返回 200,但 Pod 却卡在 ContainerCreating 或 ImagePullBackOff —— 实际是 containerd socket 响应超时或磁盘 inode 耗尽。
Agent 必须绕过 kubelet,直连底层服务做探测:
-
containerd:用ctr --address /run/containerd/containerd.sock info验证 socket 可达性与基础响应 -
cAdvisor:访问http://127.0.0.1:10250/metrics/cadvisor(需 kubelet 开启--cadvisor-port)确认指标采集通道正常 - 磁盘健康:读取
/proc/mounts后对每个挂载点调用syscall.Statfs检查f_bavail和f_files - 内存压力:解析
/sys/fs/cgroup/memory/kubepods/memory.pressure(cgroup v2)或/proc/pressure/memory
如何避免 Agent 自身被 OOMKilled 导致误判
Agent 若无内存限制,在节点资源紧张时可能先于业务容器被杀,导致健康检查“假阳性”——上报节点失联,实则只是 Agent 挂了。
必须在 DaemonSet 中硬性约束资源,并关闭 Go runtime 的内存抖动:
立即学习“go语言免费学习笔记(深入)”;
- Pod spec 中设置
resources.limits.memory: "32Mi",并配resources.requests.memory: "16Mi" - 启动时加
GOMEMLIMIT=24Mi环境变量,让 Go runtime 主动触发 GC 避免突破 limit - 禁用
debug.SetGCPercent(-1)类操作;定期用runtime.ReadMemStats记录 RSS,超 20Mi 时主动 log 并退出
示例健康检查主循环节选:
for range time.Tick(10 * time.Second) {
if memStats.Sys > 20*1024*1024 {
log.Println("memory usage too high, exiting")
os.Exit(1)
}
// ... 其他探测逻辑
}
node-problem-detector 和自研 Agent 的边界在哪
node-problem-detector 是日志模式驱动的被动检测器,依赖解析 kubelet、kernel 日志匹配预设正则。它反应慢(秒级延迟)、漏报多(如瞬时 cgroup v2 pressure spike 不写日志),且无法执行修复动作。
自研 Agent 应聚焦三件事:
- 主动低开销轮询:所有探测控制在
100ms内完成,总耗时per cycle - 带上下文的状态聚合:比如 “disk pressure” 不只看
inodes ,还要结合 <code>containerdlist images 耗时是否 > 2s - 输出结构化事件:往
/dev/stdout打 JSON 行,字段含reason、severity(warning/critical)、component("containerd", "rootfs")
别试图复刻 NPD 的日志解析能力——那是运维侧 pipeline 的事,Agent 只管“此刻能不能干活”。
DaemonSet 中挂载宿主机路径的坑
Agent 需读取 /proc、/sys、/run/containerd 等路径,但默认 hostPath 挂载会因 SELinux 或 containerd rootless 模式失败。
必须显式声明挂载参数:
-
/proc:用hostPath.type: DirectoryOrCreate,不可用Directory(某些系统首次启动时 /proc 下子目录未就绪) -
/run/containerd:指定hostPath.type: Socket,否则挂载后文件权限为 0600 但非 root 用户无法访问 - 若节点启用 cgroup v2,需挂载
/sys/fs/cgroup且readOnly: false(因要读memory.pressure) - 务必加
securityContext.privileged: false,靠capabilities补足:仅需["SYS_ADMIN"](用于Statfs)和["NET_BIND_SERVICE"](若监听本地端口)
错误配置会导致 stat /run/containerd/containerd.sock: no such file or directory —— 实际是挂载失败后路径为空,而非 socket 不存在。
Agent 最容易被忽略的不是探测逻辑,而是它和节点上其他进程共享同一套内核资源视图。比如 Statfs 返回的可用 inodes 数,受 ext4 journal 大小、reserved blocks 影响,而这些值在不同发行版默认不同。不校准就报警,等于每天都在制造噪音。










