K8s可观测性实践:kube-prometheus-stack部署与告警配置全指南
简介在云原生场景中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 定义告警规则做阈值判断触发后发给 AlertmanagerAlertmanager 再负责去重、分组和通过 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 或 PrometheusRuleOperator 会自动把变更滚到 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如果集群里没有 StorageClassPVC 会一直 PendingPrometheus 的 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 的逻辑比较简单测试环境不持久化 GrafanaPrometheus 用 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 会经常在半夜把值班人吵醒一堆毫无区分度的告警涌过来最后变成“狼来了”——真正重要的告警反被淹没。我建议的默认规则梳理思路是这样的先搞清楚自己的集群规模和生产环境等级然后分三类处理。第一类是必须保留的高价值规则比如KubePodCrashLoopingPod 反复崩溃重启、KubeNodeNotReady节点不可用、KubeAPIHighErrorRateAPI Server 错误率高、KubePersistentVolumeFillingUpPVC 快被写满。第二类是需要调整阈值的比如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_tokenyour_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 业务组的告警走钉钉群 AB 业务组走邮件。这样某条链路抖动时不会直接炸掉所有人的通知箱。用配置术语来说就是 root route 下挂多个子路由每个子路由有自己的match标签和receiver。这种设计在业务线较多的集群里价值很大前提是你在写规则 labels 时不要偷懒namespace和severity这两个标签一定要规范。对于临时但比较紧急的告警噪音还有一个更粗暴的办法是直接修改 PrometheusRule把相关规则enabled置为 false。虽然不如 Silence 优雅但更快、更直接。运行时改 CRD 会立即被 Operator 同步到 Prometheus不需要 reload。需要注意的记得事后恢复因为这种应急操作容易遗忘最好配套一段运维备注。5. 避坑指南部署与长期运行中的高频故障修复5.1 Pod 反复 OOMKilledPrometheus 内存被低估了现象部署后跑了两三天发现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至少给到 4Girequests 保持 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 显示 DOWNkubelet 指标采集失败的三种常见姿势现象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 的人也可以按手册处理第一次生产告警。每次故障处理完把根因和解法追加进手册半年之后它就是你们团队靠踩坑换来的最值钱的监控运维资产。希望这些整理下来的经验能帮到你少走几条弯路。本文还有配套的精品资源点击获取