mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
3693 字
18 分钟
Node Problem Detector (NPD) 完全指南

Node Problem Detector (NPD) 完全指南#

一、NPD简介#

1.1 什么是Node Problem Detector?#

Node Problem Detector (NPD) 是一个Kubernetes官方的节点问题检测工具,旨在使集群管理堆栈中的上游层能够看到各种节点问题。它作为一个守护进程在每个节点上运行,检测节点问题并将其报告给Kubernetes API Server。

官方仓库https://github.com/kubernetes/node-problem-detector

1.2 为什么需要NPD?#

在Kubernetes集群中,大量节点问题可能会影响节点上运行的Pod,例如:

  • 基础设施守护进程问题:NTP服务关闭、SSH服务异常
  • 硬件问题:CPU、内存或磁盘损坏、网络故障
  • 内核问题:内核死锁、文件系统损坏、内核崩溃
  • 容器运行时问题:Docker/Containerd守护进程无响应
  • Kubernetes组件问题:Kubelet频繁重启、Kube-proxy异常

在NPD出现之前,这些问题对于集群管理堆栈中的上游层是不可见的,因此Kubernetes会继续将Pod调度到有问题的节点上,导致应用不稳定。

NPD的引入解决了这个问题,它能够:

  • 实时检测节点层面的各种问题
  • 将问题报告给Kubernetes API Server
  • 触发自动化的故障处理流程
  • 提高集群的稳定性和可靠性

1.3 NPD的应用场景#

NPD已经被广泛应用于生产环境中:

  • GKE (Google Kubernetes Engine):作为默认启用的Kubernetes Addon运行
  • AKS (Azure Kubernetes Service):作为Linux Extension的一部分默认启用
  • 企业自建集群:用于提升集群的故障检测和自动恢复能力

1.4 部署方式#

NPD支持多种部署方式:

  • DaemonSet部署:作为Kubernetes DaemonSet在每个节点上运行(推荐)
  • 独立部署:作为独立的守护进程运行,适用于非Kubernetes环境
  • 混合部署:结合两种方式,满足不同场景需求

二、核心概念和架构#

2.1 Problem API#

NPD使用两种方式向Kubernetes API Server报告问题:

NodeCondition#

用于报告导致节点无法用于Pod的永久性问题,这些条件会直接影响节点的调度状态。

常见的NodeCondition类型:

  • KernelDeadlock:内核死锁
  • ReadonlyFilesystem:文件系统只读
  • FrequentKubeletRestart:Kubelet频繁重启
  • FrequentDockerRestart:Docker频繁重启
  • FrequentContainerdRestart:Containerd频繁重启
  • KubeletUnhealthy:Kubelet不健康
  • ContainerRuntimeUnhealthy:容器运行时不健康

Event#

用于报告对Pod影响有限但具有参考意义的临时性问题。这些事件不会直接影响节点的调度状态,但可以用于故障诊断和趋势分析。

2.2 架构设计#

NPD采用模块化的架构设计,主要包含以下几个核心组件:

┌─────────────────────────────────────────────────────────────┐
│ Node Problem Detector │
├─────────────────────────────────────────────────────────────┤
│ Problem Daemons (问题守护进程) │
│ ├─ SystemLogMonitor (系统日志监控) │
│ ├─ SystemStatsMonitor (系统统计监控) │
│ ├─ CustomPluginMonitor (自定义插件监控) │
│ └─ HealthChecker (健康检查) │
├─────────────────────────────────────────────────────────────┤
│ Exporters (导出器) │
│ ├─ Kubernetes Exporter (Kubernetes API) │
│ ├─ Prometheus Exporter (Prometheus指标) │
│ └─ Stackdriver Exporter (Google Cloud监控) │
└─────────────────────────────────────────────────────────────┘

2.3 工作流程#

  1. 问题检测:Problem Daemons持续监控节点的各种状态和日志
  2. 问题分析:根据预定义的规则分析检测到的问题
  3. 问题报告:通过Exporters将问题报告到目标系统
  4. 问题处理:Kubernetes或其他系统根据报告的问题采取相应措施
  5. 状态更新:持续跟踪问题的状态变化

三、Problem Daemons详解#

3.1 SystemLogMonitor (系统日志监控)#

功能说明#

系统日志监控器监视系统日志并根据预定义的规则报告问题和指标。它能够检测内核级别的错误、系统服务的异常等。

支持的配置类型#

配置类型说明示例文件
filelog文件日志监控kernel-monitor-filelog.json
kmsg内核消息监控kernel-monitor.json
kernel内核监控kernel-monitor-counter.json
abrtABRT (Automatic Bug Reporting Tool) 监控systemd-monitor-counter.json
systemdSystemd服务监控systemd-monitor-counter.json

检测的问题类型#

  • KernelDeadlock:内核死锁
  • ReadonlyFilesystem:文件系统变为只读
  • FrequentKubeletRestart:Kubelet频繁重启
  • FrequentDockerRestart:Docker频繁重启
  • FrequentContainerdRestart:Containerd频繁重启

配置示例#

{
"plugin": "kmsg",
"logPath": "/dev/kmsg",
"lookback": "5m",
"bufferSize": 10,
"source": "kernel-monitor",
"conditions": [
{
"type": "KernelDeadlock",
"reason": "KernelDeadlock",
"message": "kernel: BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]"
}
],
"metrics": [
{
"name": "kernel_deadlock_total",
"help": "Number of kernel deadlocks detected",
"labels": ["reason"]
}
]
}

3.2 SystemStatsMonitor (系统统计监控)#

功能说明#

系统统计监控器用于收集各种与健康相关的系统统计信息作为指标,包括CPU使用率、内存使用情况、磁盘IO等。

监控指标#

  • CPU使用率
  • 内存使用率
  • 磁盘使用率和IO
  • 网络流量
  • 系统负载

配置示例#

{
"plugin": "system-stats-monitor",
"metrics": [
{
"name": "node_cpu_usage_percentage",
"help": "CPU usage percentage",
"type": "gauge"
},
{
"name": "node_memory_usage_percentage",
"help": "Memory usage percentage",
"type": "gauge"
}
]
}

3.3 CustomPluginMonitor (自定义插件监控)#

功能说明#

自定义插件监控器允许用户编写自定义的检查脚本来监控特定的问题,提供了极大的灵活性。

使用场景#

  • 监控特定的应用程序状态
  • 检查自定义的服务健康状态
  • 集成第三方的监控工具
  • 实现业务特定的故障检测逻辑

配置示例#

{
"plugin": "custom",
"pluginConfigFile": "/etc/kubernetes/node-problem-detector/custom-plugin-config.json",
"metrics": [
{
"name": "custom_plugin_check_total",
"help": "Number of custom plugin checks",
"labels": ["result"]
}
]
}

插件脚本示例#

#!/bin/bash
# NTP问题检测脚本
# 检查NTP服务状态
if ! systemctl is-active --quiet ntpd; then
echo "NTP service is not running"
exit 1
fi
# 检查NTP同步状态
ntpq -p | grep "*"
if [ $? -ne 0 ]; then
echo "NTP is not synchronized"
exit 1
fi
echo "NTP is healthy"
exit 0

3.4 HealthChecker (健康检查)#

功能说明#

健康检查器用于检查Kubelet和容器运行时的健康状况,确保Kubernetes核心组件的正常运行。

检测的问题类型#

  • KubeletUnhealthy:Kubelet不健康
  • ContainerRuntimeUnhealthy:容器运行时不健康

检查机制#

  • 定期检查Kubelet的健康端点
  • 监控容器运行时的状态
  • 检查关键服务的运行时间
  • 分析相关日志的错误模式

配置选项#

配置项说明默认值
component组件名称-
service服务名称-
enableRepair是否启用自动修复false
healthCheckTimeout健康检查超时时间30s
coolDownTime修复后的冷却时间5m

四、Exporters详解#

4.1 Kubernetes Exporter#

功能说明#

Kubernetes Exporter向Kubernetes API Server报告节点问题:

  • 临时问题报告为Event
  • 永久问题报告为NodeCondition

工作原理#

  1. 接收来自Problem Daemons的状态信息
  2. 根据问题的严重程度决定报告方式
  3. 调用Kubernetes API更新Node状态或创建Event
  4. 处理API调用的错误和重试

配置参数#

参数说明默认值
—enable-k8s-exporter是否启用Kubernetes导出器true
—apiserver-override自定义API Server地址-
—addressHTTP服务绑定地址127.0.0.1
—portHTTP服务绑定端口20256

HTTP端点#

  • /healthz:健康检查端点
  • /conditions:当前节点条件
  • /debug/pprof:性能分析端点

4.2 Prometheus Exporter#

功能说明#

Prometheus Exporter将节点问题和指标本地报告为Prometheus指标,便于与Prometheus监控系统集成。

指标类型#

  • Counter:计数器类型,记录问题发生的次数
  • Gauge:量规类型,记录当前问题的状态

配置参数#

参数说明默认值
—prometheus-addressPrometheus抓取端点地址127.0.0.1
—prometheus-portPrometheus抓取端点端口20257

指标示例#

# HELP node_problem_detector_problem_total Number of times a specific type of problem have occurred.
# TYPE node_problem_detector_problem_total counter
node_problem_detector_problem_total{reason="KernelDeadlock"} 1
node_problem_detector_problem_total{reason="ReadonlyFilesystem"} 0
# HELP node_problem_detector_problem_gauge Whether a specific type of problem is affecting node or not.
# TYPE node_problem_detector_problem_gauge gauge
node_problem_detector_problem_gauge{type="KernelDeadlock",reason="KernelDeadlock"} 1
node_problem_detector_problem_gauge{type="ReadonlyFilesystem",reason="ReadonlyFilesystem"} 0

4.3 Stackdriver Exporter#

功能说明#

Stackdriver Exporter向Google Cloud Stackdriver Monitoring API报告节点问题和指标,适用于GKE环境。

配置示例#

{
"project_id": "your-project-id",
"monitored_resource_type": "gke_instance",
"monitored_resource_labels": {
"project_id": "your-project-id",
"location": "us-central1-a",
"cluster_name": "your-cluster",
"instance_id": "node-1"
}
}

五、代码结构分析#

5.1 项目结构#

node-problem-detector/
├── pkg/ # 核心代码包
│ ├── custompluginmonitor/ # 自定义插件监控
│ ├── exporters/ # 导出器实现
│ │ ├── k8sexporter/ # Kubernetes导出器
│ │ ├── prometheusexporter/ # Prometheus导出器
│ │ └── stackdriver/ # Stackdriver导出器
│ ├── healthchecker/ # 健康检查
│ ├── logcounter/ # 日志计数器
│ ├── problemdaemon/ # 问题守护进程框架
│ ├── problemdetector/ # 问题检测器核心
│ ├── problemmetrics/ # 问题指标管理
│ ├── systemlogmonitor/ # 系统日志监控
│ ├── systemstatsmonitor/ # 系统统计监控
│ ├── types/ # 类型定义
│ ├── util/ # 工具函数
│ └── version/ # 版本信息
├── cmd/ # 命令行工具
├── config/ # 配置文件
├── deployment/ # 部署文件
├── test/ # 测试文件
└── Makefile # 构建脚本

5.2 核心组件分析#

5.2.1 ProblemDaemon (问题守护进程)#

Register函数

// 注册问题守护进程工厂方法
func Register(problemDaemonType types.ProblemDaemonType, handler types.ProblemDaemonHandler) {
handlers[problemDaemonType] = handler
}

NewProblemDaemons函数

// 根据配置创建所有问题守护进程
func NewProblemDaemons(monitorConfigPaths types.ProblemDaemonConfigPathMap) []types.Monitor {
problemDaemonMap := make(map[string]types.Monitor)
for problemDaemonType, configs := range monitorConfigPaths {
for _, config := range *configs {
if _, ok := problemDaemonMap[config]; ok {
// 跳过重复配置
klog.Warningf("Duplicated problem daemon configuration %q", config)
continue
}
problemDaemonMap[config] = handlers[problemDaemonType].CreateProblemDaemonOrDie(config)
}
}
problemDaemons := []types.Monitor{}
for _, problemDaemon := range problemDaemonMap {
problemDaemons = append(problemDaemons, problemDaemon)
}
return problemDaemons
}

5.2.2 ProblemDetector (问题检测器)#

Run函数

// 启动问题检测器
func (p *problemDetector) Run(ctx context.Context) error {
// 启动所有监控器
var chans []<-chan *types.Status
failureCount := 0
for _, m := range p.monitors {
ch, err := m.Start()
if err != nil {
klog.Errorf("Failed to start problem daemon %v: %v", m, err)
failureCount++
continue
}
if ch != nil {
chans = append(chans, ch)
}
}
allMonitors := p.monitors
if len(allMonitors) == failureCount {
return fmt.Errorf("no problem daemon is successfully setup")
}
defer func() {
for _, m := range allMonitors {
m.Stop()
}
}()
ch := groupChannel(chans)
klog.Info("Problem detector started")
for {
select {
case <-ctx.Done():
return nil
case status := <-ch:
for _, exporter := range p.exporters {
exporter.ExportProblems(status)
}
}
}
}

5.2.3 ProblemMetricsManager (问题指标管理器)#

SetProblemGauge函数

// 设置问题量规的值
func (pmm *ProblemMetricsManager) SetProblemGauge(problemType string, reason string, value bool) error {
if pmm.problemGauge == nil {
return errors.New("problem gauge is being set before initialized.")
}
pmm.problemTypeToReasonMutex.Lock()
defer pmm.problemTypeToReasonMutex.Unlock()
// 清除之前的原因,确保每个问题类型在任何时刻都最多只有一个原因被设置为1
if lastReason, ok := pmm.problemTypeToReason[problemType]; ok {
err := pmm.problemGauge.Record(map[string]string{"type": problemType, "reason": lastReason}, 0)
if err != nil {
return fmt.Errorf("failed to clear previous reason %q for type %q: %v",
problemType, lastReason, err)
}
}
pmm.problemTypeToReason[problemType] = reason
var valueInt int64
if value {
valueInt = 1
}
return pmm.problemGauge.Record(map[string]string{"type": problemType, "reason": reason}, valueInt)
}

5.3 HealthChecker (健康检查器)#

healthChecker结构体

type healthChecker struct {
component string
service string
enableRepair bool
healthCheckFunc func() (bool, error)
repairFunc func()
uptimeFunc func() (time.Duration, error)
crictlPath string
healthCheckTimeout time.Duration
coolDownTime time.Duration
loopBackTime time.Duration
logPatternsToCheck map[string]int
}

字段说明

  • component:被检查的Kubernetes组件名称
  • service:服务名称或标识符
  • enableRepair:是否启用自动修复
  • healthCheckFunc:健康检查函数
  • repairFunc:修复函数
  • uptimeFunc:获取服务运行时间的函数
  • healthCheckTimeout:健康检查超时时间
  • coolDownTime:修复后的冷却时间
  • loopBackTime:日志回溯时间
  • logPatternsToCheck:需要检查的日志模式

六、部署和配置#

6.1 搭建Kind集群#

基于kind搭建测试集群快速创建一个可用的测试集群。

配置:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
apiServerAddress: "192.168.1.16"
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 6443
hostPort: 6443
listenAddress: "192.168.1.16"
protocol: tcp
- role: control-plane
- role: control-plane
- role: worker
- role: worker

创建高可用集群命令:

sudo kind create cluster --config=huari.yaml --name huari-test --image kindest/node:v1.34.0 --retain; sudo kind export logs --name huari-test

切换kubectl上下文:

sudo kubectl cluster-info --context kind-huari-test

查看信息:

# 查看集群节点
sudo kubectl get nodes
# 查看集群全部的pod
sudo kubectl get pods -A -owide

删除集群:

sudo kind delete cluster --name huari-test

6.2 Helm部署#

# 添加Helm仓库
helm repo add deliveryhero https://charts.deliveryhero.io/
# 安装NPD
helm install node-problem-detector deliveryhero/node-problem-detector \
--namespace kube-system \
--create-namespace
# 安装NPD(代理)
helm upgrade node-problem-detector deliveryhero/node-problem-detector \
--namespace kube-system \
--reuse-values \
--set image.repository=m.daocloud.io/k8s.gcr.io/node-problem-detector/node-problem-detector \
--set image.tag=v0.8.15

6.3 配置参数#

启动参数#

参数说明默认值
—version打印版本信息-
—hostname-override自定义节点名称-
—config.system-log-monitor系统日志监控配置文件路径-
—config.system-stats-monitor系统统计监控配置文件路径-
—config.custom-plugin-monitor自定义插件监控配置文件路径-
—enable-k8s-exporter启用Kubernetes导出器true
—prometheus-addressPrometheus抓取端点地址127.0.0.1
—prometheus-portPrometheus抓取端点端口20257

七、使用和测试#

7.1 基本使用#

查看NPD日志#

# 查看NPD Pod日志
kubectl logs -n kube-system -l app=node-problem-detector -f
# 查看特定节点的NPD日志
kubectl logs -n kube-system node-problem-detector-xxxxx -f

查看节点状态#

# 查看节点条件
kubectl describe node <node-name>
# 查看节点事件
kubectl get events --field-selector involvedObject.name=<node-name>

查看Prometheus指标#

# 启动kubectl proxy
kubectl proxy --port=8080
# 访问Prometheus端点
curl http://<node-ip>:20257/metrics

7.2 测试NPD功能#

测试内核死锁检测#

# 在一个终端中监控事件
kubectl get events -w
# 在节点上注入测试消息
sudo sh -c "echo 'kernel: BUG: unable to handle kernel NULL pointer dereference at TESTING' >> /dev/kmsg"

测试Docker挂起检测#

# 在节点上注入测试消息
sudo sh -c "echo 'kernel: INFO: task docker:20744 blocked for more than 120 seconds.' >> /dev/kmsg"

八、Remedy Systems (补救系统)#

8.1 概述#

Remedy Systems是一个或多个旨在尝试解决NPD检测到的问题的过程。它们会观察NPD发出的事件和/或节点状况,并采取措施使Kubernetes集群恢复健康状态。

8.2 常见的Remedy Systems#

8.2.1 Draino#

功能:根据标签和节点条件自动排空Kubernetes节点。

工作原理

  • 监控节点条件和标签
  • 匹配特定条件的节点被标记为不可调度
  • 在可配置的时间后执行排空操作
  • 可以与Cluster Autoscaler结合使用自动终止节点

使用场景

  • 自动处理硬件故障节点
  • 节点维护自动化
  • 成本优化(自动终止空闲节点)

GitHubhttps://github.com/planetlabs/draino

8.2.2 Descheduler#

功能:取消调度违反NoSchedule污点的Pod。

工作原理

  • 监控节点的污点状态
  • 驱逐违反NoSchedule污点的Pod
  • 确保Pod调度到健康的节点

要求

  • 启用Kubernetes调度器的TaintNodesByCondition功能

GitHubhttps://github.com/kubernetes-sigs/descheduler

8.2.3 Cluster Autoscaler#

功能:自动扩展Kubernetes集群的节点数量。

工作原理

  • 监控Pod的调度状态
  • 当有Pod无法调度时,自动增加节点
  • 当节点空闲时,自动减少节点

与NPD集成

  • 自动终止被排空的节点
  • 替换不健康的节点

GitHubhttps://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler

8.2.4 mediK8S#

功能:基于Node Health Check Operator (NHC)构建的自动修复系统。

工作原理

  • 监控节点状况
  • 使用修复API将修复委托给外部修复程序
  • 支持有条件的修复和手动暂停

GitHubhttps://github.com/medik8s

8.2.5 Poison-Pill#

功能:重新启动节点并确保所有有状态的工作负载都得到重新安排。

工作原理

  • 检测到严重问题时触发
  • 执行节点重启操作
  • 确保工作负载的重新调度

GitHubhttps://github.com/medik8s/poison-pill

8.2.6 Cluster API MachineHealthCheck#

功能:负责修复不健康的机器。

工作原理

  • 监控Machine对象的健康状态
  • 自动替换不健康的Machine
  • 与Cluster API集成

文档https://cluster-api.sigs.k8s.io/developer/architecture/controllers/machine-health-check

8.3 集成方案#

方案一:NPD + Draino + Cluster Autoscaler#

NPD检测问题 → 标记节点为不可调度 → Draino排空节点 → Cluster Autoscaler终止节点 → 自动创建新节点

方案二:NPD + Descheduler#

NPD检测问题 → 设置节点污点 → Descheduler驱逐Pod → Pod重新调度到健康节点

方案三:NPD + mediK8S#

NPD检测问题 → NHC监控状态 → mediK8S执行修复 → Poison-Pill重启节点

附录#

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Node Problem Detector (NPD) 完全指南
https://hua-ri.cn/posts/node-problem-detector完全指南/
作者
花日
发布于
2025-07-25
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时