简介面向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 / 内存 / 磁盘 / 网络每个节点一个 PodPrometheus 主动抓取cAdvisor容器运行指标Kubelet 内嵌Prometheus 抓取 kubelet 端口kube-state-metrics资源对象状态单副本 DeploymentPrometheus 主动抓取选 node-exporter 而不是自己写脚本收集主机信息是因为它已经覆盖了绝大多数主机维度指标而且输出格式天然能被 Prometheus 解析。自己写采集脚本最大的问题不是取数而是格式和标签不规范到后面写告警规则、画图表时全都要返工。这份资源把镜像和清单一起打包就是省掉这层自己造轮子的成本。2.2 清单文件逐个拆谁负责暴露端口谁负责启动参数资源包里 node-exporter 目录下的清单文件分工很明确逐个看就不会一把梭全 apply 之后不知道出了问题该找哪个文件。文件职责部署时要确认的点service.yaml集群内暴露 9100 端口类型是不是 ClusterIPselector 与 Pod label 是否对齐configmap.yaml注入 node-exporter 启动参数是否有不需要的采集器开关参数路径是否正确deployment.yaml管理 node-exporter 的 Pod副本数、镜像拉取策略、是否挂载宿主机 /procclaim.yaml存储声明通常属于 Prometheus 侧对应 StorageClass 是否存在且可用prometheus.tar.gzPrometheus 镜像压缩包与 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 内部只有一层而没有 tagdocker 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.sockcontainerd 节点则直接用默认配置。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-namePod 显示 Running 不代表就能出数据日志里如果刷 permission denied通常是容器的 securityContext 没放开。node-exporter 要读宿主机 /proc 里的完整内核统计很多部署清单会给 privileged: true 配合 hostPID: true。如果安全策略不接受特权模式可以改成把宿主机根目录只读挂载进容器让采集器通过 --path.rootfs/hostfs 读取宿主数据这是推荐的做法既能拿到完整指标又不需要特权。3.3 第三步验证 /metrics 端点在集群内可访问Pod 稳定后先验证 Service 有没有绑定到 Pod 的 IPkubectl 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_totalgrep 匹配的两条是内存总量和 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 状态应为 1Endpoint 列显示的是 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{modeidle}[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{modeidle}[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/*.yamlrule_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 导入后镜像没有 tagKubelet 按 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.yamlDeployment 只保证有 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 明明是 Runningcurl 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 里明明有规则 YAMLPrometheus 的 Rules 页面上要么看不到这条规则要么状态一直是 inactive。原因两个地方容易出问题。一是 prometheus.yml 的 rule_files 路径和 volumeMounts 的挂载路径不一致文件挂进去了但 Prometheus 没去读二是修改规则后没有触发 reloadPrometheus 还在用内存里的旧规则。解决先 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 不是 MemFreeCPU100 - avg(rate(node_cpu_seconds_total{modeidle}[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 曲线有数据。这套顺序走完才敢说监控上线了。资源包本身是完整可用的真正决定可用性的是部署者是否愿意把这几步验证做扎实。希望帮到你。本文还有配套的精品资源点击获取