简介:在云原生场景中,Kubernetes集群的监控与告警是保障稳定性的关键。这套资源合集面向运维工程师与云原生开发者,围绕kube-prometheus-stack提供了一套可直接参考的部署配置与说明。包体共299个文件,以272个yaml配置为主,辅以tpl模板、md说明文档及少量txt、json与Chart.lock等文件,压缩包仅690KB,内容精炼却覆盖了Prometheus、Operator、Alertmanager、Grafana等核心组件的典型配置。已有783人学习下载。资源中包含了完整的Helm Chart目录结构、可自定义的告警规则示例以及ServiceMonitor配置思路,能够帮助读者理解各组件如何协同工作,并根据自身集群环境调整参数、快速落地一整套监控告警方案,提升故障排查效率。
1. 为什么我最终选了 kube-prometheus-stack:这可能是 K8S 可观测性最省心的一条路
接手 K8S 集群监控这件事,大部分人的第一反应是去装 Prometheus 和 Grafana,装完才发现这只是开始:指标抓谁、怎么抓、Pod 重启了告警谁来发、Grafana 面板一个个手工建……特别是当你手上不止一套集群,开发环境、预发环境、生产环境各一套,这种重复劳动的痛感会非常明显。kube-prometheus-stack 这个方案把 Prometheus、Alertmanager、Grafana 以及一系列开箱即用的告警规则和面板打包成一个 Helm Chart,一条命令就能把整套监控体系拉起来,而不是一个个组件零散地去装。它解决的问题很直接:让 K8S 集群的监控告警从“要自己拼积木”变成“装好即用”,再按自己的需求去改。适合正在维护 K8S 集群、被节点状态、Pod 异常、API Server 抖动困扰的开发者或运维工程师——这个标题涉及的资源合集,本质上是帮你把监控这件事的“选择成本”省掉。
2. 看清 kube-prometheus-stack 的家底:组件关系与告警链路,先懂再部署
2.1 这个 Chart 里到底装了什么:组件清单与各自职责
在动手敲 Helm 命令之前,先把 kube-prometheus-stack 到底装了什么搞清楚,否则出了问题你连去哪个 Pod 里查日志都不知道。这个 Chart 实际上是 kube-prometheus 项目的一个 Helm 封装,标准安装会拉起来这样一批核心组件:Prometheus Operator、Prometheus、Alertmanager、Grafana,还有一堆 CRD 和 Exporter。
Prometheus Operator 是整个栈的“控制面”,它通过 Kubernetes 的 CRD 机制来管理 Prometheus 和 Alertmanager 的实例。换句话说,你不再直接改 Prometheus 的 YAML 配置文件,而是通过创建Prometheus、Alertmanager、ServiceMonitor、PrometheusRule这类自定义资源,Operator 监听这些资源的变化并自动把它们渲染成底层的 Prometheus 配置。这套机制最大的好处是配置走 Git 化、走声明式,集群里的监控配置可以被审批、被回滚,而不是在生产机器上手工改一个 prometheus.yml。
Grafana 负责可视化,这部分没什么新奇的,但要注意的是,kube-prometheus-stack 自带了一套预置的 Dashboard,覆盖 Kubernetes 集群的节点、Pod、容器、API Server 等多个维度。这些 Dashboard 是通过 ConfigMap 注入 Grafana 的,安装完以后在 Grafana 的 Dashboards 页面直接就能看到。Exporter 方面,Chart 默认会部署 node-exporter 来采集节点指标,kube-state-metrics 来从 Kubernetes API Server 生成对象的状态指标。这两者一个是看机器(CPU、内存、磁盘、网络),一个是看 K8S 对象(Deployment 副本数、Pod 状态、资源配额),缺一不可。
这里想强调一下 Prometheus 抓取指标的整体链路,理解了这条链,后续调参都会顺手很多。指标的生产者是各类 Exporter,比如 node-exporter 暴露节点指标的/metrics接口;采集者是 Prometheus Server,它通过 ServiceMonitor 这个 CRD 来发现“该抓谁”,ServiceMonitor 里定义了 namespace 选择器、标签选择器以及采集路径;抓回来的数据存在 Prometheus 里,由 PrometheusRule 定义告警规则做阈值判断,触发后发给 Alertmanager,Alertmanager 再负责去重、分组和通过 receiver(比如钉钉、飞书、邮件)把告警发出来。整个链路里 ServiceMonitor 和 PrometheusRule 是最常打交道的两个 CRD,后面第三章和第四章都会反复用到。
2.2 为什么要用 Operator 而不是直接裸装 Prometheus
直接裸装 Prometheus 并不是不行,小集群、自己学习、只监控一个应用,裸装反而更轻量。但对于一个要长期维护的 K8S 集群,我建议还是走 kube-prometheus-stack,核心原因是运维模型的差别。裸装 Prometheus,配置是静态文件,每次加一个采集目标、改一条告警规则都要 reload 配置,多个 Prometheus 实例之间配置没法同步;kube-prometheus-stack 引入 Operator 以后,采集目标和告警规则全部变成 K8S API 里的资源,你只需要kubectl apply一个 ServiceMonitor 或 PrometheusRule,Operator 会自动把变更滚到 Prometheus 配置里,不需要 SSH 进 Pod。
另一个实际的好处是版本匹配问题被解决了大半。Prometheus 的版本迭代很快,Grafana 的插件和数据源也要对得上,手工装容易装出“监控自己先挂了”的窘况。kube-prometheus-stack 的 Chart 对 Prometheus、Alertmanager、Grafana 的版本组合做过验证,Helm 安装时一次性把版本锁定,后续升级整体走 Helm,不用一个一个组件去升。
从告警规则的角度讲,裸装的代价更高。很多人裸装完 Prometheus 后,发现集群里出现了故障却没有任何通知,原因就是告警规则完全空白。kube-prometheus-stack 默认带了一百多条 K8S 相关的告警规则,从节点 CPU 高、内存压力、Pod 反复重启到 API Server 请求延迟高,覆盖面相当全。虽然这些默认规则不一定都适合你的业务场景,但至少给了你一个能跑的起点,而不是从零开始想“到底该监控什么”。
2.3 最小部署需要什么资源:给硬件预算一个参考
kube-prometheus-stack 的资源消耗不算小,这是很多第一次部署的人会低估的。默认情况下,Prometheus 会启用 rules 和 alerts 两个文件系统,加上 WAL,存储占用增长得比想象中快。我的经验是,一个 3 节点的测试集群(每台 4C8G),默认配置装完,Prometheus、Grafana、Alertmanager 加 exports 大约要吃掉 2-3G 内存。如果节点本身只有 4G 内存,那监控系统带来的压力会让集群更不稳定,这种时候就要做资源裁剪。
常见的裁剪手段有两个方向。第一,通过 values.yaml 关掉不需要的组件,比如你已经有了一套 Grafana,就可以把grafana.enabled设为 false;不需要 Alertmanager 就设alertmanager.enabled为 false,但这会连带失掉告警能力,不建议生产环境这么做。第二,限制 Prometheus 的资源请求,默认 Chart 里 Prometheus 的 requests.memory 通常会给到 500Mi 甚至更高,测试环境改成 256Mi 也能跑,只不过在指标量大的时候会频繁 OOM,不建议极限压榨。总的建议是:持久化测试环境节点至少 2C4G,生产环境至少 4C8G,并且给 Prometheus 单独配一块持久化存储,不要让它在 Pod 重启后丢失所有历史监控数据。
提示:K8S 1.25 之后移除了 PodSecurityPolicy,如果看到 Chart 里有 psp 相关的 value,可以直接忽略,默认不会再创建 PSP。另外,确保你的 Helm 版本在 3.8 以上,kube-prometheus-stack 的 CRD 较多,旧版本 Helm 在安装时对 CRD 的处理会有些兼容性问题。
2.4 我一般怎么选择版本:Chart 版本与 K8S 版本的匹配
讲个血泪经验:kube-prometheus-stack 不是随便装个最新版就行的,Chart 版本与 K8S 版本的兼容性列表值得花 5 分钟去查一下。K8S 的 API 在演进过程中有废弃和更迭,比如apiextensions.k8s.io/v1beta1的 CRD 在 K8S 1.22 就已经不可用了,如果装了一个依赖旧 API 的 kube-prometheus-stack 版本,轻则 CRD 创建失败,重则整个 Operator 启动不起来。
我的习惯是:先看自己的 K8S 版本,然后去 kube-prometheus-stack Chart 的 README 或者 release note 里确认它支持的 K8S 版本范围。通常 Chart 的 major.minor 版本会标注对 K8S 的兼容性,比如要求 K8S >= 1.26 之类的。如果 K8S 版本比较新(比如 1.30 以上),就不要用太老版本的 Chart,否则可能出现 kube-state-metrics 调用了新 API 导致报错的情况。实践上,我一般选比自己 K8S 版本早一个版本范围内的 Chart 稳定版,不追最新。版本对应不上,后面第五章的一大半问题都会找上门来。集群版本确认用kubectl version --short,装 Chart 之前先用helm install --dry-run --debug预演一遍,这一步能省掉后面很多翻车的时间。
3. 用 Helm 部署 kube-prometheus-stack:从添加仓库到验证告警通路
3.1 准备阶段:Helm、StorageClass 与命名空间规划
部署前的准备没什么高深的东西,但每一项没做都会成为后面的坑。首先确认 Helm 已经安装,且版本不低于 3.8。然后确认集群里的默认 StorageClass 是否存在,因为 Prometheus 和 Grafana 默认都会申请 PVC,如果集群里没有 StorageClass,PVC 会一直 Pending,Prometheus 的 Pod 起不来。执行kubectl get sc看一下,如果没有默认 StorageClass,要么装一个(比如 nfs-client-provisioner 或者 local-path-provisioner),要么在 values.yaml 里把prometheus.prometheusSpec.storageSpec和grafana.persistence的 size 改成 0 或直接禁用持久化——测试环境可以禁用,生产环境强烈建议配好 StorageClass。
命名空间规划上,我习惯把所有监控组件统一放在一个独立的 namespace,比如monitoring。这样 RBAC 的绑定、网络策略的配置、后续的审计都比较清晰。把监控组件散落在 kube-system 或其他命名空间里不是一个好主意,因为其他团队的人可能无意中改到这些资源。为了让 Prometheus 能够采集整个集群的指标,它需要访问其他 namespace 的资源,这一点通过 Chart 默认创建的一个 ClusterRole 就能搞定,不需要额外手动授权。
3.2 安装命令与最小 values.yaml 配置:先跑起来再调优
添加仓库和安装的命令并不复杂,真正花心思的是 values.yaml。我先给一个能跑通的最小配置作为起点,也就是“先跑起来再调优”的思路。下面是添加仓库并安装的指令序列,以及一份适合测试环境的最小 values.yaml。
# 添加 prometheus-community 仓库,注意这是社区维护的仓库 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update # 创建监控命名空间 kubectl create namespace monitoring # 用一份自定义 values 文件安装 chart # values.yaml 放的是下面贴出的最小配置 helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --values values.yaml# values.yaml — 测试环境最小可用配置 grafana: adminPassword: "admin123" # 测试环境可以直接用,生产环境务必改成强密码 persistence: enabled: false # 测试环境不持久化 Grafana 数据,重启面板配置会丢 service: type: NodePort nodePort: 30300 # 固定 NodePort,方便本地浏览器访问 prometheus: prometheusSpec: storageSpec: volumeClaimTemplate: spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 20Gi # 测试环境 20G 够跑很久;生产按保留时长和指标量估算 resources: requests: memory: 512Mi cpu: 250m limits: memory: 2Gi # limit 给足,给 Prometheus 留出处理波峰的空间 alertmanager: enabled: true service: type: NodePort nodePort: 30301 # Alertmanager UI 端口 # 关闭组件自带的 podMonitor 命名空间过滤,采集全集群 prometheusOperator: namespaces: ""这份 values.yaml 的逻辑比较简单:测试环境不持久化 Grafana,Prometheus 用 20G 的 PVC 先跑着,两个 UI 都用 NodePort 暴露。旁边的注释写着每个字段的实际用意,照着填就行。要注意的是prometheusOperator.namespaces设为空字符串,是让 Operator 关联所有 namespace 中的 ServiceMonitor,否则默认只监听自己所在的 monitoring namespace,这会导致你部署在其他 namespace 下的应用指标采集规则不被识别。
安装完成后等待 Pod 就绪,通常需要 1-3 分钟去拉镜像和初始化,执行下面的命令确认状态:
kubectl -n monitoring get pods kubectl -n monitoring get svc正常情况下,你会看到prometheus-kube-prometheus-stack-prometheus-0、alertmanager-kube-prometheus-stack-alertmanager-0、grafana-xxx等几个 Pod 处于 Running 状态。如果发现 Pod 一直 ContainerCreating,先kubectl -n monitoring describe pod <pod-name>看事件,最常见的是 PVC Pending,也就是 StorageClass 没配好。
3.3 访问 Grafana 与 Prometheus:端口转发与初始登录
部署完成后,本机浏览器访问 Grafana 的方式有两种。一种是直接访问 NodePort(如果你按上面的配置设了 NodePort 的话),另一种是在本地跑 kubectl port-forward,这种方式在云环境里更常用,因为云厂商的 NodePort 经常被安全组挡在外面。
# 语法:kubectl -n monitoring port-forward svc/<grafana-service-name> 本地端口:服务端口 kubectl -n monitoring port-forward svc/kube-prometheus-stack-grafana 8080:80执行后,浏览器打开http://localhost:8080,默认账号是admin,密码是你在 values.yaml 里设置的adminPassword,这里就是admin123。第一次登录后建议立刻去改密码,这只admin账号在 Grafana 里拥有 Admin 权限,被拿到基本上等于整个监控系统失守。Grafana 的数据源已经由 Chart 自动配置好了,进入 Dashboards 页面,可以看到 Kubernetes 相关的全家桶面板。
验证 Prometheus 的数据通路是否正常,这一步很重要。打开 Prometheus 的 UI,路径是http://nodeIP:30301或通过 port-forward,然后执行kubectl -n monitoring port-forward svc/kube-prometheus-stack-prometheus 9090:9090,在浏览器里访问localhost:9090。进入 Status -> Targets 页面,你会看到一大批采集目标,包括 node-exporter、kube-state-metrics、coredns、kubelet 等,每个 target 的 State 应该是 UP。如果存在 DOWN 的资源,点进去看 Last Scrape 的报错信息。常见的是 kubelet 的采集路径不对,这在 K8S 1.19+ 之后普遍走的是 HTTPS 的 10250 端口,如果 Chart 版本太旧,默认配置还在抓 10255,这一条在第五章排查部分再细说。
3.4 验证告警通路:把一条测试告警打到 Alertmanager
部署完成后不要急着下班,验证一下告警链路是否真的通了。一个很直观的办法是直接看 Prometheus 的 Alerts 页面,里面会列出所有 PrometheusRule 定义好的告警规则。我们把一条测试规则手动触发一下,但更稳妥的做法是给 Alertmanager 随便发一条消息,看能不能投递到接收端。下面这条命令可以把一条模拟告警直接推给 Alertmanager 的 API,用 curl 即可。
# 先做端口转发,把本地 9093 端口映射到 Alertmanager 服务 kubectl -n monitoring port-forward svc/kube-prometheus-stack-alertmanager 9093:9093 & # 向 Alertmanager API 推送一条模拟告警,验证 alertmanager 的接收和通知能力 curl -X POST http://localhost:9093/api/v1/alerts -d '[{ "labels": {"alertname": "TestAlert", "severity": "warning"}, "annotations": {"summary": "这是一条测试告警"}, "generatorURL": "http://localhost" }]'推送成功后在 Alertmanager UI 的 Alerts 页签里应该能看到这条测试告警。如果你在 values.yaml 里还没有配置任何 receiver,那这条告警只会显示在 UI 里,不会被发出去——这是正常行为,因为默认 Alertmanager 配置下 receiver 只有一个null。所以验证告警通路分成两步:第一步是先把告警推进 Alertmanager,这是验证数据链路;第二步是配置好渠道后,再推一次验证投递链路。一次全通的经验很少,大部分情况都是要先在 Alertmanager 里加个 webhook 或邮箱,再回来重新推。
到这里部署工作已经完成大半,但距离“能用”还有一段路要走。默认规则和默认面板是社区给的通用模板,真正贴合自己业务场景的告警阈值和命名规则还需要调整——这也正是第四章要讲的内容。
4. 把默认告警规则改到能用:PrometheusRule 定制与实践
4.1 从一百多条默认规则里挑出适合自己集群的保留项
kube-prometheus-stack 装好以后,Prometheus 里有上百条告警规则,这是个好消息也是个坏消息。好消息是不用从零写,坏消息是如果全量保留,你的 Alertmanager 会经常在半夜把值班人吵醒,一堆毫无区分度的告警涌过来,最后变成“狼来了”——真正重要的告警反被淹没。
我建议的默认规则梳理思路是这样的:先搞清楚自己的集群规模和生产环境等级,然后分三类处理。第一类是必须保留的高价值规则,比如KubePodCrashLooping(Pod 反复崩溃重启)、KubeNodeNotReady(节点不可用)、KubeAPIHighErrorRate(API Server 错误率高)、KubePersistentVolumeFillingUp(PVC 快被写满)。第二类是需要调整阈值的,比如KubeCPUOvercommit和KubeMemoryOvercommit,默认阈值在大型集群里很容易误报,我们一般会调整 or 调高。第三类是直接禁用的规则,比如KubeJobNotCompleted这类依赖具体 Job 语义的告警,不是所有集群的 Job 都要求在限定时间内完成,可以先把它们注释掉,观察一段时间再决定是否启用。
具体的操作方法有两种。一种是在安装 Chart 时通过 values.yaml 里的defaultRules.rules开关禁用掉某类规则,比如additionalPrometheusRules可以新增,但禁用默认规则需要在defaultRules下逐项关。另一种更灵活的方式,是直接编辑 PrometheusRule 这个 CRD 资源。注意规则文件是以 PrometheusRule 的方式存活的,执行kubectl -n monitoring get prometheusrules能看到所有规则组,用kubectl edit直接改就行。但这种方式改动后会被 Helm 在下次 upgrade 时覆盖回来,所以生产环境推荐把改动固化到 values.yaml 或单独的 rule 文件里。
下面给一个通过在 values.yaml 里自定义、追加规则的例子,把默认配置复制出来改成自己的阈值:
# custom-rules.yaml — 追加到 helm values 里 additionalPrometheusRules: - name: custom-k8s.rules groups: - name: k8s-node-alerts rules: - alert: NodeMemoryUsageHigh expr: | (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90 for: 5m labels: severity: warning annotations: summary: "节点 {{ $labels.instance }} 内存使用率超过 90%"这段规则文件定义了一个名为NodeMemoryUsageHigh的告警,表达式直接基于节点内存指标计算使用率,超过 90% 持续 5 分钟触发。注意这里的expr是多行字符串,Prometheus 的 PromQL 语法对空格敏感度没有 YAML 高,但要特别小心别把换行和缩进搞错,最常见的翻车现场是 YAML 语法校验过了,但 PromQL 粘贴后少了个括号。写完以后执行helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack -f values.yaml -f custom-rules.yaml --namespace monitoring,过一会儿在 Prometheus UI 的 Alerts 页面里就能看到新规则,有历史数据的话,规则状态会是 firing 或 pending。
4.2 常用的告警规则写法:PromQL 里那些容易忽略的边界
写 PromQL 告警规则这件事,比起 Python 脚本要更“玄学”一些,但大多数坑其实是有规律的。先说rate与irate的选择,这是最基础的坑。rate()计算的是区间内每秒平均增长率,比较平滑,适合阈值判断;irate()计算的是区间内最后两个样本的变化率,陡峭但对瞬时抖动极度敏感。告警规则里用rate()是主流选择,irate()更适合在 Grafana 面板里看趋势。如果在告警里用了irate(),你会发现一有毫秒级的 Pod 重启,告警就瞬间触发,然后又立刻恢复,非常闹心。
另一个容易忽略的边界是for这个关键字。它定义的是“条件持续多久才告警”——这个参数是防抖动的关键。很多人在写告警时只关注expr,把for: 0m,结果得到一个全是毛刺的告警流。我一般的习惯是:基础监控类(节点 CPU、内存)设for: 5m,业务相关类(接口失败率)设for: 1m或 2m,容忍短时抖动。还有labels里的severity一定要写上并保持取值规范,因为 alertmanager 的分组和路由会依赖这个标签,不写的话后面路由规则会非常难写。
还有一个变量引用问题,很多新手以为在告警消息里引用标签直接用$labels就行,但在 Prometheus 的规则配置里写注释或模板时,用的是{{ $labels.instance }}这种模板语法。如果你在 alertmanager 的配置里用模板引用,它跟 Prometheus 规则里的模板解析上下文是不同的。我把这个坑放出来是想提醒你:看到{{ $labels }}别奇怪,这是 Go template 的标准写法,不是写错。
4.3 Alertmanager 配置:把告警分组、抑制并把消息发到钉钉/飞书
Alertmanager 是整个告警链路的“路由器”。在 kube-prometheus-stack 里,Alertmanager 的配置主要通过 values.yaml 中的alertmanager.config字段来改。下面是一份适用于钉钉通知的简化配置,把 webhook 地址改成你自己的即可。
alertmanager: config: global: resolve_timeout: 5m route: group_by: ['alertname', 'cluster'] # 按告警名和集群分组,避免同一故障刷屏 group_wait: 10s # 组内首个告警等待 10s,聚合同一批触发的告警 group_interval: 5m # 组内新告警等待 5m 再次发送 repeat_interval: 4h # 同一告警重复通知的最小间隔 receiver: 'dingtalk' receivers: - name: 'dingtalk' webhook_configs: - url: 'http://dingtalk-webhook.example.com/v2/robot/send?access_token=your_token' send_resolved: true这段配置最重要的概念是route——它是树形结构的,根路由按group_by聚合告警,再分发给receiver。可以看到我这里把 repeat_interval 设成 4h,意思是同一个告警如果 4 小时内没有恢复,会再次通知一次,避免长时间没人处理时彻底沉默。send_resolved设为 true,会让恢复通知也发给钉钉,这看起来有点像“打扰”,实际上对于追究问题根因非常有帮助,至少你知道它什么时候恢复的。
钉钉机器人推送是当前国内用得最多的告警方式之一。它的要点是:机器人 webhook 地址里的 access_token 要保管好,任何人拿到这个 token 都能往你的群推送消息。如果你用的是飞书机器人,配置大体相同,只是 webhook 地址不同。需要发送邮件的话,在 global 里配smtp_smarthost和认证信息,receiver 换成email_configs即可。实践中,我建议告警通知渠道至少配置两条路,比如钉钉 + 邮件,防止单一渠道挂掉时完全错过告警。
4.4 告警数量失控的应急手段:临时静默与路由隔离
告警风暴是运维里最让人头大的事之一,特别是在大版本升级或集群节点批量重启时,一条基础告警能衍生出成百上千条关联告警。Alertmanager 提供了一个比较实用的功能叫 Silence,中文叫静默,它在 UI 里可以快速创建,指定匹配标签和时间范围,让符合标签的告警在这段时间内不通知。
另一个值得做的预防性手段是路由隔离。在 Alertmanager 的 route 树里,你可以按alertname或 namespace 把不同业务的告警分到不同的 receiver,比如 A 业务组的告警走钉钉群 A,B 业务组走邮件。这样某条链路抖动时,不会直接炸掉所有人的通知箱。用配置术语来说,就是 root route 下挂多个子路由,每个子路由有自己的match标签和receiver。这种设计在业务线较多的集群里价值很大,前提是你在写规则 labels 时不要偷懒,namespace和severity这两个标签一定要规范。
对于临时但比较紧急的告警噪音,还有一个更粗暴的办法是直接修改 PrometheusRule,把相关规则enabled置为 false。虽然不如 Silence 优雅,但更快、更直接。运行时改 CRD 会立即被 Operator 同步到 Prometheus,不需要 reload。需要注意的:记得事后恢复,因为这种应急操作容易遗忘,最好配套一段运维备注。
5. 避坑指南:部署与长期运行中的高频故障修复
5.1 Pod 反复 OOMKilled:Prometheus 内存被低估了
现象:部署后跑了两三天,发现prometheus-kube-prometheus-stack-prometheus-0这个 Pod 频繁处于 CrashLoopBackOff 状态,kubectl describe pod看到 Last State 是 OOMKilled。
原因:Prometheus 的内存占用与指标数量、保留时间、查询负载直接相关。kube-prometheus-stack 默认的资源 limits 里,memory 通常设成了 1Gi 或 2Gi,而一个活跃的中型集群(几十个节点、几百个 Pod)很容易在指标摄入高峰期冲破这个限制。另一个对内存影响很大的因素是storage.tsdb.retention.time,保留时间越长,内存里缓存的元数据越多。
解决:分两步走。先调大 limits,至少给到 4Gi,requests 保持 512Mi-1Gi 即可。再看一下 Prometheus 的 Web UI 里的 TSDB Status 页面,确认 block 数量和 Head 块大小,判断是不是指标量确实超出了预期。如果指标量本身就大,调内存只是拖延,更好的办法是减少采集频率、降低scrape_interval,或者把storage.tsdb.retention.size(注意这是按大小保留,不是按时间)设置得保守一点。长期运行中还要考虑给 Prometheus 单独的节点,避免和其他高内存应用抢资源。
提示:调整 Prometheus 资源直接改 values.yaml 里
prometheus.prometheusSpec.resources字段,然后 Helm upgrade。不要把 limits 设置得极小来“省”内存,Prometheus OOM 重启后丢失数据带来的代价远远大于内存成本。
5.2 Target 显示 DOWN:kubelet 指标采集失败的三种常见姿势
现象:Prometheus UI 的 Targets 页面里,kubelet相关的 target 状态是 DOWN,点击展开看到报错是server returned HTTP status 401 Unauthorized或连接超时。
原因:K8S 1.19 版本之后,kubelet 的/metrics端点默认绑定在 HTTPS 的 10250 端口,并且启用了认证授权。老版本的 kube-prometheus-stack 默认配置还在使用 HTTP 的 10255 端口,或者没有正确配置 bearer token,自然拿不到数据。还有一个很容易误踩的情况:metricsBindAddress被 K8S 节点上的 kubelet 配置改成了127.0.0.1:10250,这时候从 Prometheus 所在的节点根本访问不到。
解决:先确认 kubelet 监听地址,在节点上执行netstat -tlnp | grep 10250,确认端口是公开的还是绑在回环地址上。然后检查 Chart 版本,升级到较新的 kube-prometheus-stack 版本,新版本默认已经适配 HTTPS + 认证。如果升级后仍然 DOWN,检查 ServiceMonitor 的配置:kubectl -n monitoring describe prometheusserviceMonitor <name>,确认 tlsConfig 和 bearerTokenFile 字段是否正常。实在排查不出问题时,可以临时在 K8S 节点上把 kubelet 的authentication配置放宽测试一下,但千万不要在公网环境这么干。
5.3 Helm upgrade 后自定义配置丢失:CRD 与 values 的覆盖逻辑
现象:手工改了 PrometheusRule 或 ServiceMonitor,之后执行了一次helm upgrade(哪怕是只改了一个无关参数,比如 Grafana 的 adminPassword),发现问题全部回滚到了默认配置。
原因:Helm 在 upgrade 时会把 chart 里的资源与当前集群中的资源做 diff,凡是 chart 定义过的文件,如果集群里的被手工改过且与 chart 模板不一致,Helm 会用 chart 版本覆盖回去。这是预期行为,不是 bug,但坑了多少没把“配置变成代码”当回事的人。
解决:但凡想要长期生效的修改,一律抽象成 values.yaml 或独立的 YAML 文件,通过-f参数交给 Helm,而不是直接kubectl edit。如果你修改的是某个 CRD 资源,比如 ServiceMonitor,理想的做法是把它单独写成一个 YAML,用kubectl apply管理,并在 Helm 的 values 里关闭 chart 自带的对应配置(如果二者冲突)。另一种办法是使用 Helm 的--reset-values加--reuse-values等参数组合,但这对配置管理习惯的要求更高,我一般还是建议做好代码化。
5.4 告警发不出去却毫无报错:Alertmanager 路由全空的排查思路
现象:Prometheus Alerts 页面里规则状态为 firing,但钉钉和邮箱都没收到任何消息,Alertmanager UI 的 Alerts 页面里也看不到这个告警。
原因:这个现象通常是 Prometheus 把告警推给 Alertmanager 失败,或者是 Alertmanager 收到后没有匹配到任何 receiver。先在 Prometheus UI 的 Alerts 页面里,找到对应规则,点击右侧的“...”展开查看ActiveSince和状态详情,确认它确实在向 Alertmanager 推送。然后去 Alertmanager 的 Alerts 页面,如果 Prometheus 推送成功,这里会看到同一条告警。如果 Prometheus 侧已经推送而 Alertmanager 没有,通常需要检查 Prometheus 配置中alerting.alertmanagers的地址是否正确;如果 Alertmanager 有告警但不通知,再看它的配置里 receiver 是否为空、路由是否有continue陷阱。
排查这个问题的经验是分三截查:Prometheus 推了没有(看 Prometheus 日志,关键词alertmanager)、Alertmanager 收了没有(看 UI)、Alertmanager 发出去了没有(看目标系统有没有请求记录)。按这个链路逐段排查,大部分问题都会自动浮出来。
5.5 node-exporter 端口冲突:监听 9100 被占用的另一种情况
现象:node-exporter 的 DaemonSet Pod 状态 Running,但 Prometheus 采集这个 target 时报 connection refused 或 timeout。
原因:这是个比较少见的场景——node-exporter 监听 9100 端口,而这台节点上部署了其他服务也占用了 9100,导致 node-exporter 绑定失败但进程没有退出。还有一个常见变体:节点上有旧版的 node-exporter 仍在运行,它是裸进程方式起的,而 K8S 的新 Pod 无法绑定到同一端口。
解决:先在节点上执行ss -tlnp | grep 9100看是谁占用了端口,确认是进程占用后,把这进程停掉,然后重启 node-exporter 的 Pod。如果不想费力排查占用者,也可以在 DaemonSet 的 values 里直接把 node-exporter 的端口改掉,比如nodeExporter: service: port: 9101,但要注意 Prometheus 的 ServiceMonitor 配置也要同步改端口,否则该 target 的采集路径就断了。这个坑不常见,但按“端口占用先确认进程再动配置”的思路排查,通常十分钟内能解决。
6. 进阶摸索:把资源合集沉淀成团队可复用的监控模板
走到这一步,kube-prometheus-stack 对你的集群来说已经是一个能稳定运行的监控底座了。但“能跑”和“好用”之间还差一段距离,这段距离主要是通过模板化来缩短的。我的做法是:把部署用到的 values.yaml、自定义的 PrometheusRule、Alertmanager 配置以及配套的文档放在同一个 Git 仓库里,每个集群一个环境目录,目录下区分 base 和 overlay。当新集群需要接入监控时,直接从 base 复制一份,改几个参数(集群名、告警接收人、存储大小)就执行 Helm,十分钟内完成一套新集群的监控部署,而不是从零开始跑一遍。
另外一个值得投入的方向是服务发现边界的扩展。kube-prometheus-stack 对 K8S 原生工作负载的发现已经做得很完整,但外部系统(数据库、中间件、自研服务)的指标接入还是需要自己写 ServiceMonitor。我的建议是先统一规定业务服务的指标暴露规范,例如在 Pod 的 annotations 里约定prometheus.io/scrape: "true",然后写一个通用的 ServiceMonitor 模板,通过 namespace 选择器自动匹配。模板化的好处是业务方不需要理解 ServiceMonitor 的细节,只要在部署服务时带上固定的 annotations,监控就自动生效,这个思路能极大降低推广监控体系的沟通成本。
验证监控告警体系是否真的有效,我习惯做一次“混沌演练”:人为停掉一个节点上的 kubelet、把一个 Deployment 的副本数降到 0、或给节点制造一点 CPU 压力,然后看告警是否能按预期在几分钟内到达通知渠道。这种演练至少每月做一次,特别是当 K8S 版本或 Chart 版本升级后,很多“看起来正常”的配置可能已经悄悄失效了,只有真实故障才能验出问题。我个人的教训是,任何一次大版本升级后都要重新跑一遍这个演练,别等到生产环境真出故障时才发现告警是哑的。
如果你准备把这个方案分享给团队,把标题里的“资源合集”落到实际交付物上,可以做一个简单的三层清单:第一层是部署物料,包括 values.yaml、告警规则模板、Alertmanager 配置;第二层是操作手册,包括扩容、缩容、升级、回滚的具体命令和步骤;第三层是故障响应手册,包括常见告警的含义、排查路径和值班处理建议。有了这套东西,哪怕团队里没接触过 Prometheus 的人,也可以按手册处理第一次生产告警。每次故障处理完,把根因和解法追加进手册,半年之后它就是你们团队靠踩坑换来的最值钱的监控运维资产。希望这些整理下来的经验能帮到你,少走几条弯路。
本文还有配套的精品资源,点击获取