K8s Node Exporter 异常 Write 超时排查实录

适用人群: Kubernetes 运维工程师、监控运维

适用场景: Prometheus + Node Exporter 监控栈

核心内容: 从内核版本到容器配置,定位 Node Exporter 大量 write 超时的根因


前言

公司网络变更重启服务器后,Prometheus 监控中 3 个 Node 节点掉线。实际上节点服务器正常运行,但 Node Exporter 的 /metrics 接口响应超时,导致 Prometheus 抓取失败。

本文记录了完整的排查过程,最终定位到 内核版本 bug,并给出了临时和永久解决方案。


一、问题现象

1.1 监控表现

  • Prometheus 显示 3 个 Node 节点 DOWN
  • 节点服务器本身运行正常(SSH 可登录、业务正常)
  • Node Exporter Pod 运行状态正常

1.2 手动测试

bash 复制代码
# 在 Prometheus 服务器上测试
curl -o /dev/null -s -w '%{time_connect}:%{time_starttransfer}:%{time_total}\n' 'http://<NodeIP>:9100/metrics'

结果:

节点 time_connect time_total 状态
163 0.001s 0.005s ✅ 正常
167 0.001s 0.004s ✅ 正常
其他 0.001s > 30s ❌ 超时

关键发现: time_connect 很快(排除网络问题),但 time_total 超长,说明问题在 Node Exporter 处理请求阶段。


二、排查过程

2.1 查看 Pod 日志

bash 复制代码
kubectl -n monitoring logs -f <node-exporter-pod-name>

发现大量错误:

ini 复制代码
ts=2023-01-11T06:11:08.189Z caller=stdlib.go:105 level=error
  caller="error encoding and sending metric family: write tcp <内网IP>:<端口>"
  msg="-> <内网IP>:40743: write: broken pipe"

错误含义: Node Exporter 在编码和发送 metrics 数据时,TCP 连接被对端(Prometheus)关闭了(broken pipe)。

2.2 初步排查方向

根据社区经验和资料,可能的原因:

  1. Prometheus 的 scrape_timeout 太短
  2. Node Exporter 资源限制(CPU/内存)
  3. Node Exporter 配置问题
  4. 内核或系统层面问题

2.3 尝试方案一:调大 scrape_timeout

修改 Prometheus 配置,增加抓取超时时间:

yaml 复制代码
# prometheus.yml
scrape_configs:
  - job_name: 'node'
    scrape_timeout: 60s  # 从默认 10s 调大

结果: 问题依旧,不是 scrape_timeout 的问题。

2.4 尝试方案二:调大 Node Exporter 资源限制

检查当前 Node Exporter 的资源配置:

yaml 复制代码
# 当前配置(没有设置 request/limit)
resources: {}

结果: 不是资源限制问题。

2.5 尝试方案三:修改 Node Exporter 配置

参考社区方案,修改 DaemonSet 配置:

yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  template:
    spec:
      containers:
      - name: node-exporter
        args:
        - --web.listen-address=$(HOSTIP):9100
        - --path.procfs=/host/proc
        - --path.sysfs=/host/sys
        - --path.rootfs=/host          # 修改:原来是 /rootfs
        - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
        - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
        - --log.level=debug
        env:
        - name: HOSTIP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        volumeMounts:
        - mountPath: /host/proc
          name: proc
        - mountPath: /host/sys
          name: sys
        - mountPath: /host           # 修改:原来是 /rootfs
          name: rootfs
      volumes:
      - hostPath:
          path: /
          type: Directory
        name: rootfs

观察一天后: 问题依旧,其中一个节点报权限不足。

2.6 尝试方案四:添加 runAsUser

yaml 复制代码
securityContext:
  runAsUser: 0   # 添加:默认是 nobody 用户(UID 65534)

再观察一天: 问题仍然存在,大量 write: broken pipe 错误。


三、根因定位

3.1 关键线索

排查过程中发现一个规律:

节点 内核版本 状态
节点 A 5.4.0-132 ❌ 异常
节点 B 5.4.0-132 ❌ 异常
节点 C 5.4.0-132 ❌ 异常
节点 D 5.15.0 ✅ 正常
节点 E 5.15.0 ✅ 正常

结论: 所有内核为 5.4.0-132 的节点都有问题,5.15.0 的节点正常。

3.2 社区验证

在 GitHub 上找到相关 Issue:prometheus/node_exporter#2500

该 Issue 描述了相同的问题:Node Exporter 在高并发或大数据量场景下,特定内核版本会出现 write: broken pipe 错误。

根因: Linux 内核 5.4.0-132 版本存在 bug,影响 Node Exporter 的 metrics 数据发送。


四、解决方案

4.1 临时方案:设置 GOMAXPROCS=1

在 Node Exporter 的环境变量中限制 Go 程序的 CPU 使用:

yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  template:
    spec:
      containers:
      - name: node-exporter
        env:
        - name: GOMAXPROCS
          value: "1"    # 添加此环境变量
        - name: HOSTIP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP

原理: 限制 Go 运行时只使用 1 个 OS 线程,避免内核 bug 触发。

4.2 永久方案:升级内核

bash 复制代码
# Ubuntu/Debian
apt-get update
apt-get install -y linux-image-5.15.0-generic
reboot

或者使用 LTS 内核:

bash 复制代码
# Ubuntu HWE 内核
apt-get install -y linux-generic-hwe-20.04
reboot

五、验证

5.1 验证临时方案

bash 复制代码
# 重启 Node Exporter
kubectl -n monitoring rollout restart daemonset node-exporter

# 观察日志
kubectl -n monitoring logs -f <node-exporter-pod> | grep error
# 应该没有新的 write: broken pipe 错误

# 测试抓取
curl -o /dev/null -s -w '%{time_total}\n' 'http://<NodeIP>:9100/metrics'
# 应该在 1 秒内返回

5.2 验证 Prometheus

bash 复制代码
# 检查 Prometheus Targets
# 访问 Prometheus UI → Status → Targets
# 所有 Node 节点应该显示为 UP

六、完整修复后的 DaemonSet 配置

yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
  labels:
    app: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
      annotations:
        prometheus.io/scrape: "true"
    spec:
      hostNetwork: true
      hostPID: true
      hostIPC: true
      containers:
      - name: node-exporter
        image: prom/node-exporter:latest
        args:
        - --web.listen-address=$(HOSTIP):9100
        - --path.procfs=/host/proc
        - --path.sysfs=/host/sys
        - --path.rootfs=/host
        - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
        - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
        env:
        - name: HOSTIP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: GOMAXPROCS
          value: "1"  # 临时修复内核 bug
        ports:
        - containerPort: 9100
          hostPort: 9100
          protocol: TCP
        securityContext:
          privileged: true
          runAsUser: 0
        volumeMounts:
        - name: proc
          mountPath: /host/proc
        - name: sys
          mountPath: /host/sys
        - name: rootfs
          mountPath: /host
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      volumes:
      - name: proc
        hostPath:
          path: /proc
      - name: sys
        hostPath:
          path: /sys
      - name: rootfs
        hostPath:
          path: /

七、排查思路总结

markdown 复制代码
1. 确认现象
   ├── Prometheus 显示 Node DOWN
   ├── 节点本身正常
   └── curl 测试确认超时

2. 排除网络
   ├── time_connect 很快 → 网络正常
   └── 问题在 Node Exporter 处理阶段

3. 查看日志
   ├── 大量 write: broken pipe
   └── 说明数据发送阶段失败

4. 尝试常见修复
   ├── 调大 scrape_timeout → 无效
   ├── 调大资源限制 → 无效
   ├── 修改挂载路径 → 无效
   └── 添加 runAsUser → 无效

5. 对比分析
   ├── 对比正常和异常节点的差异
   └── 发现内核版本不同

6. 社区验证
   ├── 搜索 GitHub Issue
   └── 确认是内核 bug

7. 修复
   ├── 临时:GOMAXPROCS=1
   └── 永久:升级内核

八、常见问题 FAQ

Q1:GOMAXPROCS=1 会影响性能吗?

对于 Node Exporter 这种轻量级监控程序,影响很小。它主要是采集和暴露 metrics,不需要大量 CPU 计算。

Q2:如何批量检查集群内核版本?

bash 复制代码
kubectl get nodes -o wide
# 或者
ansible all -m command -a "uname -r"

Q3:升级内核会影响业务吗?

升级内核需要重启节点。建议:

  • 逐台升级,避免同时重启
  • 业务低峰期操作
  • 先驱逐 Pod:kubectl drain <node> --ignore-daemonsets

Q4:还有其他监控组件有类似问题吗?

某些版本的 cAdvisor、Process Exporter 也可能受内核版本影响。如果遇到类似问题,可以尝试相同的排查思路。


总结

本次 Node Exporter 异常的根因是 Linux 内核 5.4.0-132 版本的 bug ,导致 Node Exporter 在发送 metrics 数据时出现 write: broken pipe 错误。

关键排查技巧:

  1. 用 curl -w 精确测量各阶段耗时
  2. 对比正常和异常节点的差异(内核版本、配置等)
  3. 善用 GitHub Issue 搜索社区经验

修复方案:

  • 临时:设置 GOMAXPROCS=1
  • 永久:升级到 5.15.0+ 内核

本文基于 Kubernetes 1.18 + Prometheus 2.x + Node Exporter 1.x 环境整理。

相关推荐
众壹新能源科技5 分钟前
逆变器自带无功不够用,加装5MVar SVG后功率因数从0.85回到0.99
运维·人工智能·自动化
..Dauntless..9 分钟前
【Linux】一切皆文件和缓冲区
linux·运维·服务器
QMCY_jason17 分钟前
x86 ubuntu2204 无gui 安装 openeuler 2203 aarch64版本
linux·运维·服务器
重生之小比特22 分钟前
【Linux系统】12 进程程序替换 自定义Shell 命令行解释器
linux·运维·服务器
考拉讲支付24 分钟前
母婴亲子服务平台分账架构设计:多场景多方分账与合规清结算实践
大数据·运维·人工智能·微信小程序·架构·微信公众平台
拖孩25 分钟前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
流烟默29 分钟前
SSE 实战:在 SpringBoot + Vue 项目中零依赖实现轻量级实时消息推送
vue.js·spring boot·后端·sse
upala35 分钟前
【仓颉语言入门 · 第二篇 第 9 课】
后端
LateFrames40 分钟前
“场控软件” 如何容错与运维
运维·容错·led大屏·播控系统·场控软件
あ-1 小时前
CubeSandBox配置CubeMaster CPU 超分
linux·运维·服务器