先聊个结论:Prometheus + Grafana 这套组合,目前就是 Kubernetes 监控领域事实上的标准方案。我自己在测试集群和生产环境里反复搭过好几遍,第一次装的时候觉得组件多、概念多、YAML 写不完,真把链路跑通之后才意识到,整套体系的边界其实非常清晰:Prometheus 负责收数据、存数据、算数据,Grafana 负责把数据画成图,Alertmanager 负责把异常喊出来。这篇文档就把部署 Prometheus 全家桶、接入 Grafana、配置告警这三件事完整拆开讲一遍,顺带把我在实际环境中踩过的坑都标出来。适合刚准备给 K8s 集群加监控的同学,也适合已经部署成功但正在排查“为什么没有数据”“为什么内存一直涨”这类问题的人。
下面直接进入正题。
1. 为什么是 Prometheus+Grafana:监控方案选型背后的逻辑
1.1 K8s 集群监控到底在监控什么
一个 Kubernetes 集群,表面看是一堆 Node 和 Pod,真到排查故障的时候,你需要的不是“谁挂了”,而是“什么东西在什么时候开始不对”。所以监控对象至少要分四层:节点层、集群组件层、工作负载层、业务指标层。
可以用一张表把监控对象对应清楚:
| 监控层 | 核心指标示例 | 采集组件 |
|---|---|---|
| 节点层 | CPU、内存、磁盘、网络、inode 耗尽 | node-exporter |
| 集群组件层 | API Server 延迟与错误率、etcd 写入耗时、调度器状态 | 组件自带的 /metrics 接口,部分需要额外暴露 |
| 工作负载层 | Pod 状态、容器资源用量、重启次数、副本可用性 | kubelet 内置 cAdvisor + kube-state-metrics |
| 业务指标层 | 请求量、错误率、P95 延迟、队列长度 | 业务应用自行暴露 Prometheus 格式指标 |
四层缺一不可。很多新手只盯着容器 CPU 和内存,真出了故障才发现 API Server 的延迟指标早就开始报警了,或者节点磁盘 inode 耗尽半天找不到原因。我在下面几章里给的告警规则模板,也是围绕这几层设计的。
1.2 Prometheus 的核心设计理念与优势
Prometheus 最核心的设计是Pull 模型。它主动从一个 HTTP 端点拉取指标,目标不暴露/metrics,数据就进不来,目标挂了,它也自然拿不到数据。这个思路在容器环境里特别吃香,因为 Pod IP 随时会变,Prometheus 配合服务发现可以不断问 K8s API:“你现在有哪些 Node、哪些 Pod、哪些 Service?”拿到列表后再去逐批拉指标。相比之下,以前用 Zabbix 那一套 Agent 主动上报的方案,在动态环境里光是维护节点列表就要疯掉。
另一个设计优势是多维数据模型。Prometheus 里的每条指标都是一条带标签的时间序列,比如:
container_cpu_usage_seconds_total{namespace="default", pod="nginx-7c8b5d5b9c-xxxx", container="nginx"}这种设计让告警和查询变得非常灵活:你可以按 namespace 聚合,按 deployment 聚合,也可以精确取某一个 Pod 的数据。配合 PromQL 这个查询语言,出一张“某个应用所有实例的平均 CPU 使用率”的图,一句话就能查出来。
1.3 Grafana 在其中的角色定位
Grafana 在这套体系里干的是展示层的活,它不采集数据,也不负责判断异常,它做的是把 Prometheus 里的时序数据变成人类能一眼看懂的面板。它最大的价值有两个:一是可以把无数套业务和基础设施的指标汇总到一个入口,二是可以对同一份数据做多视角的观察,比如同样的内存数据,既可以看集群总趋势,也可以看某个 Pod 的波动。
另外提醒一点,Grafana 本身也带告警功能,可以直接在面板上设置 Alert rules。但在 Prometheus 体系里,建议优先用 Alertmanager 做告警,因为规则写在 PrometheusRule 里,版本可控、可 GitOps,切换集群成本低。Grafana 告警适合那种“临时盯一个指标”的场景,不一定是要长期跑的核心链路。
1.4 与其他监控方案对比后的取舍
Zabbix 是很成熟的传统监控,基础设施监控做得早,但容器时代它的服务发现能力跟不上,而且指标模型不是面向标签的,告警规则写起来繁琐。云厂商提供的托管监控(比如各种云原生监控服务)确实省心,但如果你的集群是自建或者混合云,数据出网、成本、多集群统一视图都是问题。SkyWalking 这类 APM 工具更关注调用链和性能分析,定位是“链路追踪 + 性能剖析”,不是通用的基础设施监控。
所以如果你的目标是自建一套 K8s 基础设施监控,Prometheus + Grafana 是生态最成熟、资料最全、二次开发成本最低的组合。尽量别再纠结“要不要上 XX ”,先顺手把 Prometheus 这套跑起来,后续再按需叠加其他组件。
2. 部署前必懂:Prometheus 监控 K8s 的核心概念
2.1 数据采集链路:从 Exporter 到仪表盘
整套链路一句话概括:Exporter 暴露指标,Prometheus 抓取指标,Grafana 查询展示指标。你可以把它类比成查水表:Exporter 是装在家里的水表,Prometheus 是抄表员,定期挨家挨户看数字,Grafana 就是给你出月度用水账单的系统。
在 K8s 里,Exporter 经常以一个 Sidecar 容器或者一个独立 Deployment 的形式跑在集群里。不过容器本身的指标不需要你自己部署 Exporter,因为 kubelet 内部已经内置了 cAdvisor,它会暴露每个容器的 CPU、内存、网络、磁盘 IO 指标。也就是说,Pod 建好后,Prometheus 只要能从 kubelet 的/metrics/cadvisor端点拉到数据,容器级别的指标就全有了。
2.2 服务发现机制:为什么静态配置不行
传统监控里你会在配置里写死一批 IP,比如1.2.3.4:9100,但对 K8s 来说 Pod IP 是临时资源,随时会被调度到别的节点,重启后 IP 就变了。所以 Prometheus 在 K8s 环境里必须用服务发现:
- 发现全部 Node,用来抓 node-exporter 的指标;
- 发现全部 Pod,用来抓容器和业务指标;
- 发现全部 Service/Endpoint,用来抓 Service 背后的实例;
- 发现 Ingress,可以按域名维度做监控。
这套机制由kubernetes_sd_configs实现,Prometheus 会持续请求 K8s API,拿最新对象列表,然后自动匹配 target。你在使用 kube-prometheus-stack 时,它已经把这套配置全部做好了,不需要自己去写繁琐的抓取配置。
2.3 三个必须理解的关键采集组件
真正负责采集 K8s 数据的组件主要有三个:
node-exporter:部署在每个节点上,读取
/proc和/sys,输出节点级别的 CPU、内存、磁盘、网络、文件系统等指标。节点挂了或者磁盘满了,靠它的指标判断。kube-state-metrics:监听 K8s API,把 Deployment、Pod、Service、Node 等对象的状态转换成指标,比如
kube_deployment_status_replicas_available、kube_pod_container_status_restarts_total。它不是一个“用的多不多”的指标来源,而是“集群对象是否符合预期状态”的指标来源。cAdvisor:内置在 kubelet 里,输出容器级别的资源使用指标,比如
container_cpu_usage_seconds_total、container_memory_working_set_bytes。
新手容易把 node-exporter 和 kube-state-metrics 搞混,记住一个判断标准:看的是机器资源,还是集群对象状态。前者用 node-exporter,后者用 kube-state-metrics。
2.4 PromQL 快速入门
PromQL 是整个监控查询的核心,不要求精通,但至少得看懂几个常用套路:
rate(metric[5m]):计算 Counter 指标在 5 分钟内的平均每秒增长率,比如 CPU 使用率通常是 Counter,要用 rate 转成速率。sum by (namespace, pod) (expr):按 namespace 和 pod 维度聚合。irate(metric[5m]):算瞬时速率,适合画 CPU 曲线,但抖动大。label_replace:偶尔用来转换标签值。
常用查询我直接给你几个可以直接抄进 Grafana 的例子:
# 每个 Pod 的 CPU 速率(单位:核心数) sum(rate(container_cpu_usage_seconds_total{container!="", pod!=""}[5m])) by (namespace, pod) # 每个 Pod 的内存使用量 sum(container_memory_working_set_bytes{container!="", pod!=""}) by (namespace, pod) # 每个 Pod 的容器重启次数 max(kube_pod_container_status_restarts_total) by (namespace, pod)这些查询在后面的 Dashboard 章节里还会反复出现,先把语法糖吃透,面板就能自己写。
3. 生产级部署实操:Helm 一键部署全套件
3.1 环境准备与版本确认
开始之前,先确认三件事:
- Kubernetes 集群版本建议 1.26 及以上;
- Helm 3 已安装,并且能访问你要用到的 chart 仓库;
- 有足够的资源给监控组件跑,至少给监控 namespace 预留 2 个节点、每个节点保障 4~8 Gi 内存的余量。
然后用下面的命令把 Helm chart 仓库加进来:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update如果是在离线环境,需要提前把这几个镜像拉下来推到私有仓库:Prometheus、Alertmanager、Grafana、node-exporter、kube-state-metrics、存活探针镜像等。相信我,这一步在离线环境非常耗时,但值得提前做好镜像清单。
3.2 用 kube-prometheus-stack 一把梭
市面上部署 K8s 监控有几种路子:用 Prometheus Operator + 一堆 CRD 自己编排、用官方 Prometheus Helm Chart 装裸 Prometheus、或者直接用kube-prometheus-stack。绝大多数情况下,我会直接推荐第三种。
kube-prometheus-stack 是一个集大成的 Helm chart,里面同时包含了 Prometheus Operator、Prometheus、Alertmanager、Grafana、node-exporter、kube-state-metrics,以及全套默认的告警规则和预置 Dashboard。装完以后基础监控能力直接就有了,根本不用一个个去拼。
部署命令很简单:
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --version 61.9.0安装后检查 Pod 状态:
kubectl get pods -n monitoring正常情况下你会看到一组以“kube-prometheus-stack-”开头的 Pod,比如 prometheus-operator、prometheus、grafana、alertmanager、node-exporter、kube-state-metrics。等它们全部 Running,再继续往下走。
3.3 关键 values 配置:存储、保留时长与资源限制
如果你直接用默认参数跑,过几天大概率会遇到两个问题:Prometheus 数据没持久化,Pod 一重启数据全没了;或者内存没限制,开始无限上涨。所以正式部署前建议准备一个自定义 values.yaml,重点改三块。
第一块:数据保留时长。
prometheus: prometheusSpec: retention: 15d这个参数决定 Prometheus 本地的 TSDB 数据保留多久。默认可能是 10 天,生产环境按需调到 15 天或 30 天。注意保留时间越长,磁盘占用越大。
第二块:持久化存储 PVC。
prometheus: prometheusSpec: storageSpec: volumeClaimTemplate: spec: storageClassName: managed-premium accessModes: ["ReadWriteOnce"] resources: requests: storage: 100Gi如果不配置这一块,Prometheus 使用的是 emptyDir,重启就没数据。生产环境必须挂 PVC。有个细节值得留意:storageClassName 要根据你的集群里的 StorageClass 去改,别照抄。
第三块:资源限制。
prometheus: prometheusSpec: resources: requests: memory: 4Gi limits: memory: 8GiPrometheus 是比较吃内存的,尤其抓取目标多、指标基数大的时候。我见过很多集群内存告警是因为把 Prometheus 内存限额设置得比正常水位还低。建议先给一个宽松的下限,观察几天再收。
部署带配置的安装命令:
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --values values.yaml如果已经默认装过,可以用helm upgrade带着同样参数更新。
3.4 Grafana 暴露访问与默认凭证
装完后 Grafana 默认是 ClusterIP,需要暴露出来才能访问。先看 Service:
kubectl get svc -n monitoring想快速访问,直接改成 NodePort:
kubectl patch svc kube-prometheus-stack-grafana -n monitoring \ -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 3000, "nodePort": 30080}]}}'然后通过任意节点的30080端口打开浏览器。如果想要域名访问,更优雅的做法是配 Ingress,这里不展开。
登录账号是admin,密码怎么拿?我一般这样执行:
kubectl get secret kube-prometheus-stack-grafana -n monitoring -o jsonpath="{.data.admin-password}" | base64 -d拿到密码后登录 Grafana,第一件事就是去左下角头像里把密码改了。默认密码在公司环境里就是个公共密码,不改迟早出事。
4. 数据接入:让 Grafana 看懂 K8s
4.1 手动配置 Prometheus 数据源
kube-prometheus-stack 装完后,Grafana 里其实已经自动添加了一个 Prometheus 数据源,名字通常叫“prometheus”。但如果你要在一个已有的 Grafana 实例上接入新集群,就需要手动添加。
点击左侧菜单 Connections → Data Sources → Add data source,选 Prometheus,URL 填集群内部地址:
http://kube-prometheus-stack-prometheus.monitoring.svc:9090如果你的 Grafana 和 Prometheus 不在同一个集群(比如集中式 Grafana 监控多个 K8s 集群),则需要填 Prometheus 的 Ingress 或者 NodePort 地址,并确认网络安全策略放通了端口。保存前点一下Save & Test,会显示Successfully queried the Prometheus API。很多“面板没有数据”的问题,都是在这一步暴露出来的。
4.2 自带仪表盘与社区仪表盘怎么选
kube-prometheus-stack 自带的 Grafana 里已经预置了不少 Dashboard,在 Dashboards 搜索栏里能看到,比如:
- Kubernetes / Compute Resources / Node
- Kubernetes / Compute Resources / Namespace (Pods)
- Kubernetes / Compute Resources / Pod
- Kubernetes / Kubelet
- Kubernetes / API server
这些自带的面板质量很高,而且版本一致性强,建议先看它们,别一上来就导入社区 Dashboard。社区里最常用的还有这么几个:
| Dashboard 名称 | Dashboard ID | 用途 |
|---|---|---|
| Node Exporter Full | 1860 | 节点级全量指标总览 |
| Kubernetes Cluster Monitoring | 315 | 集群总览(新版更推荐 15760) |
| Kubernetes Pod Monitoring | 8588 | Pod 资源消耗、重启、网络 |
| 1 Node Exporter for Prometheus Dashboard | 11074 | 单节点细颗粒度检查 |
导入方式:左侧 Dashboards → Import,输入 ID,然后选择你已经配置好的数据源。导入后如果发现部分查询报错,大概率是数据源名称不匹配,去 JSON 编辑器里把datasource字段改成你自己的数据源名称即可。
4.3 核心面板查询解读
能把面板里的 PromQL 读明白,比多导十个 Dashboard 都有用。这里挑三个最常用的给你解释一下。
CPU 使用率面板:
sum(rate(container_cpu_usage_seconds_total{container!="", pod!=""}[5m])) by (namespace, pod)container_cpu_usage_seconds_total是累计 CPU 秒数,所以要用rate算每秒速率。按 namespace 和 pod 聚合后,得到的就是“每个 Pod 用了多少个 CPU 核”。如果你想看某个 Deployment 的聚合,可以把by (namespace, pod)改成by (namespace, deployment),前提是你的工作负载有 deployment 标签。
内存使用量面板:
sum(container_memory_working_set_bytes{container!="", pod!=""}) by (namespace, pod)这里我用的working_set_bytes比memory_usage_in_bytes更接近“真实占用”的概念,它排除了部分可回收的缓存页,是判断内存是否吃紧更靠谱的指标。
重启次数面板:
max(kube_pod_container_status_restarts_total) by (namespace, pod)这个指标来自 kube-state-metrics,注意它是累计值。如果曲线是一条阶梯上升的线,说明这个 Pod 在频繁重启,通常要去看 CrashLoopBackOff 的日志。
5. 告警规则与通知:从“看到”到“收到”
5.1 通过 PrometheusRule 定义告警规则
光有 Dashboard 还不够,你不会 7×24 小时盯着屏幕。这套体系里的告警规则是通过PrometheusRule这个 CRD 定义的,Prometheus Operator 会把符合 selector 的规则文件自动加载进 Prometheus。写规则非常简单,下面这个 YAML 就是一条“节点 CPU 使用率超过 85% 持续 10 分钟”的告警:
apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: node-high-cpu namespace: monitoring labels: prometheus: kube-prometheus-stack-prometheus spec: groups: - name: node.rules rules: - alert: NodeCPUUsageHigh expr: (100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) * 100) > 85 for: 10m labels: severity: warning annotations: summary: "节点 {{ $labels.instance }} CPU 使用率超过 85%" description: "当前使用率已持续超过 10 分钟,请检查该节点上的容器分布。"关键点有三个:expr是触发条件,for表示条件持续多久才触发,annotations是告警消息里会展示的内容。for这个字段特别容易被忽略,很多人把for设成 0,结果节点内存稍微抖动一下就收到一堆告警轰炸。
告警规则写好后,在 Prometheus 自带的 UI 里打开Status → Rules,能看到每条规则的当前状态。如果规则没出现在列表里,检查 PrometheusRule 的 labels 是否匹配 Prometheus 的ruleSelector。
5.2 Alertmanager 通知配置:邮件、Webhook 实战
规则报警之后,Alertmanager 负责把信息推出去。在生产环境里最常见的两种方式是邮件和Webhook(企业微信/钉钉/飞书机器人)。
在 kube-prometheus-stack 里,可以通过 values.yaml 修改 Alertmanager 配置。下面是一个邮件通知的最小配置片段:
alertmanager: config: global: resolve_timeout: 5m smtp_smarthost: smtp.example.com:465 smtp_from: alert@example.com smtp_auth_username: alert@example.com smtp_auth_password: "your-smtp-password" route: group_by: ['alertname', 'cluster'] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: default-receiver receivers: - name: default-receiver email_configs: - to: ops@example.comWebhook 的配置类似,把email_configs换成webhook_configs,填入机器人地址即可:
receivers: - name: webhook-receiver webhook_configs: - url: "https://open.feishu.cn/open-apis/bot/v2/hook/your-bot-token" send_resolved: truesend_resolved: true很关键,它表示告警恢复时也推一条“已恢复”的消息,不然值班人员只能在群里看到告警而看不到恢复。
配置完更新 Prometheus 实例的状态,在 Alertmanager 的 Web UI(通常通过 Port Forward 访问 9093 端口)里能看到已接收的告警和所有配置的 receiver。测试告警链路时,我喜欢把某条规则的expr临时改成一个肯定满足的条件,等告警真正推送到群/邮箱后,再把规则改回来,这样能避免空跑一场。
6. 常见问题与排障实录
6.1 Prometheus 内存和磁盘一直涨
这条几乎每个集群都会遇到。Prometheus 的内存占用和指标基数直接相关,指标基数又由抓取目标和每个目标暴露的时间序列数量决定。如果你的集群规模不大(比如总 Pod 数小于 500),Prometheus 内存常年 2~4 Gi 是正常的,不用惊慌。真正的异常是内存无限增长且伴随查询变慢,这时优先检查:
kubectl top pods -n monitoring看 Prometheus Pod 是否超过 limits。其次检查抓取量:
# 查看 TSDB 样本数和序列数 kubectl exec -n monitoring prometheus-kube-prometheus-stack-prometheus-0 -- \ wget -qO- http://localhost:9090/api/v1/status/tsdb如果headStats里的numSeries达到几百万,说明抓取面太宽。常见优化手段:调低抓取频率(比如 30s 改 60s)、给 Prometheus 配置scrapeLimit或者调整 Selector 减少不需要的 target。磁盘增长一般是 retention 和 TSDB 压缩的问题,确认 PV 容量和 retention 是否匹配即可。
6.2 Grafana 面板大量 No data
No data 的原因太常见了,我见过不下十种,但排障顺序基本固定。
第一,先检查数据源是否连通:在 Grafana 的 Explore 页面输入一个最简单的查询up{},看有没有结果。如果返回空,去数据源设置里看 URL、认证方式、网络访问是否正常。
第二,检查面板的时间范围。Grafana 默认是 last 6 hours,如果 Prometheus 刚部署完,可能数据还不够 6 小时,切到 last 15 minutes 看看。
第三,确认指标名在新版本里是否已被替换。比如 kube-state-metrics 升级到 v2 后,很多旧 Dashboard 里的kube_*指标名变了,像kube_pod_container_status_restarts_total还在,但kube_pod_container_status_last_terminated_reason这类指标可能换名。面板里点开查询编辑器,看有没有红色提示。
6.3 告警不触发或一直 Pending
告警状态在 Prometheus UI 的 Alert 页面能看到。如果规则一直 Pending,说明条件已经满足但for的持续时间还没到,再等等看。如果一直 Pending 且永远不变成 Firing,检查是否是规则里引用了已变更的标签。比如instance这个标签在节点告警里是节点 IP:9100,但在 Pod 告警里可能不存在,用了错误的标签就会出现永假表达式。
如果规则已经 Firing 但群里没收到消息,先看 Alertmanager。打开 Alertmanager UI,查 Silences 和 Receivers,确认有没有被静默,再确认路由匹配的 receiver 是不是真的配置了推送地址。还有一个常见坑:多个 PrometheusRule 同时匹配同一条告警名字,导致 labels 冲突,接收信息里看不到应有的标签。
6.4 时间不同步导致数据错乱
这套监控对时间非常敏感。如果某个节点时钟偏差大,采集到的数据点会出现乱序,面板上就是一块一块的空洞,甚至 Prometheus 会报out of bounds。我发现很多集群跑了一段时间后时钟会慢慢漂移,所以生产环境一定给所有节点统一配好 NTP/chrony。排查命令:
# 看所有节点的时钟偏差量,用 node-exporter 的指标 node_timex_offset_seconds如果这个值长期大于几百毫秒,监控数据就不可信了。另外 Prometheus 容器本身的时间跟随宿主机,宿主机时间歪了,Prometheus 的查询区间也会跟着歪。
6.5 升级 Chart 的注意事项
kube-prometheus-stack 升级从来不是简单的helm upgrade。它包含大量 CRD,升级时 CRD 版本和 Prometheus Operator 版本必须匹配,否则会出现自定义资源被旧 Operator 解析不了的问题。升级前一定要把当前 values 导出备份:
helm get values kube-prometheus-stack -n monitoring > backup-values.yaml升级后先看 Prometheus Operator 和 Prometheus Pod 有没有滚动重启,再看 CRD 版本是不是和 chart 一致。遇到 Prometheus Pod 一直 CrashLoopBackOff 时,先看 operator 日志和 Prometheus 自身的日志,不要一上来就回滚,很多问题只是资源配置被新版规范改了字段名。
写在最后的一点个人体会
这套方案我反复搭过很多次之后,最大的感触是:Prometheus + Grafana 监控 K8s,真正难的地方不在“装起来”,而在“用起来”。装完半天,把面板和告警调好可能需要一周。尤其是告警阈值、规则分组、数据保留周期这些,没有一个标准答案,必须结合集群规模、业务特点、值班节奏去调。如果你刚搭好,我建议前两周先别急着把告警阈值调太低,宁可让 Prometheus 多收点数据、Grafana 多画几张图,等你熟悉了当前集群的基线数据,再去把告警规则收紧。这个循序渐进的过程,比我一开始就介绍的“生产完整配置”要稳得多,也更容易沉淀出真正属于自己团队的监控体系。