简介:面向Kubernetes集群运维与监控工程师,这份资源聚焦node-exporter的部署与告警落地,解决K8s节点CPU、内存、磁盘、网络等主机层指标采集困难、部署步骤分散的问题。资源内含完整部署清单,包括服务、配置、部署、存储声明等5个YAML文件,分别用于暴露监控端口、定义采集参数、管理副本数量、声明存储资源;同时提供node-exporter与Prometheus的离线镜像压缩包,以及README说明文档和告警规则文件,覆盖从指标采集到告警触发的完整链路。资源共7个文件,以YAML清单为主,配合TXT说明与GZ镜像包,整体大小约72.83MB,在离线或内网环境中可直接分发使用,省去联网拉取镜像的步骤,特别适合防火墙受限、无法访问公共镜像仓库的企业环境。目前已有621人学习下载,适合具备基础Kubernetes操作能力、需要快速搭建节点监控与告警体系的运维人员参考。使用该资源可缩短部署时间,按环境调整镜像地址后即可复用,对监控系统初始化、私有化交付场景有较高的实用价值。同时,文件结构简洁,便于对照YAML清单学习组件配置要点。
1. 内网 K8s 主机监控的「开箱组件」:node-exporter 资源包到底能省什么
在一套内网 K8s 集群里做宿主机监控,第一步往往不是装 Grafana,而是先把 node-exporter 跑起来。node-exporter 是 Prometheus 生态里最常见的宿主机指标采集器,部署在每个节点上,暴露 9100 端口,把 CPU、内存、磁盘、网络这些主机维度指标以文本格式暴露给 Prometheus。这份资源包围绕这条链路准备:node-exporter 的部署清单(service.yaml、configmap.yaml、deployment.yaml)、镜像压缩包、Prometheus 镜像,以及配套告警规则,解决的是内网环境里镜像怎么搬、采集器怎么跑、告警怎么挂三件事。
适合刚接手现网集群、发现监控数据不完整的从业者,以及要在离线集群交付监控能力的运维。下面是按部署链路拆文件的实战记录。
2. 拆开资源包看监控链路:node-exporter、Prometheus 与告警规则如何配合
2.1 为什么先选 node-exporter 做宿主机采集
Prometheus 生态里针对 K8s 的采集器有好几个,第一次接触很容易混。cAdvisor 是 Kubelet 内置的,负责容器维度的 CPU、内存、网络;kube-state-metrics 负责 Deployment、Service、Pod 这些资源对象的存在状态;而宿主机本身的负载、磁盘容量、网卡流量、文件系统使用率,前两者都不管。node-exporter 就是补这一层的:它直接读宿主机的 /proc 和 /sys,把内核统计信息转换成 Prometheus 的标准文本指标格式。
这个采集器的定位是「只采集不存储」。它不写数据库、不计算告警、不提供查询接口,唯一的动作是把收集到的指标挂在 9100 端口的 /metrics 路径上,等 Prometheus 按配置来抓。这个设计让部署成本很低:不需要额外存储,不用考虑持久化,Pod 挂了重建一个就行。资源包里的 node-exporter 镜像就按这个最小职责打包,所以部署层面真正要确认的不是采集器本身,而是运行时参数——是否特权模式、是否把宿主机根目录挂进容器、Pod 调度策略。
三个采集器放到一起看,分工差异更直观:
| 采集器 | 监控维度 | 部署形态 | 数据由谁消费 |
|---|---|---|---|
| node-exporter | 宿主机 CPU / 内存 / 磁盘 / 网络 | 每个节点一个 Pod | Prometheus 主动抓取 |
| cAdvisor | 容器运行指标 | Kubelet 内嵌 | Prometheus 抓取 kubelet 端口 |
| kube-state-metrics | 资源对象状态 | 单副本 Deployment | Prometheus 主动抓取 |
选 node-exporter 而不是自己写脚本收集主机信息,是因为它已经覆盖了绝大多数主机维度指标,而且输出格式天然能被 Prometheus 解析。自己写采集脚本最大的问题不是取数,而是格式和标签不规范,到后面写告警规则、画图表时全都要返工。这份资源把镜像和清单一起打包,就是省掉这层自己造轮子的成本。
2.2 清单文件逐个拆:谁负责暴露端口,谁负责启动参数
资源包里 node-exporter 目录下的清单文件分工很明确,逐个看就不会一把梭全 apply 之后不知道出了问题该找哪个文件。
| 文件 | 职责 | 部署时要确认的点 |
|---|---|---|
| service.yaml | 集群内暴露 9100 端口 | 类型是不是 ClusterIP,selector 与 Pod label 是否对齐 |
| configmap.yaml | 注入 node-exporter 启动参数 | 是否有不需要的采集器开关,参数路径是否正确 |
| deployment.yaml | 管理 node-exporter 的 Pod | 副本数、镜像拉取策略、是否挂载宿主机 /proc |
| claim.yaml | 存储声明,通常属于 Prometheus 侧 | 对应 StorageClass 是否存在且可用 |
| prometheus.tar.gz | Prometheus 镜像压缩包 | 与 node-exporter 映像是配套版本,需要同步导入 |
| 告警规则文件 | Prometheus 规则定义 | 会被挂载到规则目录,需要检查声明路径 |
这里最容易出问题的在 service.yaml:它的 selector 必须和 deployment 里 Pod 的 label 完全一致,差一个键值,Endpoints 就不会填充。很多翻车案例都是这个原因,排查时先 kubectl describe svc,看 Endpoints 下面有没有地址就知道问题在哪。
configmap.yaml 在 node-exporter 场景下算可选件,因为采集器默认行为就能覆盖大多数需求。需要关掉某些 collector(比如 --no-collector.arp、--no-collector.softnet)或者调整 filesystem 采集器时,通过 ConfigMap 把启动参数注入才是干净的做法。这也解释了为什么资源包目录里要放两份 configmap.yaml——一份属于 node-exporter,一份属于 Prometheus,职责完全不同,别混用。
deployment.yaml 里有个细节会决定这套监控能不能覆盖整个集群:如果资源里给的是 Deployment,它只保证集群里有 N 个副本,不保证每个节点都有一个;而宿主机监控偏偏要求每个节点都有采集器。很多刚上手的人沿用 Deployment 部署,结果新加节点后指标一直缺,这个问题在第 5 章展开说。镜像拉取策略也值得检查,离线环境一定要是 IfNotPresent,否则 Kubelet 一旦触发拉取远端仓库,Pod 就卡在 ImagePullBackOff。
2.3 镜像打包进资源包的意义:内网搬运的标准动作
这套监控链路用到的镜像原始来源基本都在国外仓库或云厂商镜像库。能联网的机器上 docker pull 很方便,但生产集群大多处在隔离网络里,pull 操作直接超时。把镜像打成 tar.gz 随资源包分发,是这类交付场景里的标准动作。资源包里的 prometheus-node-exporter.tar.gz 和 prometheus.tar.gz,就是 node-exporter 与 Prometheus 两个镜像的离线载体;images 目录一般放的就是这些 tar 文件,方便一次性传到目标节点。
拿到镜像文件后有两个动作不能省。一是传文件,通常通过跳板机中转,scp 或 U 盘都行,注意目标节点存放到一定路径后要给 Kubelet 用户可读权限,否则导入阶段就会遇到 permission denied。二是确认架构,README 里一般会写明 amd64 还是 arm64,如果集群是 arm 节点,x86 镜像导进去后 Pod 一样起不来,这种问题光看镜像名根本发现不了。
另外要养成一个习惯:别用文件名猜镜像名。文件名是给人看的,镜像内部的 Repository 和 Tag 才是 Kubelet 真正去找的标识。导入后第一件事是把 docker images 或者 ctr images list 的输出和 deployment.yaml 里的 image 字段对一遍,不一致就补 tag。这一步做扎实了,后面整条链路都顺。
3. 从镜像导入到指标被抓取:内网部署的完整命令链与参数说明
3.1 第一步:把 node-exporter 镜像加载进节点
先确认目标节点的容器运行时类型,命令在 Docker 和 containerd 之间有明显差别。先看 /etc/containerd 目录是否存在,或者直接执行 kubectl describe node <节点名>,看容器运行时字段。确定之后选择对应的导入命令:
# Docker 运行时,适用于较老的自建集群 docker load -i prometheus-node-exporter.tar.gz docker images | grep node-exporterdocker load 会把 tar 里保存的镜像连同 tag 一起还原,之后 docker images 的输出里能看到镜像名和版本标签,这个标签要和 deployment.yaml 里的 image 字段一致。如果 tar 内部只有一层而没有 tag,docker load 也能成功,但镜像会显示为 ,Kubelet 按名字拉取时一样找不到。
# containerd 运行时,云厂商托管集群和较新的自建集群常用 ctr -n k8s.io images import prometheus-node-exporter.tar.gz ctr -n k8s.io images list | grep node-exporterctr 命令的 -n k8s.io 参数必须带,它指定镜像写入的命名空间。Kubelet 只从 k8s.io 这个命名空间读镜像,不指定的话镜像会进入默认命名空间,等于白导。导入后如果列表里的镜像只显示 digest 而没有 Repository:Tag,补 tag 的命令是把 digest 重新指向一个完整名字,然后再用 crictl images 验证一次。这个「导入 → 补 tag → crictl 验证」三步走,是我在离线节点上反复使用的顺序,缺一步后面都会绕远路。
注意:crictl 是直接对标 Kubelet 的镜像管理工具,能看到 Kubelet 实际可用的镜像列表。Docker 节点的 crictl 需要配置 endpoint 指向 docker.sock,containerd 节点则直接用默认配置。
3.2 第二步:部署 node-exporter 并确认 Pod 状态
镜像就绪后,在资源包目录下按顺序执行三个 apply:
# 顺序:configmap 先于 deployment,避免依赖缺失 kubectl apply -f configmap.yaml kubectl apply -f service.yaml kubectl apply -f deployment.yaml先 apply configmap,再 apply 依赖它的 deployment,这个顺序能避免 deployment 创建时因为 ConfigMap 还不存在而短暂报错。service.yaml 放在 deployment 前面属于个人习惯,反正 service 的 selector 是在运行时绑定后端 Pod,时序上影响不大。apply 完了立刻看 Pod 状态:
# 确认每个节点都有 Pod,且都在 Running kubectl get pods -o wide | grep node-exporter kubectl logs -f <pod-name>Pod 显示 Running 不代表就能出数据,日志里如果刷 permission denied,通常是容器的 securityContext 没放开。node-exporter 要读宿主机 /proc 里的完整内核统计,很多部署清单会给 privileged: true 配合 hostPID: true。如果安全策略不接受特权模式,可以改成把宿主机根目录只读挂载进容器,让采集器通过 --path.rootfs=/hostfs 读取宿主数据,这是推荐的做法,既能拿到完整指标,又不需要特权。
3.3 第三步:验证 /metrics 端点在集群内可访问
Pod 稳定后,先验证 Service 有没有绑定到 Pod 的 IP:
kubectl get endpoints | grep node-exporter kubectl describe svc node-exporterEndpoints 里有 IP 就说明 selector 匹配成功。如果空着,说明 service.yaml 的 selector 和 deployment 里的 label 不一致,去把两个文件的 label 部分对齐,重新 apply 后再看。有了绑定关系,用端口转发在本地直接看指标:
# MemTotal 与 cpu_seconds 是判断采集器是否工作的基础指标 kubectl port-forward svc/node-exporter 9100:9100 curl http://127.0.0.1:9100/metrics | grep -E "^node_memory_MemTotal|^node_cpu_seconds_total"grep 匹配的两条是内存总量和 CPU 时间累计值,只要有一条能返回数字,采集器本体就是正常的。返回大段 # HELP 和 # TYPE 开头的内容不是报错,那是 Prometheus 指标格式的注释说明。要警惕的是 connection refused,那多半是 Service 端口写错或 Pod 没监听 9100;请求超时则可能出现在节点负载极高的时候,先看节点状态再动配置。
4. Prometheus 抓取与告警规则:让资源包里的 alert 从纸面变报警
4.1 Prometheus 侧配置:让抓取任务指向 node-exporter
资源包里的 prometheus.tar.gz 是 Prometheus 镜像,对应的 configmap.yaml 管理的是挂载进容器的 prometheus.yml 和规则文件。最核心的抓取配置长这样:
scrape_configs: - job_name: "node-exporter" static_configs: - targets: ["node-exporter.default.svc.cluster.local:9100"]job_name 会成为这笔抓取的所有指标的公共标签,后面写告警规则、做 Grafana 图表全部依赖这个名字,建议定成固定值,全局统一。targets 用 Service 的完整 DNS 名字,Prometheus 会走集群 DNS 解析到后端 Pod;如果 Prometheus 和 node-exporter 在同一个 namespace,服务短名就够,跨 namespace 就必须写全。
内网环境里 static_configs 是最少依赖、最好排查的写法。也可以用 kubernetes_sd_configs 让 Prometheus 自动发现 Pod,配置会短很多,但依赖 RBAC 权限和服务发现机制,排错链路变长。我一般建议第一版先用手写 target 跑通,再考虑自动发现升级。
配置改完要触发重载,否则 Prometheus 还是拿旧配置跑:
# 若返回 404,说明 lifecycle API 未启用,改用重启进程 curl -X POST http://<prometheus-address>:9090/-/reload不是所有版本的 Prometheus 都开放了 lifecycle API,如果返回 404,就退回到重启进程的方式。生效与否不用猜,直接打开 Prometheus 的 Targets 页面,node-exporter 这个 job 的 UP 状态应为 1,Endpoint 列显示的是 Service DNS 对应的实际 Pod IP。
4.2 告警规则文件:CPU、内存、磁盘三类常用告警怎么写
资源包里的告警规则文件是挂载到规则目录的 YAML。下面是三类最常见的主机告警写法,规则语法为 Prometheus 标准格式:
groups: - name: node_exporter_alerts rules: - alert: NodeCPUUsageHigh expr: 100 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100 > 85 for: 10m labels: severity: warning annotations: summary: "节点 {{ $labels.instance }} CPU 使用率超过 85%" - alert: NodeMemoryUsageHigh expr: 100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100) > 90 for: 5m labels: severity: warning annotations: summary: "节点 {{ $labels.instance }} 内存使用率超过 90%"CPU 那条规则最容易写错的地方在 rate 函数。rate(node_cpu_seconds_total{mode="idle"}[5m]) 表示过去 5 分钟空闲 CPU 时间的增长速率,用 100 减掉这个占比才是使用率。by(instance) 的分组很重要,不加的话多节点场景下整个表达式只算出一个值,告警会失去定位意义。for: 10m 表示条件持续 10 分钟才真正触发,避免瞬时尖峰造成告警轰炸。annotations 里的 {{ $labels.instance }} 是模板语法,字段名必须和指标上的标签完全一致,大小写写错了规则能加载但告警内容会是空的。
磁盘告警要单独说。直接用 node_filesystem_avail_bytes 会把系统保留分区、只读挂载点统统算进去,常见做法是加 mountpoint 过滤,只保留根分区和明确的数据分区。比如在 expr 里追加 {mountpoint="/"},或者用正则排除 /var/lib/docker 这类内置目录。这一步不做,磁盘告警基本是乱的,容量明明还够却整天误报。
4.3 规则文件怎么挂载与重载:从 ConfigMap 到 Prometheus 生效
Prometheus 容器镜像本身不带业务规则,规则文件必须从外部挂进来。资源包的做法是:把规则 YAML 写进 Prometheus 侧的 configmap.yaml,再在 Prometheus deployment 的 volumeMounts 里挂载到容器内规则目录,prometheus.yml 里用 rule_files 声明加载路径:
rule_files: - "/etc/prometheus/rules/*.yaml"rule_files 写的是容器内路径,不是宿主机路径。很多人挂载完忘记检查这一行,结果规则文件明明在 ConfigMap 里,Prometheus 的 Rules 页面却一片空白。另外 Prometheus 不会定时扫描规则目录,新增或修改规则之后必须手动执行 reload。而 ConfigMap 更新后 Pod 里的文件内容不会立刻同步,需要重新 apply configmap、再删掉 Prometheus Pod 让它重建,这套组合在离线环境里最省事。
规则文件从挂载到真正生效,验证路径是:kubectl exec 进 Prometheus 容器看规则文件是否存在 → 触发 reload → 打开 Rules 页面确认规则列表 → 人为调低触发阈值验证告警状态能从 inactive 变 pending 再到 firing。走到最后一步,这套监控链路才算真正闭环,也才算把资源包里的 alert 从纸面变成了报警。
5. 避坑手册:内网部署 node-exporter 到 Prometheus 告警的五个翻车点
5.1 Pod 一直 ImagePullBackOff
现象:apply deployment.yaml 之后,Pod 状态反复在 ImagePullBackOff 和 ErrImagePull 之间横跳,kubectl describe 里看到 failed to pull image ...: not found。
原因:绝大多数情况是镜像根本没进对地方。要么 tar 没导入到当前节点,要么导入到了 Docker 的命名空间而 Kubelet 用的是 containerd;要么 ctr 导入后镜像没有 tag,Kubelet 按 deployment.yaml 里的完整名字拉取,自然找不到。
解决:先跑 crictl images 看 Kubelet 视角下的镜像列表,确认名字和 tag 都存在;没有就重新执行 ctr -n k8s.io images import,然后补 tag,再跑一次 crictl images 验证。这套动作做完再去看 Pod,比反复 kubectl describe 翻日志高效得多。
5.2 指标少了一半:Deployment 部署导致部分节点没有数据
现象:Prometheus 里能查到 node_cpu_seconds_total,但只有集群里一部分节点有数据,新加入的节点长期没有任何指标。
原因:资源包给的是 deployment.yaml 而不是 daemonset.yaml,Deployment 只保证有 N 个副本,不保证每个节点都有一个。Pod 可能全部调度到同一个节点的现象虽然少见,但节点数量增加后副本数不够、新节点没被覆盖是必然的。
解决:把 kind 改成 DaemonSet,删掉 spec.replicas,保留 selector 与 template。DaemonSet 保证每个节点一个 Pod,新增节点也会自动拉起。改动后用 kubectl get pods -o wide 确认每个节点上都出现了 node-exporter Pod。这也是为什么生产环境里宿主机采集几乎不用 Deployment 的原因。
5.3 抓取不到目标:Service selector 与 Pod label 对不上
现象:Prometheus Targets 页面里该 job 一直 DOWN,但对应 Pod 明明是 Running,curl Pod IP 也能拉回指标。
原因:service.yaml 里的 selector 写的是 app: node-exporter,而 deployment 里的 Pod label 写成了 name: node-exporter,两个概念没对上,Service 后端没有可用 Pod。
解决:kubectl describe svc node-exporter 看 Endpoints 字段,如果显示 ,把 selector 改成和 Pod label 一致的键值,重新 apply 后再看 Endpoints 是否填充。这一步排查顺序比修改顺序更重要,先确认端点有没有绑定,再决定改哪边。
5.4 告警规则文件挂载了却始终不报警
现象:ConfigMap 里明明有规则 YAML,Prometheus 的 Rules 页面上要么看不到这条规则,要么状态一直是 inactive。
原因:两个地方容易出问题。一是 prometheus.yml 的 rule_files 路径和 volumeMounts 的挂载路径不一致,文件挂进去了但 Prometheus 没去读;二是修改规则后没有触发 reload,Prometheus 还在用内存里的旧规则。
解决:先 kubectl exec 进 Prometheus 容器,确认规则文件真实存在于挂载路径下,再比对 rule_files 的声明是否指向同一路径。确认后执行 curl -X POST :9090/-/reload,回到 Rules 页面刷新,规则出现在列表里才开始谈告警触发。另外注意 ConfigMap 更新后容器内文件不会自动同步更新,重新 apply 加重建 Pod 是离线环境里最可靠的重载方式。
5.5 9100 端口通了,但磁盘和网络指标全是零
现象:curl http://127.0.0.1:9100/metrics 能拉回内容,node_cpu 有数据,但 node_filesystem_avail_bytes 和 node_network_receive_bytes 全为 0。
原因:node-exporter 容器里没挂载宿主机根目录,它读到的是容器自己的文件系统和网络设备,而不是宿主机的;还有一种可能是启动参数里用 --no-collector.filesystem 或 --no-collector.network 关掉了相关采集器。
解决:在 deployment 里把宿主机根目录只读挂载进容器,比如挂到 /hostfs,启动参数加 --path.rootfs=/hostfs;同时检查 configmap.yaml 里有没有不想要的 no-collector 开关。改完重启 Pod,再拉一次 metrics,看这两类指标是否有真实数值。
6. 验证与进阶:把部署结果从「能跑」变成「数据可靠」
6.1 用三条 PromQL 快速验收核心指标
部署完成不代表可用,花两分钟做一次数据验收比任何「部署成功」的截图都可靠。三条 PromQL 分别覆盖负载、内存和 CPU:
| 指标 | PromQL | 说明 |
|---|---|---|
| 负载 | node_load1 | 过去 1 分钟平均负载,明显大于 CPU 核数时说明过载 |
| 内存 | node_memory_MemAvailable_bytes | 实际可用内存,注意是 MemAvailable 不是 MemFree |
| CPU | 100 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100 | 过去 5 分钟的 CPU 使用率百分比 |
三条同时有持续数据返回,说明采集、抓取、存储链路是通的。只验证了 up 状态为 1 还不够,因为 up 只代表端口可达,不代表数据内容是对的——磁盘和网络指标为零的坑就是因为缺了这层验证才容易被放过。
6.2 进阶:接入 Grafana 与告警通知
集群里已有 Grafana 的话,导入社区里成熟的 node-exporter 看板模板,数据源选 Prometheus,把模板里的 job 名改成你配置文件里的 job_name 就能出图。告警要真正发出去,还需要 Alertmanager 参与:Prometheus 只负责计算规则并产生 alert,通知动作归 Alertmanager 管,邮件、Webhook 都在那边配。
我从第一次被「磁盘快满但群里没人收到消息」坑过之后,每次部署这套监控都强制自己走一遍同样的验收顺序:crictl images 有镜像 → endpoints 有地址 → Targets UP → Rules 已加载 → 一条 PromQL 曲线有数据。这套顺序走完才敢说监控上线了。资源包本身是完整可用的,真正决定可用性的是部署者是否愿意把这几步验证做扎实。希望帮到你。
本文还有配套的精品资源,点击获取