凌晨两点十七分,手机连续震了三次。PagerDuty 上挂了一条 P1,钉钉群里运维同事已经炸锅:“生产环境某节点 NotReady 了,上面十几个 Pod 全在重启。”
我打开 Grafana,先看节点总览面板,再切到 kubelet 状态,最后拉出这段时间的 CPU、内存和磁盘 IO 曲线。说实话,这种场景我经历了不止一次。真正让我头疼的不是节点挂了这件事本身,而是每次都要在一堆散落的指标里翻找根因,或者在告警风暴里分辨哪些是真问题、哪些是监控自身产生了噪音。
后来我把整套 K8s 节点监控与告警体系重新梳理了一遍,从指标采集、阈值设定、告警路由到报告生成,形成了一套基本可以“抄作业”的方案。这篇文章就把这套方案完整地拆开讲清楚,包括当时踩过的坑、调参的经验和最后沉淀下来的分析模板,希望能给正在做 K8s 监控的运维或平台同学一些参考。
1. 整体设计与思路拆解:K8s 节点监控到底要解决什么问题
1.1 为什么单独把“节点监控”拎出来做
K8s 集群的监控对象很多,API Server、etcd、Controller Manager 这些控制面组件需要盯,Deployment、StatefulSet 这些工作负载资源需要盯,Pod 里的业务容器也需要盯。但节点这一层,往往是最容易被忽略、却又最容易出大事的。
节点是承载一切的基础。节点挂了,上面跑的 Pod 全部受影响;节点磁盘满了,kubelet 会开始驱逐 Pod;节点内存压力大,系统可能直接触发 OOM Killer 随机杀进程;节点时钟漂移,会导致整个集群的证书校验、日志时间戳全部错乱。我见过一个最典型的案例:某次故障排查到最后,发现是节点时间慢了五分钟,导致 Prometheus 拉取的指标时间戳错位、告警全部失效——这种问题,光看应用层的监控根本发现不了。
所以我的思路是:将监控体系按“底座 → 编排层 → 应用层”三层拆分,而节点监控是底座的核心。底座不稳,上层做再多监控都是白搭。这一点需要先想清楚,再去设计指标和告警。
1.2 整体监控架构与技术选型
先把技术选型说清楚。目前主流的开源监控方案无非是 Prometheus 生态、Zabbix、夜莺(Nightingale)这几类。我自己在多个集群里都试过混合方案,最后沉淀下来的核心组合是 Prometheus + node_exporter + kube-state-metrics + Alertmanager + Grafana。
选择 Prometheus 生态的原因很简单:K8s 原生集成度最高,指标采集用服务发现,告警规则直接用 PromQL 写,不用额外造数据模型。Zabbix 在传统物理机、虚拟机监控上确实很强,但对于容器动态调度、Pod 生命周期变化这种场景,模板和自动发现的模型会显得笨重。夜莺在告警分组和通知编排上做得不错,如果团队已经重度使用夜莺,也可以作为告警层的替代,但底层指标采集我还是推荐 Prometheus 体系。
在这个组合里,各组件分工是:
| 组件 | 职责 | 采集目标 |
|---|---|---|
| node_exporter | 采集节点的基础资源指标 | CPU、内存、磁盘、网络、文件系统 |
| kube-state-metrics | 将 K8s 对象状态转为指标 | Pod、Deployment、Node 的状态与数量 |
| cAdvisor(kubelet 内置) | 采集容器运行指标 | 容器 CPU、内存、网络、磁盘 |
| Alertmanager | 接收 Prometheus 告警并处理 | 路由、分组、去重、静默 |
| Grafana | 可视化与报告输出 | 仪表盘、日志关联、PDF 报告 |
这套架构从采集到告警到可视化是一条完整链路。Prometheus 通过 service discovery 拿到节点列表后,定期去 scrape node_exporter 暴露的 9100 端口,拿到指标后存储到 TSDB。告警规则在 Prometheus 里评估,命中就推给 Alertmanager,由它根据路由树决定怎么通知人。Grafana 负责把指标变成人能一眼看懂的图,同时兼顾周报、月报的生成。
1.3 监控设计里最容易犯的三个错误
这个架构看起来简单,但真正把“节点综合监控”做扎实,需要避免几个常见问题。
第一个误区是只盯资源使用率,不盯节点状态。很多团队的节点监控面板上只有 CPU、内存、磁盘几个图,但 kubelet 健康状态、容器运行时状态、节点 Ready 状态这些真正决定集群是否可用的信号反而没有。资源指标只能告诉你“现在紧不紧张”,状态指标才能告诉你“还能不能继续承载业务”。
第二个误区是指标粒度过粗。整个集群的平均 CPU、平均内存意义不大,需要做到按节点维度、按 Pod 维度、按容器维度多级下钻。否则节点内存被某个容器吃满,你在集群平均值上根本看不出来。我的建议是所有关键面板都必须支持按节点、按命名空间、按 Pod 筛选。
第三个误区是告警规则拍脑袋,没有基于容量规划反推阈值。很多团队把 CPU 阈值设成 90%,然后三天两头被误报骚扰。实际上阈值应该结合节点规格、业务流量模型、混部情况来定。比如同样一台 8C16G 的节点,跑的是在线业务和跑的是离线任务,CPU 告警阈值就应该不一样。
2. 核心细节解析与实操要点:从指标采集到 PromQL 实战
2.1 节点层核心指标拆解
节点层指标是整套监控的地基。我按照“信号价值”从高到低整理了以下指标清单,整套体系都是围绕这些指标展开的。
CPU 方向,核心指标是node_cpu_seconds_total。这个是计数器类型,不能直接看原始值,要用 rate 函数算速率。常用的 PromQL 是这样:
1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)这条语句的意思是把 CPU 的空闲时间速率算出来,然后用 1 减掉,得到的是 CPU 使用率。mode 还可以换成iowait,单独看 CPU 等待 IO 的比例。如果 iowait 长期超过 10%,多半是磁盘或存储链路出现了瓶颈。
内存方向,需要特别说明一点:node_memory_MemTotal_bytes减node_memory_MemAvailable_bytes得到的内存使用量,比直接用MemFree更准确。因为 Linux 的 free 内存不包含可回收的 page cache,用 MemAvailable 才能反映真实可用内存。我用的核心指标是:
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100这条语句是内存使用率的常用写法,它把 page cache 等可回收内存也算进了可用部分,排除了“明明 free 很少但系统并不慌”的误报场景。
磁盘方向,除了使用率还要盯 inode。实践中有个很经典的坑:磁盘使用率才 60%,但应用报“No space left on device”,最后发现是 inode 耗尽。node_exporter 提供了node_filesystem_avail_bytes和node_filesystem_files_free两组指标,前者管容量、后者管 inode,都要纳入监控。另外要记得过滤掉 tmpfs、overlay 等虚拟文件系统,不然会有大量无用数据。
网络方向,流量吞吐和连接数都要看。node_network_receive_bytes_total和node_network_transmit_bytes_total用来算网卡速率,node_sockstat_TCP_alloc这类指标用来盯 TCP 连接分配情况。对于高并发业务节点,TCP 连接数暴涨往往比带宽跑满更早暴露问题。
2.2 K8s 专属指标:kubelet 与容器运行时
节点层面的指标再全,也覆盖不了 K8s 的场景。有一类故障是节点本身一切正常,资源也充足,但 kubelet 和容器运行时之间的通信出了问题,导致 Pod 调度后起不来。这时候必须看 K8s 专属指标。
kubelet 的健康状态可以从两个维度看:一是 Prometheus 拉取 kubelet 指标接口是否正常,二是 kubelet 上报的节点状态。kube-state-metrics 暴露的kube_node_status_condition指标可以直接反映节点 Ready、MemoryPressure、DiskPressure、PIDPressure 等状态,配合 condition 的 status 字段筛选:
kube_node_status_condition{condition="Ready", status="true"} == 0这条规则表示节点 Ready 状态不为 true。注意这里可能出现 status 为 unknown 的情况,也属于异常,需要单独加规则覆盖。
容器运行时侧,重点盯两类指标:容器重启次数和 OOMKilled。容器频繁重启通常意味着应用有问题,而 OOMKilled 则表示内存配额设置不合理或者确实有内存泄漏。kube-state-metrics 里kube_pod_container_status_restarts_total是容器重启的计数器,kube_pod_container_status_waiting_reason可以过滤 Waiting 状态的原因是 CrashLoopBackOff 或 OOMKilled。
我见过太多团队直到业务侧反馈“Pod 一直在重启”才开始排查,其实这些指标在监控里早就有信号了。所以这两类容器状态指标是节点监控里不可省略的一环——它们反映的是节点上的“住户”是否住得安稳。
2.3 指标采集落地:node_exporter 部署细节
指标设计完之后,落地采集反而是坑最多的地方。node_exporter 的部署方式我推荐用 DaemonSet,保证每个节点都跑一个实例。如果节点有 taint,需要加上对应的 tolerations。
采集精确度方面,node_exporter 默认的采集器已经覆盖了绝大多数场景,不需要额外开太多。需要注意的是一个指标过滤项:--collector.filesystem.mount-points-exclude。建议把/dev/sd*、/run、/var/lib/docker等系统虚拟路径排除掉,否则node_filesystem_*系列指标会非常庞杂,不仅拖累 Prometheus 存储,还会在告警规则里造成误匹配。
还有一个高频问题是端口冲突。node_exporter 默认监听 9100,但在一些管控严格的集群里,主机上可能有其他 agent 也占了 9100。我习惯在 DaemonSet 里显式声明端口并加上 ServiceMonitor 的 label,这样 Prometheus 的 service discovery 会自动找到目标,省去手工维护 targets 的麻烦。
Prometheus 侧的采集配置也比较关键。scrape_interval 我设为 15 秒,这个频率在大多数场景下足够,同时不会给 TSDB 带来太大压力。如果业务对实时性要求很高,可以缩到 10 秒,但要注意评估存储膨胀和查询变慢的问题。
2.4 PromQL 实战:常用节点监控查询语句
PromQL 是 Prometheus 的查询语言,很多人看到一堆函数就发怵,其实节点监控常用的就那几类写法。我把高频使用的查询语句整理成了一份速查表,直接复制改一改就能用。
CPU 使用率(按节点维度,5 分钟平均):
100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100)内存使用率:
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)磁盘使用率(过滤掉 tmpfs 和 overlay):
100 * (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})磁盘 IO 读写速率:
rate(node_disk_read_bytes_total[5m])网络接收速率:
rate(node_network_receive_bytes_total[5m])节点文件描述符使用率:
100 * node_filefd_allocated / node_filefd_maximum这些查询语句建议在 Grafana 里先逐个验证正确性,再放进告警规则,避免因为 PromQL 本身的括号或聚合维度写错,导致告警规则一直在 pending 或者反复触发。
2.5 阈值设定:参考值与背后的逻辑
阈值的设定是监控系统里最考验经验的部分。定低了,告警轰炸,运维同学很快会“狼来了”麻木掉;定高了,真正出问题时收不到通知,监控形同虚设。
我先给出自己常用的参考阈值,再解释为什么这么定。
| 指标 | 告警阈值 | 持续时长 | 级别 |
|---|---|---|---|
| CPU 使用率 | > 85% | 5 分钟 | Warning |
| CPU 使用率 | > 95% | 5 分钟 | Critical |
| 内存使用率 | > 90% | 5 分钟 | Warning |
| 内存可用量 | < 512MB | 5 分钟 | Critical |
| 磁盘使用率 | > 80% | 10 分钟 | Warning |
| 磁盘使用率 | > 90% | 10 分钟 | Critical |
| inode 使用率 | > 90% | 10 分钟 | Critical |
| 节点 Ready 状态 | != true | 1 分钟 | Critical |
| kubelet 采集失败 | 持续 5 分钟 | 5 分钟 | Critical |
CPU 阈值 85% 的 Warning、95% 的 Critical 是很多团队的经验值,但一定要结合节点规格调。如果节点是 32 核的大规格,85% 意味着还有约 5 核的余量,可以适当放宽;如果是 4 核的小规格,85% 时只剩不到 1 核,业务早就开始抖动了。内存的 Critical 条件我习惯增加“可用量”这个绝对值判断,因为 90% 使用率在 128G 的大内存节点上还有 12G 可用,而在 4G 的小节点上只剩 400M,不可同日而语。
磁盘阈值要按数据盘和系统盘分开设。系统盘猛涨通常是日志或 docker 目录写满,数据盘则是业务数据。如果磁盘容量大(比如 2T),80% 的阈值意味着有 400G 余量,看起来安全,但要注意写入速率,如果每日写入量很大,要给足前置预警时间。
3. 实操过程与核心环节实现:告警体系搭建与告警规则调优
3.1 告警规则的编写技巧与常见坑
Prometheus 的告警规则写在 rules 配置里,格式是 YAML。一个完整的规则包含 alert 名称、expr 表达式、for 持续时长、labels 和 annotations。下面是我常用的一条节点内存告警规则:
groups: - name: node_alerts rules: - alert: NodeMemoryUsageHigh expr: | (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90 for: 5m labels: severity: warning team: ops annotations: summary: "节点 {{ $labels.instance }} 内存使用率超过 90%" description: "当前使用率 {{ $value | printf \"%.2f\" }}%,请及时排查是否有内存泄漏或容量不足。"编写规则时最容易踩的坑有三个。第一个是忘记加for,导致瞬时抖动也能触发告警,一晚上能收到几十条“假警”。第二个是 PromQL 里正则表达式写错,比如fstype=~"tmpfs|overlay"和fstype!~"tmpfs|overlay"很容易搞反,导致把系统关键分区排除在监控外。第三个是 annotations 里的模板语法写错,$labels.instance取不到值,告警内容变成一长串无效字符。
还需要特别提醒:规处的告警一定要保留最近变化的历史,方便复盘。我一般会给 rules 文件加版本注释,每次调整都记录原因,不然三个月后回来看,根本想不起某个阈值为什么从 80% 改成了 85%。
3.2 Alertmanager 配置:路由树、分组与静默
告警规则定义了“什么时候该报警”,Alertmanager 则决定“报给谁、怎么报、要不要合并”。这部分配置直接决定了团队对告警的体感。
路由树是我优先设计的部分。我的做法是按告警级别和团队两个维度做路由分流,确保关键告警能以最高优先级触达对应负责人。下面是一个典型的路由配置:
route: group_by: ['alertname', 'cluster'] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: 'default' routes: - matchers: - severity = critical receiver: 'page-ops' continue: false - matchers: - severity = warning receiver: 'alert-group'其中 group_wait 是同一组告警首次通知的等待时间,group_interval 是组内新增告警的通知间隔,repeat_interval 是相同告警的重复发送间隔。这些参数直接影响告警打扰程度。我建议 group_wait 设短一些(30 秒左右),让告警尽快出去;repeat_interval 设长一些(比如 4 到 8 小时),避免同一问题反复轰炸。
静默(silence)是告警治理的好工具。节点主动维护、计划内扩容这类场景,提前给对应的 instance 加一条静默规则,可以有效避免维护期间产生误报。但静默规则一定要设置过期时间,我见过有人建了一条永久静默最后忘记清理,节点挂了一整天都没人收到告警。所以,静默必须带 schedule 和 expires,宁可到期重新建,也不要留永久静默。
3.3 告警通知渠道:统一 Webhook 接入钉钉/企微
通知渠道的建设,我强烈建议不要直接对接 Prometheus 的 email 或者 Slack 原生集成,而是走统一 Webhook 网关。这样以后更换通知工具,只需要改网关一侧,不用动 Alertmanager 配置。
Alertmanager 配置一个 webhook receiver 非常简单:
receivers: - name: 'webhook' webhook_configs: - url: 'http://alert-gateway:8080/k8s/alert' send_resolved: true http_config: bearer_token: 'your-token'接着在网关里做一次告警内容的标准化:把 Alertmanager 推送过来的 JSON 转换成钉钉或企业微信的卡片消息格式,再按告警级别打上不同颜色标签。这样团队成员在手机上就能快速区分:红色是 Critical,橙色是 Warning,绿色是恢复通知。
这里有个实践细节:send_resolved一定要设为 true。很多人只配置了告警触发通知,恢复通知没开,导致下一个值班同学不知道问题已经解决,还要在群里反复问“现在恢复了吗”。恢复通知不仅是给值班人吃定心丸,也是给整个团队留一份闭环记录。
3.4 告警噪音治理方法论
告警噪音是监控体系里最影响体验的问题。噪音多了,团队成员会逐渐忽略所有告警,真正的 P1 反而没人响应。我治理噪音的方式,核心是“每个告警必须回答三个问题”:影响面多大、需要谁处理、应该多快处理。
按照这个原则,我通常先将告警分 P0/P1/P2 三级。P0 是指节点宕机、API Server 不可用、大面积 Pod 驱逐,要求立即响应,直接打值班人电话;P1 是指某节点磁盘即将写满、内存持续高位,需要 30 分钟内紧急确认;P2 是指资源使用率较高但尚能自愈,进入日常工单池即可。
![告警分级参考]
| 级别 | 典型场景 | 响应要求 | 通知方式 |
|---|---|---|---|
| P0 | 节点 NotReady、kubelet 挂掉、大面积容器 OOM | 立即响应 | 电话 + 语音通知 |
| P1 | 磁盘使用率超 90%、内存可用不足 | 30 分钟内 | 钉钉/企微卡片 |
| P2 | CPU 使用率持续超 85%、inode 偏高 | 2 小时内 | 工单池/邮件 |
然后要定期回顾告警历史,统计哪些告警触发了但最后确认是误报或无需处理,把这类告警的阈值调高或直接下线。我在实际过程中发现,一套新的监控系统上线后的头一个月,至少有 30% 的告警规则是需要调整的。这不是规则写得不好,而是因为实际业务负载模式还没被完全摸清。
还有一个值得注意的点:不要为了告警而告警。有些团队把所有指标都配上告警,结果告警列表一眼望不到头。我更推荐的做法是,每条告警规则都对应一个明确的运维动作——收到告警后应该去执行什么操作,如果动作不明确,这条告警就不应该存在。
4. 监控分析报告与故障复盘:从 Grafana 仪表盘到 PDF 报告
4.1 Grafana 仪表盘设计:节点总览与明细下钻
监控不只有告警,还有日常巡检和分析。Grafana 是这套体系里负责把人眼跟数据连接起来的环节。节点监控的仪表盘,我分成了三个层级:总览、明细、趋势。
总览盘面向值班人员,要求一眼看清整个集群的节点健康状态。我把核心面板设计成一个表格:每个节点一行,列为节点名称、CPU 使用率、内存使用率、磁盘使用率、网络速率、Pod 数量、节点状态。这个表格就是日常巡检的第一落点,任何异常都能在这里被发现。
明细盘面向故障排查,按单节点下钻。点击总览盘的某个节点,进入该节点的详细面板,包含 CPU 各核心分布、内存组成(used/buffers/cache)、磁盘 IO 读写、网络连接数、关键容器状态。这套下钻路径非常关键,它能帮助你在 5 分钟内定位到“节点磁盘 IO 高是因为哪个 Pod 在大量读写”这种级别的问题。
趋势盘面向容量规划。我看的是 7 天和 30 天的资源使用趋势,用于回答“下个月需不需要扩容”这类问题。趋势数据不能只看平均值,要看峰值和 95 分位值,因为平均值掩盖了流量毛刺。
4.2 生成 PDF 监控报告的两种方案
很多团队除了实时看板,还需要定期输出监控报告给管理层或客户,这就用到了 Grafana 的“生成 PDF 监控报告”能力。这里有两种方案,我都实测过,分享下各自的注意事项。
方案一是利用 Grafana 自带的报表插件。装好 grafana-image-renderer 插件后,可以在 Grafana 的 Reporting 功能里配置定时任务,按天/周/月生成当前仪表盘的截图,然后推送到邮箱或企业微信。这个方案上手最快,但输出内容是图片拼接,如果仪表盘面板太多,PDF 会很长,而且不能对每个面板单独加文字说明。
方案二是调用 Grafana HTTP API 按需渲染,再配合脚本做数据汇总。我写过一个简单的 Python 脚本,先通过 API 获取指定时间范围的节点指标,然后用 pandas 做统计汇总,生成资源使用率 Top 10 节点、告警数量分布、持续时长这些结构化数据,最后调用 Grafana 渲染图表,合并成一份完整 PDF。这个方案灵活度高,适合要做深入分析报告的场景。
4.3 从指标到结论:监控分析报告应该包含什么
报告是给决策者看的,不是给监控系统看的。所以一份好的监控分析报告,应该包含结论、数据支撑、风险提示三个部分,而不是简单贴几张图。
我通常的章节结构是:先说本月集群整体运行情况(可用性、最大告警数、主要事件),再给节点资源使用趋势分析和 Top 节点清单,然后是告警分类统计(哪类告警最多、平均响应时长、重复告警占比),最后给出容量规划建议(哪些节点需要扩容、哪些资源可回收)。
这里要特别强调一点:报告里的每一个结论都要有数据支撑。比如写“集群存在容量风险”,就要附上内存使用率连续 7 天超过 85% 的节点列表和趋势图。如果没有数据链支撑,这样的报告没有任何说服力。
我还会在报告中加一个“上期问题跟进”章节,把上个月提出的风险项逐条列出当前状态。这个习惯让监控报告从一个静态文档变成了持续迭代的运维闭环,管理层能明显感受到监控体系的价值。
4.4 故障复盘:怎么让监控数据变成经验
故障复盘是监控数据分析价值最大化的场景。每次 P1 以上故障结束后,我都会强制团队输出一份复盘文档,里面必须包含几个部分:故障时间线(从最先出现的异常指标开始)、监控盲区总结(哪些指标没有覆盖到)、告警有效性回顾(告警是否及时触发、是否被噪音淹没)、改进项清单。
时间线的还原,主要依赖 Prometheus 的历史数据。比如某次故障时间线写着“14:00 内存使用率开始爬升,14:20 触发内存告警,14:35 节点 NotReady,14:50 告警升级到 P1”。有了这条时间线,就能清楚地看到从异常出现到告警触发的间隔是否合理,是阈值设太高导致告警晚了 20 分钟,还是告警规则本身有缺陷。
监控盲区总结是最有价值的内容。每次复盘都可能发现新的监控盲区,比如“这次发现我们竟然没有盯节点时钟偏移”“fs.inotify 上限告警缺失导致应用无法创建文件”。每发现一个盲区,就补一条对应的监控规则,整套体系就是这样一轮一轮迭代完善的。
5. 常见问题与排查技巧实录
5.1 高频问题速查表
整理了一份我在实际运维中常遇到的问题速查表,按症状列出可能原因和排查建议。
| 症状 | 可能原因 | 排查建议 |
|---|---|---|
| 节点状态显示 NotReady | kubelet 与 API Server 通信异常 | 检查 kubelet 日志、网络策略、证书是否过期 |
| 告警规则不触发 | PromQL 语法错误或 for 时长过长 | 在 Prometheus 的 Alerts 页面查看规则状态,确认是否 Pending |
| 告警重复轰炸 | 缺少 Alertmanager 分组配置 | 设置合理的 group_by 和 repeat_interval |
| 磁盘有空间但报 No space | inode 耗尽 | 用 df -i 查看 inode 使用率,清理无效小文件 |
| scrape 目标显示 DOWN | node_exporter 挂掉或网络不通 | 检查 Pod 状态和网络策略,确认 9100 端口可达 |
| Grafana 图表无数据 | 数据源时间范围或标签不匹配 | 检查 Prometheus 数据源查询是否命中,标签名是否一致 |
| 告警内容变量不显示 | 模板语法错误 | 检查 {{ $labels.xxx }} 中的标签名是否在表达式中存在 |
| 节点时钟漂移导致告警异常 | NTP 同步失效 | 检查 chrony/systemd-timesyncd 状态,强制同步并监控时钟偏移 |
这张表不是标准文档的搬运,而是我一次次处理事故后沉淀出来的浓缩经验。特别是 inode 耗尽和时钟漂移这两个问题,搜索频率极低、但一旦发生就是大事。
5.2 一个完整的告警排查实例
拿一次真实的磁盘告警来演示整体排查思路。某天下午团队收到一条 P1 告警:某节点数据盘使用率超过 85%,持续 10 分钟。
第一步不是直接连上服务器清日志,而是先在 Grafana 上看这个节点的磁盘使用趋势,确认是“缓慢爬升”还是“突然跳变”。结果发现曲线从三天前开始持续向上,斜率比较陡,说明是持续写入,不是一次性爆发。
第二步是在 Prometheus 里查这个节点上所有容器和宿主机的写入速率,用rate(node_disk_writes_bytes_total[5m])按 device 分组,再用容器侧的container_fs_writes_bytes_total按 Pod 分组。交叉对比后发现,某个日志采集组件的 Pod 在大量写宿主机的/var/log。
第三步是登录节点核实。du -sh /var/log确认日志目录已经占了几十个 G,再进到该组件的容器配置里检查日志输出路径和轮转策略,发现它的日志文件没有配置轮转。最后加上 logrotate 配置,清理历史大文件,磁盘使用率恢复正常。整个排查过程不到二十分钟,核心不是运气,而是监控数据让每一步研判都有据可依。
这个例子说明,节点监控的最终价值不是“能收到告警”,而是拿到告警之后,你能用监控数据把问题边界迅速缩小,不让排查过程变成翻山越岭找线索。
5.3 避坑经验合集
最后分享几个我在实际部署和调优过程中踩过的坑,都属于“不上一次当永远记不住”的那种。
第一个坑是关于 node_exporter 的版本升级。某些 node_exporter 版本升级后,指标命名发生了变化(比如node_cpu变成了node_cpu_seconds_total),旧版 Grafana 面板和告警规则里的 PromQL 全部失效。所以升级前一定要先查阅官方 CHANGELOG,先在测试环境升级、校验所有面板和规则,再灰度更新到生产。
第二个坑是关于 Prometheus 的存储时长。很多人忽略了 TSDB 的默认保留时间,默认只有 15 天。做容量规划时想翻历史数据,发现早就被清理了。我在生产环境把--storage.tsdb.retention.time设成了 60 天,并评估了存储容量,避免报告需要数据时无数据可用。
第三个坑是关于标签的高基数问题。比如在告警规则里用 Pod 名称做标签,Pod 重建后标签会不断变化,Prometheus 的指标数量会指数级膨胀,最终拖垮 TSDB 查询性能。标签的设计一定要控制基数,能用标签归类就尽量归纳,不要把唯一的实体名当标签用。
第四个坑是关于 Grafana 的时区。默认时区是 UTC,国内团队直接看图会发现所有时间都偏移了 8 小时。首次部署 Grafana 时就把默认时区改为 Asia/Shanghai,并统一所有看板的时区设置,避免看告警时间还要在心里默默加八小时。
5.4 监控体系上线后的持续运营
最后想聊聊监控体系上线后的运营。很多人以为监控搭好就结束了,其实真正的维护工作才刚刚开始。我建议每个月抽半天时间做一次“告警规则健康检查”:拉出 Prometheus 的告警历史,统计每条规则在过去 30 天的触发次数、平均持续时长、最终是否演变成真实故障。
如果一条规则触发了 20 次,但只有 2 次是真实问题,那这条规则要调。如果一条规则从未触发过,但对应的指标确实发生过严重故障,那说明规则有盲区要补。持续做这项工作,告警系统的信噪比才会越来越高。
另外,监控数据也是容量规划的重要输入。每季度根据节点使用趋势做一次资源水位评估,提前识别出哪些节点会在未来一两个月内触达容量上限,提前扩容或迁移业务,把被动救火变成主动规划。
根据我个人的经验,真正可靠的 K8s 节点监控体系不是在搭建完成那一刻“上线”的,而是在一次次告警、一次次复盘、一次次规则调优中逐步“长”出来的。现在每次看到告警消息,我心里不再是“又出事了”的烦躁,而是“监控没白做”的踏实。也希望这篇文章能帮你少踩一些坑,把节点监控这件事做得更扎实。