kube-prometheus-stack实战:构建K8S监控告警体系 简介面向云原生运维与K8S集群管理者这份资源合集聚焦基于kube-prometheus-stack构建监控告警系统的完整配置方案。内容涵盖Prometheus、Alertmanager、Grafana、Prometheus Operator等核心组件的部署思路并配有ServiceMonitor定义、告警规则与仪表盘调整示例适合希望在生产环境落地集群可观测性的中高级用户。压缩包共299个文件以272个YAML配置文件为主辅以模板、说明文档与Chart元数据整体仅690KB轻量但结构完整便于直接对照修改。目前已有783人学习下载资源附带的tpl模板可用于定制Helm图表md说明则梳理了部署步骤与排错要点能帮助快速掌握监控栈的配置逻辑与实际运维技巧。1. 一套开箱即用的 K8S 监控告警栈先看它解决了什么有一次线上服务挂了二十分钟才被用户发现凌晨两点的电话比告警先到从那以后我养成了习惯不管新集群多小先把监控告警铺上。这套kube-prometheus-stack资源合集就是把这件事做成了半成品——它打包了 Helm Chart、自定义 Grafana 仪表板和部署文档跟着 README 过一遍就能在 K8S 集群里拉起 Prometheus、Alertmanager、Grafana 这套完整的监控告警链路。适合正在搞 k8s 监控的运维和平台工程师也适合想搞懂 Prometheus Operator 工作原理的 k8s 学习者。它解决的问题很直接Pod 重启、节点宕机、PVC 快满、API Server 响应变慢这些事不该等用户来告诉你。2. 为什么是 kube-prometheus-stack组件拆解与选型理由2.1 社区默认组合不只是 Prometheus 单点部署如果你搜过 k8s 监控方案会看到两种主流路线一种是在集群里裸装一个 Prometheus配一堆手动发现的 job另一种是kube-prometheus-stack这种全家桶。前者适合单机实验后者才是生产环境该有的样子。这个资源合集选的是后者核心原因是它把 Prometheus 从手工维护的进程变成了K8S 原生声明的资源。裸装 Prometheus 的痛点很明显每加一个服务就要改一次配置文件每改一次就要 reload 一次进程。而在kube-prometheus-stack里Prometheus 的采集配置不再是一份静态 YAML而是通过 ServiceMonitor、PodMonitor 这类 CRD 动态发现目标。你只需要给服务打个标签Prometheus Operator 会自动帮你把采集任务生成好。这套机制让扩容和接入成本都降了一个量级。2.2 五个核心组件各自的职责边界先把这套栈里的组件拆开看搞清谁干什么后面排错才不抓瞎。Prometheus是核心的时序数据库负责拉取指标、存数据、算告警规则。它通过 PromQL 查询数据告警规则也用它写表达式。需要记住的是Prometheus 是拉模式不是推模式它主动去抓目标暴露的/metrics接口所以网络策略和安全组要放行。Prometheus Operator是这套栈的调度中枢。它监听 ServiceMonitor、PodMonitor、PrometheusRule、Alertmanager 这些 CRD把它们翻译成 Prometheus 和 Alertmanager 的实际配置。没有它你创建的 ServiceMonitor 就是一堆没用的 YAML。Alertmanager专门处理告警消息。Prometheus 把触发的告警推给它它负责分组、去重、静默、抑制再通过邮件、Slack、Webhook 通知出去。它的存在解决了一个节点宕机导致几十条告警刷屏的问题。Grafana做可视化。Prometheus 自带的 UI 只能做简单查询和规则维护真正的仪表板、多数据源、告警展示都要靠 Grafana。这个资源里带的custom-dashboard.json就是一块已配好的面板导入即用。Prometheus Adapter是连接 HPA 的桥梁。它把 Prometheus 里的自定义指标转成 Kubernetes Metrics API 的格式让 HPA 能根据 Pod 的 QPS、队列深度这类业务指标扩缩容。这块是很多团队到后期才补的资源里直接给你包含进来了。2.3 版本对应关系最容易翻车的部分部署这套栈之前第一条要核对的就是版本对应关系。社区对版本很敏感Prometheus Operator、Prometheus、K8S API 版本之间一旦不匹配部署完会出现 CRD 创建失败或 Operator 完全不工作的情况。组件常见版本与 K8S 的兼容说明kube-prometheus-stack45.x ~ 56.xchart 版本间 CRD 结构差异大升级要谨慎Prometheus / Alertmanager2.45 以上与 CI/CD 中的 kubelet 指标采集兼容K8S 集群1.23 ~ 1.291.25 之后 kubelet 的/metrics改用鉴权需额外配置Grafana9.x ~ 10.xdashboard JSON 兼容性取决于版本提示部署前先查 chart 的 values.yaml 里写的 K8S 版本要求用kubectl version和你的集群版本对照。版本不匹配是这套栈最常见的部署失败原因没有之一。2.4 资源消耗预估先算账再上生产还有一个需要提前算清楚的事情这套栈不是玩具默认安装会吃掉不少资源。我一般在部署前先用helm show values看默认配置然后按节点规模画一个底线。单 master 测试集群Prometheus 2GB 内存、1 核 CPUGrafana 512MBAlertmanager 256MB。5~10 节点生产集群Prometheus 至少 4GB 内存存储 head 块配合持久卷跑 15 天下文再讲。如果你的集群节点数超过 20建议把 Prometheus 拆成多个实例按 namespace 分片否则查询性能会明显下降。评估资源时还有个小技巧看 kube-state-metrics 采集的数据量。这个组件会拉取集群中所有核心资源对象的状态信息Pod 多、CRD 多内存消耗就水涨船高。如果发现 Prometheus 经常 OOM优先检查 kube-state-metrics 的资源限制是否太低。3. 用 Helm 部署 kube-prometheus-stack四步落地与 values 配置3.1 前置检查Helm、StorageClass、CRD 一个都不能少动手之前先把三样东西确认好。第一是 Helm 版本3.x 是必须的这个 chart 不支持 Helm 2。第二是 StorageClassPrometheus 和 Grafana 默认都开着持久卷如果你的集群没配默认 StorageClassPVC 会一直 Pending。第三是权限kube-prometheus-stack 会创建 ClusterRole 和 ClusterRoleBinding所以执行helm install的账号需要集群管理员权限。# 检查 Helm 版本 helm version --short # 检查默认 StorageClass 是否存在 kubectl get storageclass # 检查当前集群权限输出应包含 cluster-admin kubectl auth can-i create clusterrole --all-namespaces这三条命令不用每一条都看懂输出细节重点看有没有报错。第一条确认包管理器可用第二条决定你后面要不要手动指定 storageClass 参数第三条是权限底线——如果输出为 no后面安装会卡在 CRD 创建阶段。3.2 添加仓库并安装用固定版本而不是 latest仓库地址是这个 chart 官方发布源添加后建议先搜索版本列表选一个你确认过兼容性的版本号再装。直接装 latest 是给自己埋坑你永远不知道 chart 更新会带来什么 CRD 变化。# 添加 prometheus 社区仓库 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update # 搜索可安装的版本 helm search repo prometheus-community/kube-prometheus-stack --versions | head -20搜索结果里会有几十个版本号。我一般选最新的稳定版本往回退一两个小版本因为最新版可能刚发布存在兼容问题退一个版本往往是最稳的状态。选定版本后执行安装helm install kps prometheus-community/kube-prometheus-stack \ --version 45.0.1 \ --namespace monitoring \ --create-namespace \ -f values.yaml这里把参数列一下kps是 release 名称后续所有操作都围绕这个名字--namespace monitoring指定部署空间--create-namespace在没有该 namespace 时自动创建-f values.yaml指向你的自定义配置。注意我们在这里先引用了 values.yaml这个文件在下一步就要准备好。3.3 自定义 values.yaml三类必调参数安装前必须准备一份 values.yaml不写这个文件直接装默认配置的告警渠道是空的Grafana 密码也是随机的装完根本没法用。我按重要性分三类第一类必须改第二类建议改第三类按需改。# values.yaml - 第一版基础配置 prometheus: prometheusSpec: # 数据保留天数默认 10 天对生产环境不够看 retention: 15d # 资源限制别让 Prometheus 吃掉整个节点 resources: requests: memory: 2Gi cpu: 500m limits: memory: 4Gi cpu: 2 # 每 30 秒检查一次规则告警延迟可接受 evaluationInterval: 30s # 持久化配置依赖前置检查里的 StorageClass storageSpec: volumeClaimTemplate: spec: accessModes: [ReadWriteOnce] resources: requests: storage: 50Gi grafana: adminPassword: your-strong-password # 让 Grafana 只允许内网访问别暴露到公网 service: type: ClusterIP alertmanager: alertmanagerSpec: resources: requests: memory: 256Mi limits: memory: 512Mi这段配置有三个关键决策。retention: 15d是我个人的习惯值太短查不到一周前的历史数据太长磁盘和查询压力都大Prometheus 资源请求和限制之间留了一倍余量防止指标量突增直接 OOMGrafana 设成 ClusterIP 是安全考量需要访问时用kubectl port-forward临时暴露。3.4 验证部署状态看 Pod、看 CRD、看 Targets安装后不要急着配告警先确认整套系统真的活了。三个检查维度按顺序来。# 1. 检查所有 Pod 是否 Running kubectl get pods -n monitoring # 2. 检查 CRD 是否注册成功 kubectl get crd | grep monitoring # 3. 检查 Prometheus 采集目标是否在线 kubectl port-forward -n monitoring svc/kps-kube-prometh-prometheus 9090:9090注意第三条需要保持终端运行另开一个终端访问http://localhost:9090/targets你会看到一堆 state 为 UP 的采集目标。如果这里出现大量 DOWN说明 kubelet 指标接口的鉴权配置没对齐——这是后面避坑章节的第五条重灾区。3.5 配置 ServiceMonitor让 Prometheus 认识你的业务服务部署对象本身只是监控集群底座真正要监控你的业务服务得定义 ServiceMonitor。它的名字听起来复杂原理很简单告诉 Prometheus 这几个服务需要被采集然后 Operator 自动生成抓取配置。# service-monitor-example.yaml apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: my-service-monitor namespace: monitoring spec: # 选择要监控的目标 Service这里按标签匹配 selector: matchLabels: app: my-web-service # 指定从哪个端口采集指标 endpoints: - port: metrics interval: 30s path: /metrics # 限定只在目标 namespace 中寻找匹配的 Service namespaceSelector: matchNames: - production创建完这个 ServiceMonitor 后不需要重启任何组件。Prometheus Operator 会监听这个 CRD 的事件自动把抓取配置更新进 Prometheus。这里的五个字段是核心selector按标签选服务、endpoints.port指定端口名、interval定义抓取频率、path明确指标路径、namespaceSelector划定搜索范围。4. 配置 Alertmanager 告警路由、抑制与通知渠道接入4.1 告警路由避免告警轰炸的关键Alertmanager 的核心概念是路由树。Prometheus 产生的所有告警会进入 Alertmanager由它根据标签匹配规则决定送到哪个通知渠道。默认配置只给了一个空路由不配的话告警发不出去所以这是必须做的一步。# alertmanager-values.yaml 片段 alertmanager: alertmanagerSpec: configSecret: global: # 抑制规则里的时间窗 resolve_timeout: 5m route: # 根路由当 receiver 不存在时兜底 receiver: default group_by: [alertname, cluster] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: - matchers: - severitycritical receiver: critical-webhook - matchers: - severitywarning - namespacedefault receiver: warning-webhook receivers: - name: default webhook_configs: - url: http://localhost:8080/alert - name: critical-webhook webhook_configs: - url: http://alert-bot:8080/critical - name: warning-webhook webhook_configs: - url: http://alert-bot:8080/warning这段配置里四个时间参数是配置调优的重头。group_wait是告警分组前等待的时间设太短会把抖动触发的告警一条条全发出去group_interval是同组告警再次发送的最小间隔repeat_interval是已解决但仍未恢复的告警重新提醒的周期我一般设 4 到 8 小时resolve_timeout控制自动恢复的判定时间。路由树的分支用matchers按标签分流critical 走单独通道warning 走另一个分组逻辑从这里分流。4.2 接入钉钉webhook 转发是最常用的方案大多数团队不会直接用原生 webhook而是通过一个转发服务把 Alertmanager 的消息推到钉钉、企业微信或 Slack。原理不变——Alertmanager 配置 webhook receiver转发服务负责转换格式并调用平台 API。这里以钉钉为例做一个极简转发服务。# alert-dingtalk-forward.py - 极简转发服务 from flask import Flask, request import requests, json, os app Flask(__name__) DINGTALK_WEBHOOK os.environ.get(DINGTALK_WEBHOOK, ) SECRET os.environ.get(DINGTALK_SECRET, ) app.route(/alert, methods[POST]) def alert(): data request.json # Alertmanager 会把所有告警放在 alerts 数组里 alerts data.get(alerts, []) if not alerts: return ok # 提取关键字段组装成钉钉支持的 markdown 消息 title alerts[0].get(labels, {}).get(alertname, unknown) content ## K8S 告警通知\n\n for a in alerts: labels a.get(labels, {}) annotations a.get(annotations, {}) content f- **{labels.get(alertname)}** \n content f 级别: {labels.get(severity)} | 命名空间: {labels.get(namespace)}\n content f 描述: {annotations.get(description, )}\n # 发消息到钉钉机器人 response requests.post( DINGTALK_WEBHOOK, json{msgtype: markdown, markdown: {title: title, text: content}} ) return response.text if __name__ __main__: app.run(host0.0.0.0, port8080)这个脚本里有一个容易忽略的设计点Alertmanager 的 webhook 请求体里会有alerts数组可能同时携带多条告警。很多新手在这里只取data[alerts][0]导致同一批告警只发一条、其他的丢了。把消息体里的告警逐条遍历拼进 markdown 才是常规做法。另外annotations.description里通常包含了阈值和当前值信息这对值班人员判断问题非常重要不能省略。部署这个服务的方式很简单打成镜像放在集群里Service 暴露 8080 端口然后把 Alertmanager 的 webhook URL 指向它。4.3 测试告警链路手工触发一条看看配置完不要直接收工先手工触发一条测试告警验证从 Prometheus 规则到钉钉消息的整条链路是否通畅。# 进入 Prometheus Pod 手动查询确认规则已加载 kubectl exec -it -n monitoring kps-kube-prometh-prometheus-0 -- \ wget -qO- http://localhost:9090/api/v1/rules | grep -c name # 临时创建一个触发告警规则源码里的 alert-test 示例 kubectl create -f - EOF apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: test-alarm namespace: monitoring spec: groups: - name: test rules: - alert: TestAlert expr: vector(1) labels: severity: critical annotations: description: 这是一条测试告警 EOF第二条命令的expr: vector(1)会永远返回 1意味着告警立即触发。等evaluationInterval配置的 30 秒过去后你应该能在钉钉里收到这条测试消息。确认收到后把测试规则删掉避免它每 4 小时重复提醒一次。4.4 打通 Prometheus AdapterHPA 依据自定义指标扩缩容最后把 Adapter 接入业务监控链路。前面 3.5 节配了 ServiceMonitor现在把这些自定义指标喂给 HPA。注意直接使用 Prometheus 查询结果做 HPA 需要额外配置规则。# adapter-values.yaml 附加配置 prometheusAdapter: rules: default: false custom: - seriesQuery: {__name__~my_app_request_total} resources: overrides: namespace: {resource: namespace} pod: {resource: pod} name: matches: ^(.*)_total$ as: my_app_qps metricsQuery: | sum(rate(.Series{.LabelMatchers}[2m])) by (.GroupBy)这个配置做了一件事把my_app_request_total这个计数器类型的指标转成my_app_qps的速率指标并暴露给 HPA。rate()函数计算的是 2 分钟窗口内的每秒平均增量这避免了瞬时抖动导致误扩缩。配置完后HPA 的metrics字段里就能引用pods/my_app_qps这种指标了。5. 避坑指南五个真实踩坑记录与复盘5.1 现象Prometheus Pod 反复被 OOMKilled现象部署正常运行两周后Prometheus Pod 频繁重启kubectl describe pod显示状态为 OOMKilled。原因这是最常见的坑之一。默认的 values.yaml 里 Prometheus 的 limits.memory 设得太小而 kube-state-metrics 和节点导出器采集的数据量随着节点数、Pod 数增长远超预期。更隐蔽的是Prometheus 的 head 块内存占用和 scrape 间隔成正比1 分钟间隔改成 30 秒后内存压力直接翻倍。解决把 limits.memory 提到 4Gi 或更高同时把storage.tsdb.retention.size加上限值——只设了 Retention 时间没设 sizePrometheus 会在磁盘写满前无限占用内存。我给一个经验公式节点数量乘以 150MB 是 Prometheus 的最小内存需求业务指标特别多的再乘 1.5 系数。5.2 现象升级 chart 后 CRD 报错所有 ServiceMonitor 失效现象用helm upgrade从旧版本升到较新版本后kubectl get servicemonitor仍能列出资源但 Prometheus 里所有 Targets 都消失了。原因kube-prometheus-stack 的 CRD 结构在不同版本间有破坏性变更而 Helm 不会帮你自动迁移已有的 CRD 实例。最常见的是 v1 CRD 和新的 apiVersion 不匹配导致 Operator 无法解析旧的 ServiceMonitor。解决升级前先备份所有 CRD 实例的 YAML然后删掉旧 CRD 再装新的。操作顺序是kubectl get servicemonitors -A -o yaml monitor-backup.yaml然后卸载 release、清理 CRD最后用新版本重新安装并恢复备份。从那以后每次升级我都先读 changelog看有没有 CRD breaking change。5.3 现象Grafana 仪表板大盘全是 N/A现象导入 custom-dashboard.json 后部分面板显示Data source not found或图表全部是 N/A。原因dashboard JSON 里的数据源名称和实际部署的 Prometheus 数据源名称不一致。很多第三方 dashboard 写死了datasource: Prometheus但你的 Grafana 里配置的数据源叫prometheus或kps-prometheus。解决进入 Grafana 的仪表板设置用变量替换方式查询每个面板的数据源值。如果多个面板都是这个问题直接在 dashboard JSON 里全局替换datasource: Prometheus为datasource: prometheus改成你实际的数据源名。这个坑所有 Grafana 用户都踩过不算高级但足够恼人。5.4 现象告警通道每分钟收到几十条消息夜间被轰炸现象某个应用发布后钉钉群每分钟收到几十条告警消息全都来自同一个 alertname。原因Prometheus 告警规则里没定义合适的for参数导致指标只要瞬时超过阈值就触发一分钟内的抖动也会触发并上报。Alertmanager 虽然有group_interval分流但如果每个告警的标签组合不同比如 Pod 名作为标签分组机制根本压不住。解决给每条告警规则加上for: 5m让指标连续 5 分钟超过阈值才真正触发。这个方法对抖动型告警有奇效。另外在 Alertmanager 的抑制规则里把 workload 级别的告警和 Pod 级别的告警做抑制source_matchers: [severitycritical]这样高等级告警触发时可以自动隐藏同源的较低等级告警。5.5 现象节点监控大片 DOWNkubelet 指标拉不到现象Prometheus Targets 页面里 node-exporter 和 kubelet 大部分状态是 DOWN但业务服务的指标正常。原因K8S 1.25 之后 kubelet 的/metrics接口默认要求 HTTPS 且需要鉴权。kube-prometheus-stack 通过 kubelet service 来抓取指标但使用的 ServiceAccount 缺少针对 kubelet 的认证权限。大部分工程师在这里卡住很久。解决确认 values.yaml 里kubelet.serviceMonitor.https: true并且kubelet.serviceMonitor.cAdvisor也是 true。如果仍然 DOWN检查集群是否启用了 webhook 鉴权——如果 kubelet 配置了--authorization-modeWebhook需要给采集器用的 ServiceAccount 授权。在我的实践里这个问题的排查率最高遇到先看 Targets 页面里的具体错误信息再动手。5.6 现象数据盘写满Prometheus 持续报错现象运行几个月后数据盘使用率达 95%Prometheus 日志中出现大量storage.remote.full或写入失败。原因retention只是时间维度没说数据量维度。同一时间段内如果业务指标特别多每天产生的 TSDB 数据量会远超估计时间维度再短也填满磁盘。解决在 values.yaml 里同时加storage.tsdb.retention.size: 40Gi。时间、大小两个维度双上限哪个先到算哪个实际效果是磁盘使用率被完全控制住。这个配置是生产环境必须具备的但很多默认配置里根本没给提示。6. 进阶把 custom-dashboard.json 和告警闭环做彻底验证6.1 导入自定义仪表板不要光用默认面板资源里自带的 custom-dashboard.json 是一份已经调好的业务监控面板包含 Pod 重启次数、容器 CPU 使用分布、API Server 延迟分位数等高频关注项。与其自己在 Grafana 里从零拖拽不如直接用这份 JSON把它放到 Grafana 的 provisioning 目录里实现自动加载。# grafana-dashboard-config.yaml 给 Grafana 挂配置 apiVersion: v1 kind: ConfigMap metadata: name: custom-dashboards namespace: monitoring labels: grafana_dashboard: 1 data: custom-dashboard.json: | # 这里粘贴 custom-dashboard.json 的完整内容 # 注意 YAML 缩进json 内容整体缩进两级这个方式绕过了手动导入的过程重装 Grafana 后仪表板也会自动恢复。重点是把grafana_dashboard: 1这个标签打对Grafana 的 sidecar 容器会扫描带这个标签的 ConfigMap并自动加载里面的 JSON 文件。6.2 验证告警闭环规则、触发、通知、恢复每一步都检查部署完成后的第一周我推荐做一次完整的告警演练。先查 Prometheus Alerts 页面确认规则都加载了再手工触发一条测试告警参考 4.3 节的方法等消息进入钉钉后把触发条件恢复原状确认 Alertmanager 发出 resolved 通知。这一步不要偷懒——很多团队的告警通道搭好了一个月没发过消息真要出问题时才发现 webhook 早就失效了。6.3 数据持久化与备份给关键配置上双保险Prometheus 的告警规则、Alertmanager 的配置、Grafana 的 dashboard 这三类信息应该全部用 ConfigMap 或 PrometheusRule CRD 声明纳入 Git 管理。资源里的 Chart.lock 文件锁定了依赖版本这就保证了任何时间重新部署得到的版本组合完全一致。我从不用手工改容器内配置的方式做持久化变更因为 Pod 一重启东西就没了还不计入审计。从那以后每次搭建新的 K8S 集群监控我都会强制走一遍同样的流程先查版本对应关系再写 values.yaml装完立刻检查 Targets 和告警链路最后把 custom-dashboard.json 挂进 provisioning。这套流程跑顺了新集群从零到有监控告警只需要半天。希望帮到你。本文还有配套的精品资源点击获取