☰
K8s监控实践:Prometheus Operator与Grafana告警配置详解
2026/10/16 1:59:07 网站建设 项目流程

先说个背景,我自己维护的几套集群都是从裸机部署走过来的。刚开始监控全靠人肉看节点负载,真正出事的时候才发现两眼一抹黑。后来花了两天时间把 Prometheus Operator + Grafana 这套组合落地,彻底把K8s监控捋顺了。这里把整个部署、配置、排障的过程写成一份较详细的文档,希望能帮你少走弯路。

这套方案解决的核心问题就三个:指标怎么采、数据怎么存、告警怎么发。K8s环境里Pod随时重建、节点动态伸缩,传统那种静态配置监控目标的方式根本玩不转。Prometheus自带的服务发现机制能自动感知集群变化,Grafana负责把时序数据变成可视化的面板,Alertmanager把异常转化成有效通知。适合正在运维K8s集群的工程师,或者刚接触云原生监控但被各种概念绕晕的朋友参考。

1. 监控方案的整体架构与选型思路

1.1 为什么是Prometheus+Grafana这套组合

先说说我对几类监控方案的真实感受。传统主机监控工具像Zabbix,对付固定IP的虚机没问题,但放到K8s的Pod漂移场景里就非常别扭,每次Pod重建IP变化后都要手动改监控项,这体验谁能顶得住。而Prometheus基于拉取模型的机制,天然适应K8s这种动态环境,配合Kubernetes的API服务发现,新Pod一上线就会被自动纳管。

另外Prometheus的标签体系特别适合K8s多维度聚合分析。比如我要统计某个Deployment所有副本的CPU使用率,一条sum(rate(container_cpu_usage_seconds_total[5m])) by (pod)就能把多个Pod的数据聚合起来。这种灵活度在传统监控工具里要配一堆规则才做得出来。

Grafana在整个链路里扮演的角色的可视化拼图。它本身不存储数据,通过数据源接口对接Prometheus,把TSDB里冷冰冰的指标变成直观的曲线面板。选择这套组合还有个现实考量:社区生态非常成熟。K8s官方的监控指标、各种中间件exporter,都能找到现成的图表模板和告警规则,不需要从零开始造轮子。

1.2 部署方式选型:Operator化 vs 手工二进制

部署这套监控栈有两条路线,我劝你直接选Operator化。确实,最初我也动过手工用二进制部署的念头——毕竟Prometheus、Alertmanager、Grafana各自装一个,再配Prometheus配置文件里的static_configs,看起来好像没什么门槛。

但仔细一想就有坑了。K8s里的大多数资源对象(Deployment、Pod、Service)都是动态的,如果配置里写死了targets,那Pod一换IP或者滚动更新一次,监控目标就断了。手工部署还得考虑拓扑:Prometheus怎么配置才能跨namespace去发现其他服务的metrics端口?这些配置散落在各种yaml里,越堆越多,后期根本维护不动。

用Prometheus Operator就能把这些绕来绕去的问题全部变成声明式配置。你只需要定义ServiceMonitor和PodMonitor资源对象,Operator会动态生成Prometheus的采集配置。Prometheus启用--enable-feature=prometheus-operator这种玩法,本质把它变成API controller,监控目标的变化全部由K8s API实时同步。部署对象(prometheus、alertmanager、thanos等)也通过CRD管理,追层需要加一个采集目标,改一行CR描述就能滚动生效。

提示:如果没有特殊理由(比如公司基建里必须二进制独立部署),直接无脑走 Operator 化,能省掉大量维护成本。

1.3 采集链路里各组件怎么配合

把整套链路的职责边界先梳理清楚,后续配置会顺手很多:

  • Prometheus Server:核心调度器,负责拉取指标、存时序数据、按规则算告警。默认15秒拉一次抓取周期,短了会增大K8s API压力,长了告警不准。
  • exporter:指标生产者。node-exporter暴露节点层面指标(CPU、内存、磁盘、网络),kube-state-metrics专门把K8s对象的状态(Pod数量、Deployment副本数、PVC容量)转换成指标,cadvisor被内置在kubelet里,负责暴露容器运行指标(container_cpu、container_memory等)。应用定制的业务指标则要自己在代码里暴露/metrics端点。
  • ServiceMonitor/PodMonitor:采集发现器。ServiceMonitor 监听指定 namespace 下带某个 label 的 Service,发现后端 Endpoints 后就把它们加入采集列表。PodMonitor 更底层,直接监听 Pod 的 labels 和 annotations,适合跨 namespace 去采集没有 Service 关联的实例。
  • Grafana:数据展示与控制台。通过 Prometheus 数据源拉取指标,用面板的方式渲染出来。也负责接收 Alertmanager 产生的告警记录,辅助查看告警状态和历史。
  • Alertmanager:告警调度中心。接收 Prometheus 推送的告警表达式结果,做去重、分组、静默、路由,最后走 Webhook、邮件、Web渠道等渠道把消息送出去。

组件之间全部靠 HTTP 拉取模型互联,结构简单清晰。每个环节的配置都是 K8s 的 CR 对象,用kubectl apply就能管理,服务重启、扩缩容全都交给K8s自己调度。

2. 核心概念与资源对象详解

2.1 指标是怎么被采集上来的

理解了Prometheus拉取模型,后面的配置就不会迷糊。核心动作就两个:先通过服务发现拿到目标实例的地址列表,然后定期从这个地址的HTTP端口(默认/metrics)抓取数据。K8s场景下,服务发现靠的是访问Kubernetes API。

举个例子来说,当你在CR里指定一个ServiceMonitor,它会告诉Prometheus:去监控某一个namespace下,带有特定label的Service。Prometheus拿到这个Service关联的所有Endpoint后,会自动解析出Pod的IP和端口,这个就是采集目标。重点要区分三个层面的服务发现:

  • role: node:发现所有K8s节点,用于采集kubelet、cadvisor、node-exporter的指标。
  • role: endpoints:发现Service关联的Endpoint列表,这是ServiceMonitor背后的发现机制。
  • role: pod:直接发现暴露了/metrics端口的Pod,对应PodMonitor的使用场景。

网络路径上有个坑要了解:从Prometheus所在的Pod发出请求,到达目标Pod是直接走集群分配的虚拟 IP 或 Cluster IP,因此要保证目标Pod允许被ALL来自其他namespace的流量,也就是 network policy 不能误伤监控流量。

指标格式非常规整,每一行就是一条时序数据,基本长这样:

node_cpu_seconds_total{cpu="0",mode="idle",node="node-01"} 1254736.2

花括号里的key="value"都是标签key-value对,它们是做聚合查询的分组依据,也是告警通知里要带的定位信息。

2.2 ServiceMonitor 和 PodMonitor 到底怎么选

这俩对象是监控配置的灵魂,但也是新手最容易搞混的地方。直接给结论:

选了 ServiceMonitor,等于让Prometheus通过Service资源去找后端Pod。前提是你要监控的应用已经定义了Service(哪怕只用ClusterIP),并且Service的spec.selector能正确匹配到后端Pod的labels。它的好处是即使Pod重建、IP变了,只要Service在,Prometheus就会自动发现新端点。

定义 ServiceMonitor 的核心字段很简单:

apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: my-app-monitor namespace: my-namespace spec: selector: matchLabels: app: my-app endpoints: - port: metrics interval: 30s

这段CR表达的意思是:去my-namespace空间里找带有app=my-app标签的 Service,找到之后,请求它那个叫metrics的端口,每30秒拉一次指标。注意port字段写的是 Service 里定义的端口名称,不是端口号。

那 PodMonitor 用在什么场景?比如某个中间件运行在独立的Pod里,没有定义Service(像一些临时Job任务),或者你要跨namespace去把零散的Pod都纳进来,这时候就直接用PodMonitor盯着Pod的labels看。它比ServiceMonitor更底层,因为它直接获取Pod IP列表。

apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: name: sidecar-monitor namespace: infra spec: selector: matchLabels: app.kubernetes.io/name: sidecar podMetricsEndpoints: - port: metrics path: /metrics scheme: http

判断标准很简单:应用有没有稳定的 Service?稳定就用ServiceMonitor,没有或者跨namespace分散部署就用PodMonitor。

2.3 kube-state-metrics 和 node-exporter 的作用差异

这两类 exporter 的定位完全不同,别混为一谈。node-exporter 采集的是节点资源状态。它暴露的是node_cpu_seconds_total、node_memory_MemAvailable_bytes、node_filesystem_avail_bytes这种带node_前缀的指标,告诉你这台机器还剩多少CPU、多少内存,硬盘快不快满。它必须作为 DaemonSet 部署,保证每个节点都跑一个,不然会出现监控盲区。

kube-state-metrics 采集的是 K8s 资源对象状态。它不关心你Pod里面跑什么,它只看K8s API里面记录的状态:kube_deployment_status_replicas_available表示可用副本数,kube_pod_container_status_running是某个容器是否还在Running,kube_pvc_info记录PV和PVC的绑定关系。这个组件也必须起,没有它,你就看不到“应用明明显示3个副本,实际可用只有1个”这类关键告警。

从部署形态上区分,node-exporter走DaemonSet,kube-state-metrics走普通Deployment(单副本或三副本,看集群规模)。采集到的指标前缀也因此不同,排障的时候看到指标名就能判断数据出处。

3. Prometheus Operator 化部署实操

3.1 环境前置检查与资源规划

开始部署之前,先用下面几条命令确认集群环境是否满足条件:

kubectl version --short kubectl get nodes kubectl get sc

Operator 依赖 K8s 的 CRD 机制,所以集群版本不能太低,1.16 以上是最低要求,我用的是1.24集群没任何问题。在比较老的1.14集群上装,部分CRD的apiextensions版本不兼容,会报unable to recognize "*.yaml": no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1",这就是目录版本与集群不匹配导致的。

资源规划方面,一个小型生产集群(10-20个节点)建议给监控栈预留这样的配额:

  • Prometheus StatefulSet:4核8G内存,加上每节点约500MB的磁盘存储采集数据(根据设置保留时长调整)
  • Grafana Deployment:1核1G起步,2核2G比较舒服
  • Alertmanager StatefulSet:1核512MB就够
  • kube-state-metrics Deployment:500MB内存级别即可

如果集群节点数超过50,kube-state-metrics 的内存消耗会显著上升,要按1000万指标项预估内存,不然频繁OOM重启,监控数据断档比出事更可怕。

3.2 部署 Prometheus Operator 到集群

这里推荐直接用官方维护的 Helm Chart,能省掉大部分手工操作。先添加仓库:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update

然后创建一个监控专用的 namespace,防止监控组件和业务互相干扰:

kubectl create namespace monitoring

通过 values.yaml 自定义部署参数。下面是我常用的最小化配置模板,覆盖了Operator、Prometheus、Alertmanager、Grafana、常见的采集组件(node-exporter、kube-state-metrics):

# values.yaml prometheus: prometheusSpec: serviceMonitorSelector: matchLabels: release: my-prometheus # 用这个标签筛选ServiceMonitor podMonitorSelector: matchLabels: release: my-prometheus retention: 15d resources: requests: memory: 4Gi cpu: "2" limits: memory: 8Gi cpu: "4" storageSpec: volumeClaimTemplate: spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 100Gi storageClassName: standard grafana: adminPassword: admin persistence: enabled: true accessModes: ["ReadWriteOnce"] size: 10Gi alertmanager: enabled: true persistence: enabled: true size: 5Gi

这里有两个很关键的 selector 参数。serviceMonitorSelector和podMonitorSelector就像一个过滤器:Prometheus 只会去拉取带release: my-prometheus标签的ServiceMonitor/PodMonitor。这防止了团队之间的监控配置互相污染。如果你想把别的项目已经定义好的ServiceMonitor也纳入监控,那就得给那个对象补上这个标签。

执行安装命令:

helm install my-prometheus prometheus-community/kube-prometheus-stack -n monitoring -f values.yaml

等Pod起来后,用kubectl get pods -n monitoring确认状态。全部Running之后,基本的采集链路就通了。

注意:如果你用的是 Kind 或者 Minikube 这类本地集群,StorageClass 默认识别不出来的话,volumeClaimTemplate和 PVC 会一直Pending,直接把 storageClassName 改成你环境里可用的,比如standard或local-path。

这里再补充一下为什么建议先用serviceMonitorSelector过滤。在一个多人共用的集群里,如果开了全量发现(serviceMonitorSelector: {}),那么任何人的ServiceMonitor都会被所有Prometheus实例去采集一遍。一方面重复采集浪费资源,另一方面如果对方定义的采集路径有问题,你的Prometheus会不断报错刷日志。加一层标签隔离是团队协作的最佳实践。

3.3 配置核心采集目标(让监控先看到东西)

装上Operator之后,默认的采集目标已经包含这几类组件了:node-exporter(节点指标)、kube-state-metrics(资源对象状态)、kubelet里的cadvisor(容器指标)。你可以先在Grafana里临时看看有没有数据。但为了后面监控自己的应用,还需要定义新的采集目标。

以监控一个标准的业务应用为例,假如这个应用叫my-web-app,部署在prod这个namespace下,暴露了一个叫metrics的端口(8080),端口路径是/metrics。先确认你的业务Service长这样:

apiVersion: v1 kind: Service metadata: name: my-web-app namespace: prod labels: app: my-web-app spec: selector: app: my-web-app ports: - name: metrics port: 8080 targetPort: 8080

然后写一个对应的ServiceMonitor:

apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: my-web-app-monitor namespace: prod labels: release: my-prometheus # 关键:必须和Helm values里select的标签一致 spec: selector: matchLabels: app: my-web-app endpoints: - port: metrics path: /metrics interval: 30s relabelings: - action: labelmap regex: __meta_kubernetes_service_label_(.*) - sourceLabels: [__meta_kubernetes_namespace] targetLabel: namespace

relabelings里做了两件事:把Service的原始labels带进指标标签里,方便后面聚合查询;再把namespace提取成一个独立标签。不然你查数据的时候只知道有个Pod IP,根本不知道它属于哪个空间,很难定位问题。这个步骤我建议所有ServiceMonitor都加上。

应用一下这个CR:

kubectl apply -f service-monitor.yaml

去Prometheus的Targets页面(Grafana里可以嵌入访问)确认状态变成UP,就说明采集链路已经通了。

3.4 Grafana 数据源接入与仪表盘导入

Grafana 装好后,先做数据源配置。Charts 自带的 Prometheus 数据源默认就会关联同一个namespace下的Prometheus服务,直接就能用。如果有一套独立部署的Prometheus,要手动填数据源地址,URL填http://prometheus-operated.monitoring.svc:9090。

仪表盘导入时,两张社区面板是 P8s 世界的标配,按需选择:

  • Node Exporter Full(ID: 1860):节点CPU、内存、磁盘、网络、IO全维度覆盖,节点层排查首选。
  • Kubernetes Cluster(ID: 641 或 12136):集群概括视图,包括Pod状态分布、容器CPU内存、Deployment副本健康度。
  • Kubernetes Pod(ID: 641 的detail版,或 库里的 Pod 分析)如果找不到合适的,也可以用kube-state-metrics的官方仪表盘。

导入的时候注意一个细节:数据源要选对。如果多个Prometheus实例(比如分区域部署),每个数据源导一次。绝大多数字段模板基于 PromQL 写的,数据源选错面板就会全空。导入后马上看右上角的“Last 1 hour”数据是否有波动,没有就说明数据链路还有问题,别着急配告警。

4. 监控项拆解与告警规划

4.1 四个监控层面到底看什么

K8s监控不能眉毛胡子一把抓,我习惯分成四层来盯:

基础设施层(节点硬件):每台节点的CPU使用率、内存使用率、根分区磁盘剩余百分比、节点网络带宽。这层数据来源是node-exporter。节点挂了Pod再多也没用,一切调度都基于节点可用性。

K8s资源对象层(控制面与状态):APIServer的请求延迟、Etcd的leader切换次数、Deployment的期望副本数vs可用副本数、Pod的启动状态、PVC使用率。这就是kube-state-metrics能给的指标。看到可用副本数低于期望副本数,就要担心是否节点不够或者POD被驱逐了。

容器运行时层:每个容器的CPU limit使用率、内存limit使用率、网络收发速率、磁盘IO读写延迟。数据由cadvisor产生,前缀是container_。这是应用性能分析和Pod缩扩容判断的第一手依据。

应用业务层:应用自身的QPS、请求错误率、P99延迟、队列积压数。这类指标需要业务代码主动暴露,或者借助中间件exporter(比如Redis exporter、MySQL exporter)。这层最终反映用户体验。

分层的目的在于:发生故障时快速缩小排查范围。比如页面访问卡顿,先看容器层CPU是否打满(是不是被限流),再看节点层网络带宽是否被打满(是不是被其他Pod挤占),再看应用层P99是否攀升(是不是代码慢查询)。

4.2 关键监控指标清单(含 PromQL 示例)

监控维度核心指标 PromQL含义与预警条件
节点CPU100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)节点整体CPU使用率,超过85%持续10分钟就需要注意
节点内存(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100节点内存使用率,超过90%可能触发OOM
节点磁盘(node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100根分区剩余空间百分比,少于15%准备清理或扩容
Pod重启次数changes(kube_pod_container_status_restarts_total[1h])1小时重启次数大于3说明频繁崩溃
Pod 内存使用率max(container_memory_working_set_bytes{container!=""}) / sum(container_spec_memory_limit_bytes{container!=""}) * 100容器内存Limit使用率,超过100%会被OOM Kill
Deployment可用副本kube_deployment_status_replicas_available / kube_deployment_spec_replicas比值小于1说明有副本缺失
APIServer错误率sum(rate(apiserver_request_total{code=~"5.."}[5m])) / sum(rate(apiserver_request_total[5m]))超过5%基本可以判定控制面异常
容器网络丢包rate(container_network_receive_errors_total[5m]) / rate(container_network_receive_packets_total[5m])网络链路质量问题,重点排查网卡和CNI

这些 PromQL 表达式不加 labels 分组,看起来已经能直接用于告警基本判断。实际配置建议按 namespace 或 deployment 分组写。

4.3 告警规则怎么写更有效

Prometheus 的告警规则本质是一组持续N分钟为真的表达式。这条规则要表达的意思很重要,表达式立即为真不算数,必须持续一段时间(比如5分钟)才触发,避免抖动导致误报。

写告警规则最容易踩的坑是阈值拍脑袋。不要把阈值定得太敏感。节点内存超过80%就告警?生产环境经常性超过80%,一天到晚告警信息刷屏,最后大家连看都懒得看。我的经验是分三级来定:

  • Warn级别:资源持续超过85%且持续时间15分钟以上,先发消息出来观察。
  • Critical级别:资源持续超过95%或可用副本数低于期望值,立马走电话/钉钉。
  • Info级别:某个指标在特定namespace出现异常高峰,只记录不打扰。

另外强烈建议把告警规则里加上可执行信息。比如:

- alert: NodeMemoryUsageHigh expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90 for: 10m labels: severity: critical annotations: summary: "节点 {{ $labels.instance }} 内存使用率过高" description: "当前内存使用率已超过90%,持续10分钟,请登录节点排查是否有异常进程或进行扩容。"

这样收到告警的人不用再去猜,主动作都写在描述里了。for: 10m的配置要写得宽一点,避免一分钟的毛刺引发告警疲劳。

5. Alertmanager 告警接入与通知配置

5.1 Alertmanager 的分组与路由机制

Alertmanager 收到 Prometheus 推送的告警后,不是直接发出去,而是做了一堆处理。理解它的思路,配置才能合理。主要流程是:分组 -> 抑制 -> 静默 -> 路由发送。

分组的意思是把同类型告警合并成一个通知。比如某个节点挂了,可能同时触发10条Pod相关的告警,要是不分组,一晚上能收到几百条消息。分组配置通常按namespace或alertname划分:

route: group_by: ['namespace', 'alertname'] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: 'default'

group_wait是第一条告警产生后等待30秒,把同期到达的告警凑一波再发,避免一秒一条的轰炸。repeat_interval是同一个告警再次通知的间隔,4小时的默认值比较合理。太短真的会扰民。

路由的作用是把不同告警送给不同渠道。比如严重级别走电话,一般级别走钉钉或邮件:

routes: - match: severity: critical receiver: phone - match: severity: warning receiver: webhook

抑制功能也很实用:一个节点宕机后,这个节点上所有Pod的告警其实都是次级告警。配置一条规则让主机级告警抑制容器级告警,就能避免告警风暴。在生产环境里,告警风暴引发的二次故障比第一次故障还危险——大家会为了处理冗余告警而错过关键异常。

5.2 接入企业微信/钉钉/邮件等渠道

Alertmanager 走 Webhook 是通用做法。比如钉钉群机器人,只要把 Webhook URL 配到 Alertmanager 的配置里,再把告警消息转成钉钉要求的 JSON 格式就行。

先启用webhook_configs,再把自定义模板挂进来处理消息:

receivers: - name: 'dingtalk' webhook_configs: - url: 'http://dingtalk-webhook.monitoring.svc:8060/dingtalk/webhook/send' send_resolved: true

send_resolved: true很关键,意思告警恢复后也推一条“已恢复”消息,否则排障结束后大家还会不断问“现在好了没”。

如果是邮件接收者,直接指定email_configs并配置 SMTP 地址。但邮件通知延迟高,不适合紧急告警。我的实践经验是:紧急告警走钉钉/企微群,非紧急的走邮件存档,历史告警统一在 Grafana 的 Alerting 页面里查看。

5.3 防止告警疲劳的几个关键设置

告警疲劳是监控系统最大的隐形杀手。疲劳一产生,真正的故障就会被淹没在消息海里。我的几个办法:

  • 精确匹配:告警规则中的表达式尽量带namespace、deployment级别的分组,避免一个表达式把几十个Pod的告警全混在一起。
  • 配置抑制规则:对已知问题(比如某个节点在重启)生成静默,时间段固定。
  • 合理设置repeat_interval:同一问题持续存在,每4小时提醒一次即可,不要每小时缠一次。
  • 定期复盘告警命中率:每两周看一次所有历史告警,把90%以上都是误报或“看看无妨”的规则直接下掉或升级阈值。

我自己维护的集群,告警规则从最初的120条精简到50条左右,有效的告警反而更醒目。数量多不代表覆盖全,准确率才是第一位的。

6. 常见问题与排障实录

6.1 部署与采集的经典问题速查表

现象大概率原因排查与解决
Target 状态显示401 Unauthorizedkubelet 开启了对 /metrics 的 HTTPS 认证需要配置 bearer token 或调整 kubelet 的authentication.mode设置
Target 状态显示connection refused业务的 metrics 端口实际没有监听先kubectl exec进容器里curl localhost:8080/metrics验证
有些 Pod 数据一直为零ServiceMonitor 的 selector 没匹配到 Service,或端口名写错检查selector.matchLabels和 Service 的 labels 是否一致,端口名必须完全一致
Prometheus 内存持续暴涨采集的目标数量过多,或 label 基数太大增加--storage.tsdb.retention.size限制,或者用metric_relabel_configs丢弃高基数 label
Grafana 面板显示 No Data数据源不对,或查询的时间区间没数据切到 Prometheus 的 Graph 页执行相同 PromQL 验证查询;检查 target 拉取周期
kube-state-metrics 崩溃重启大集群里的内存不够用提高 limits 到2Gi以上,并开启分片(--pod-max-connections=30)
PVC 一直 PendingStorageClass 不存在或配额不足检查kubectl get sc,确认 PV provisioner 可用

6.2 我踩过的几个坑(附排查过程)

第一次把这套监控栈跑起来时,Grafana 上节点 CPU 面板一片空白。查了 kubelet 日志发现/metrics/resource接口返回401。这是因为新版 Kubernetes 默认启用了 kubelet 的 HTTPS 认证,而 Prometheus 没带凭证访问。解决办法是在 ServiceMonitor 里给 kubelet 相关的采集配置注入 bearer token:

- jobName: kubelet bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token scheme: https tlsConfig: insecureSkipVerify: true kubernetes_sd_configs: - role: node

部署完第一次导入仪表盘时,发现磁盘面板上每个磁盘都显示独立曲线,点了半天才搞清楚是node_filesystem_avail_bytes这个指标带了mountpoint标签,PromQL 里没加mountpoint="/"过滤导致的。这也是新手常踩的坑,面板不显示数据先看标签筛选是否正确。

还有一次 Pod 内存告警老是误报。后来才发现container_memory_working_set_bytes其实包含了缓存在内,直接拿它和 limit 计算容易误判。正确做法是用container_memory_working_set_bytes对比 limit,还是用container_memory_usage_bytes(包含 cache),两种算法结果差距不小。为了避免应用 OOM 才要告警,应该选working_set而不是usage。

6.3 长期维护中的性能优化经验

这套监控体系跑几个月后,问题主要集中在 Prometheus 内存占用。最有效的优化方式不是加内存,而是从源头削减指标基数。举几个具体列子:

  • 有些应用暴露的指标带上了 Pod 的 UID 或者请求路径,每个请求路径都会变成一条新时序线。基数膨胀在业务量大的时候非常吓人,必须在采集端做metric_relabel_configs,把高基数的 label 删掉。
metricRelabelings: - action: labeldrop regex: uid - action: labeldrop regex: request_path
  • 对不关注的指标直接丢丢弃。比如 K8s 系统自身的指标,很多带container_前缀的细分指标我们用不到,只保留关键几条,能砍掉一半的序列数。
metricRelabelings: - action: keep regex: 'container_(cpu|memory|network|fs).*' sourceLabels: [__name__]

另一个长期优化手段是使用 Thanos 做对象存储归档。Prometheus 本地默认存储15天,超过15天的历史数据直接查不到了。但合规审计或者月度报表可能需要查更久的数据,这时候把 Thanos Receiver 或 Thanos Sidecar 接上对象存储(比如 S3 兼容存储),历史数据全部落到对象存储里,Prometheus 本地只需保留一小段时间,内存占用能降一大截。

注意:Thanos Query 对接的时候一定要把--query.timeout设置大一点(建议5分钟以上),特别是查询存储侧历史数据的时候,太短的超时时间会让慢查询直接失败。

从我个人的实际经验来看,监控体系的搭建只是第一步,真正让这套系统发挥价值的是持续迭代告警规则和面板。每周花上半小时看看告警命中率和面板使用率,只留有效的规则,才能保证真正出事的时候,所有人都会第一时间去看这条告警。这套先跑起来再优化的思路,我觉得是最适合多数团队落地 Prometheus+Grafana 监控K8s的方式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询