K8s监控实战:Prometheus+Grafana从部署到告警的完整指南 监控K8s和监控传统虚拟机完全是两码事。你在虚拟机时代写死一组目标IP配好账号密码定期拉数据就完事了。到了K8s里Pod频繁漂移、节点自动扩缩容、探针重试、滚动发布监控对象的生命周期可能只有几分钟。我第一次给K8s集群上监控时用旧思路手动加了一堆静态采集端点结果发布一次服务全乱套Prometheus targets里一片红排错排到怀疑人生。后来我彻底换成了 Prometheus Grafana 这套组合基于服务发现自动感知Pod和节点变化配合kube-state-metrics、node_exporter这类辅助组件把K8s集群从基础设施层、平台层到工作负载层的指标全部收拢起来再通过Grafana做可视化Alertmanager做告警通知。这篇文章把整套部署过程、关键原理、PromQL常用查询以及我在实战中踩过的坑完整记录下来适合准备给K8s上监控的运维、开发和架构师直接参考。1. 部署前必须想清楚的K8s监控到底要解决什么问题1.1 动态环境让传统监控手段失效传统监控的思路是静态目标先列一批机器IP和服务端口监控系统主动去连或者装个Agent上去采集目标列表基本固定。K8s打破了这套逻辑一个Deployment从2个副本扩到20个副本Pod IP完全随机分配滚动更新时旧的Pod销毁、新的Pod拉起你怎么维护监控对接关系这也是当初我在规划K8s监控方案时最头疼的点。靠人工维护列表不现实靠脚本同步节点信息也容易滞后真正可行的是让监控系统自己去发现目标。Prometheus的K8s服务发现机制就是干这个的通过访问K8s API实时获取Service、Pod、Endpoint的变化动态调整采集目标。Pod销毁了数据点自动消失新Pod创建了秒级出现在采集列表里。这个机制才是适配容器环境的核心而不只是某个采集组件换了个名字。所以无论用什么监控系统在K8s里最关键的能力是能不能跟上资源对象的生命周期变化而不是靠人肉维护拓扑。1.2 从三个层次拆解监控目标在动手部署前最好把监控目标分成三个层次每个层次关心的指标完全不同后续的采集配置、仪表盘设计、告警阈值都会跟着分层走。我在文档里习惯用下面这张表帮团队对齐需求层次监控对象核心指标示例数据来源基础设施层节点CPU、内存、磁盘、网络node_cpu_seconds_total、node_memory_MemAvailable_bytes、node_filesystem_avail_bytesnode_exporterK8s平台层API Server、etcd、Scheduler、Controller Managerapiserver_request_duration_seconds_bucket、etcd_server_leader_steady_state_totalskube-apiserver、etcd自身的/metrics端点工作负载层Deployment、Pod、容器、HPAkube_pod_container_status_restarts_total、kube_deployment_status_replicas_availablekube-state-metrics这三层监控缺一不可。只盯节点不盯Pod容器频繁重启你是看不出来的只盯工作负载不盯平台层某次故障很可能根源在API Server响应变慢而你抓到的只是表象。我当时就把这套分层逻辑画成墙上的图团队开会讨论时一对照需求很快就清楚了。1.3 为什么是PrometheusGrafana这个组合市面上的监控方案不少但K8s生态里绕不开Prometheus因为它是云原生计算基金会孵化的项目K8s自身就把/metrics端点作为指标暴露的标准方式几乎所有云原生组件都自带Prometheus格式的指标端点。它的拉取式采集模型配合服务发现机制天然适配动态环境PromQL查询语言支持多维数据模型按标签聚合、筛选、计算都很方便再加上Alertmanager的告警路由、分组、静默能力整条链路是完整的。Grafana的优势不光是图表好看它对数据源做了抽象层一套面板可以对接Prometheus、Loki、Elasticsearch、CloudWatch等多种数据源。这意味着你以后加了日志平台或者链路追踪可以在同一个看板里把指标、日志、链路关联起来不用反复切换系统。而且Grafana社区有海量现成Dashboard模板导进来改改数据源就能用上手成本很低。这套组合给我的感觉是Prometheus负责把数据收得全、算得准Grafana负责把数据看得懂、看得爽。分工明确不越界这也正是它能成为K8s监控事实标准的原因。2. 组件分工与数据流先搞清楚谁在干什么再动手2.1 Prometheus时间序列数据库加采集调度器Prometheus不是一个简单的采集器它是一个带有时间序列数据库TSDB的完整监控系统。它通过HTTP轮询方式抓取各目标暴露的指标并将指标数据按时间维度存储随后通过PromQL对外提供查询能力。在K8s环境中它通过内置的Kubernetes服务发现机制实时Watch API Server中的资源变化动态生成采集目标列表。理解Prometheus的关键在于三个词抓取、存储、查询。抓取是它的采集循环按照scrape_interval配置的时间间隔去拉取暴露的指标存储是TSDB在本地落盘默认保留15天数据查询是接受PromQL请求从存储中计算结果。这三个环节只要有一个出问题整个监控链路就会出状况。2.2 Grafana只负责展示不负责采集很多刚接触的人会搞混Prometheus和Grafana的职责边界以为Grafana是负责采集的。其实Grafana只是一个可视化平台它通过配置数据源地址向Prometheus发起查询请求然后把结果渲染成图表面板。Prometheus不采集的话Grafana画不出任何东西。这意味着排错顺序很重要。我一般碰到大盘没数据先看Prometheus里能不能查到指标再回头看Grafana配置。在Prometheus查询页面里输入一条PromQL如果有数据问题一定出在Grafana侧的数据源配置、时间范围选择或变量设置上如果Prometheus里本来就查不到数据就得从采集链路排查。2.3 采集链路全览从kubelet到GrafanaK8s监控数据流可以画成一条清晰链路节点上的node_exporter暴露节点指标kubelet内嵌的cAdvisor暴露容器指标kube-state-metrics从API Server聚合资源对象状态指标这三类数据源被Prometheus以服务发现方式采集Prometheus将指标写入内置TSDB后一方面通过PromQL响应Grafana的查询请求另一方面通过告警规则评估定时触发Alertmanager。链路里最容易忽略的是kubelet的指标端点。K8s的每个节点上运行着kubelet它内置了cAdvisor来采集本节点所有容器的CPU、内存、磁盘、网络指标通过/metrics/cadvisor端点暴露。这是Pod和容器级指标最主要的来源不需要额外部署Agent。这也是Prometheus采用拉取模式的好处K8s已经把这些指标准备好了监控系统只需要知道去哪个端点拿就行。2.4 辅助组件的角色定位除了Prometheus和Grafana实际部署中还需要三个重要辅助组件每个组件承担的角色不同不能混用node_exporter负责采集节点级别的硬件指标包括CPU、内存、磁盘、文件系统、网络流量。它运行在每个节点上以DaemonSet方式部署采集到的指标都带instance标签用于标识节点IP。kube-state-metrics负责将K8s资源对象的状态转换成指标例如期望副本数、可用副本数、Pod重启次数、Pod状态、资源请求量等。它不采集日志不采集节点指标只关注资源对象的期望状态和实际状态。需要注意的是如果你关心的是Deployment有几个副本可用这类问题kube-state-metrics是不可替代的数据源。第三个是metrics-server它轻量采集节点和Pod的CPU、内存实时用量为kubectl top和HPA提供数据。它和上面两个组件并不冲突metrics-server的数据是聚合API方式提供的Prometheus一般不直接采集它而是绕过去使用更底层的指标源。搞清楚这三个组件的分工部署时就不会装重复或者漏装。3. 从零到一部署Prometheus监控栈3.1 前置条件与环境规划部署前先把环境确认一遍省得中途返工。K8s集群版本建议1.26以上Helm版本3.xPrometheus Operator对K8s版本有兼容性要求版本太老会有各种API不兼容问题。存储方面Prometheus默认用emptyDir存数据Pod一重启数据全没生产环境必须挂持久卷。我一般建议提前确认集群里的StorageClass使用块存储或者分布式存储都可以磁盘性能直接决定PromQL查询的响应速度。名字空间我统一用monitoring也可以按团队习惯调整但所有组件放同一个命名空间便于权限管控和资源隔离。另外建议在部署前确认集群是否有足够的资源余量kube-prometheus-stack默认会部署Prometheus、Alertmanager、Grafana、kube-state-metrics、node_exporter等组件至少预留2核CPU和4GB内存否则部署到小规格集群容易把节点压垮。3.2 Helm安装kube-prometheus-stack操作步骤这是我在生产环境用得最多的方式。kube-prometheus-stack是专门为K8s监控栈打好的集成包内置了Prometheus Operator、Prometheus实例、Alertmanager、Grafana、kube-state-metrics、node_exporter以及一系列预置的告警规则和Dashboard。相比一个个组件手动装Helm方式能极大减少配置工作量。先添加Helm仓库并拉取最新版本helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm search repo prometheus-community/kube-prometheus-stack然后创建一个values文件按需覆盖默认配置。下面是一个我在生产环境常用的最小化配置片段grafana: adminPassword: your-strong-password persistence: enabled: true storageClassName: your-storage-class size: 5Gi service: type: LoadBalancer prometheus: prometheusSpec: retention: 15d storageSpec: volumeClaimTemplate: spec: storageClassName: your-storage-class accessModes: [ReadWriteOnce] resources: requests: storage: 50Gi resources: requests: cpu: 500m memory: 2Gi limits: cpu: 1 memory: 4Gi alertmanager: alertmanagerSpec: storage: volumeClaimTemplate: spec: storageClassName: your-storage-class accessModes: [ReadWriteOnce] resources: requests: storage: 10Gi然后执行安装helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack -n monitoring -f values.yaml等待Pod全部就绪kubectl get pods -n monitoring -w正常情况下会看到prometheus-operator、prometheus、alertmanager、grafana、kube-state-metrics以及每个节点上的node-exporter类似的Pod。这些Pod全部Running之后基础监控栈就起来了。3.3 关键参数解析为什么这样配置Grafana的adminPassword必须设成强密码因为默认账号admin权限极高能直接看到集群所有监控数据和告警配置暴露在公网容易被爆破。Grafana的persistence我同样建议开启否则你辛辛苦苦配好的面板、数据源、用户Pod重启后全丢。storageClassName根据实际环境填前提是集群里有默认的存储类。Prometheus的retention表示数据保留天数15天是一个平衡点太长对磁盘空间要求高太短做长时间范围的趋势分析会查不到历史数据。storageSpec配置了持久卷声明模板这是Prometheus Operator的典型用法它会为每个Prometheus副本自动创建PVC比手动绑定PV更灵活。Alertmanager的存储用于保存告警抑制和静默状态生产环境建议持久化否则重启后静默规则丢失可能引发重复告警。资源requests和limits是为了防止监控组件抢占集群资源尤其当监控目标数量很大时Prometheus内存占用会明显上升设limit可以避免它拖垮节点。3.4 部署后必须完成的验证动作组件起来了不代表监控链路是通的。我用三分钟做三件事验证第一检查Prometheus Targets。通过kubectl port-forward把Prometheus服务暴露出来访问Targets页面状态应该是全部UP至少能看到kube-apiserver、kubelet、node-exporter、kube-state-metrics这些采集目标。kubectl get svc -n monitoring kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090:9090浏览器打开http://localhost:9090/targets这里记录着所有采集目标的状态和最近一次抓取时间。如果某个目标显示Down点进去看错误信息一般是证书问题、RBAC权限问题或者网络不通。第二验证PromQL基本查询。在Prometheus自带的查询页面执行以下语句确保有数据返回up正常情况下会返回一堆指标数据后面跟着1代表各目标在线。再试试node_cpu_seconds_total有数据说明node_exporter的采集链路没问题。第三验证Grafana登录。同样用port-forward暴露Grafana服务使用设置的admin密码登录默认数据源和预置Dashboard应该已经存在。找一个现成的Kubernetes集群视图的Dashboard看是否出图。这一步能确认Prometheus和Grafana之间的数据查询链路是通的。4. Grafana数据源接入与仪表盘实战4.1 数据源的两种接入方式kube-prometheus-stack安装完成后Grafana会自动配置好Prometheus数据源因为Operator在初始化时就把数据源地址写进去了。但如果你是基于已有Grafana实例接入新的Prometheus就需要手动添加数据源。在Grafana左侧菜单进入Configuration下的Data Sources页面点击Add data source选择Prometheus把URL填成Prometheus服务的访问地址例如http://kube-prometheus-stack-prometheus.monitoring.svc:9090点击Save Test看到Success说明连接成功。这里有个细节容易踩坑如果Grafana和Prometheus在同一K8s集群内URL用Service的ClusterIP或DNS名称是最稳的走内网不需要经过负载均衡如果Grafana在集群外就得把Prometheus通过Ingress或NodePort暴露出来并配置可访问的地址。4.2 导入现成Dashboard与手工搭建核心看板Grafana社区有很多现成的K8s Dashboard模板我常用的是Kubernetes Cluster维度和Kubernetes Node维度模板ID在Grafana官网可以搜到市场上也能直接搜到。导入方式是在Dashboards页面点击Import输入模板ID选择之前配置好的Prometheus数据源即可。这类模板的PromQL表达式非常全面覆盖CPU、内存、网络、文件系统、Pod状态等维度基本满足日常巡检需求。不过我的建议是别完全依赖现成模板至少自己搭一张核心总览看板这样你能理解每个面板背后的PromQL逻辑一旦出问题也知道去哪查。核心看板我一般放四五个面板集群节点数、节点CPU使用率Top、内存使用率Top、Pod状态分布、容器重启次数Top。每个面板对应一个Panel类型和一条PromQL查询。4.3 日常用得最多的PromQL查询这里把我在日常运维中高频使用的PromQL整理成表供参考场景PromQL表达式节点CPU使用率100 - (avg(rate(node_cpu_seconds_total{modeidle}[5m])) by (instance) * 100)节点内存使用率100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100)节点磁盘使用率(1 - node_filesystem_avail_bytes{fstype!~tmpfsPod重启次数变化changes(kube_pod_container_status_restarts_total[5m])Deployment可用副本数kube_deployment_status_replicas_available / kube_deployment_spec_replicasAPI Server平均延迟rate(apiserver_request_duration_seconds_sum[5m]) / rate(apiserver_request_duration_seconds_count[5m])这些查询的价值不仅在看板排查问题时直接在Prometheus里执行能快速定位问题。比如某个服务发布后大量报错先查kube_pod_container_status_restarts_total确认是不是容器反复重启再看节点CPU使用率排除资源竞争。4.4 变量、权限与看板组织Grafana面板支持模板变量可以把PromQL中的标签值动态化。我在做多集群纳管时经常在Dashboard里定义一个cluster变量取值范围来自label_values(up, cluster)然后用$cluster做查询过滤这样一张Dashboard就能在多个集群之间切换。变量是Grafana用起来很顺手的功能建议熟练掌握。如果团队内有多人查看监控建议启用Grafana的组织和权限控制。默认org所有人共享容易误操作修改面板。可以在Server Admin里创建独立组织或者给不同用户分配Viewer角色只给少数管理员Editor权限。监控是给全团队看的但改动权限应该收敛否则哪天有人不小心改了个PromQL整块看板就成了错的。5. 告警规则与通知链路让监控从看板变成值班5.1 告警规则的本质与工作机制告警不是Prometheus凭空产生的它本质上是一条PromQL布尔表达式当查询结果满足某个条件时触发告警。Prometheus会持续评估规则中定义的表达式如果条件的持续时长超过了for字段设置的时间窗口就标记为Firing并通过Alertmanager发送通知。这里有一个关键概念叫告警疲劳的消除for字段的意义是防止瞬时抖动误报。比如节点CPU瞬时冲到90%可能只是某个任务高峰期并不是故障设置for: 10m意思是持续10分钟才触发告警这能过滤掉大量误报。我在告警配置上花的时间比部署本身还多因为规则设置得好不好直接决定值班团队会不会被噪音淹没。5.2 内置规则与自定义规则写法kube-prometheus-stack自带了一套齐全的告警规则部署后Prometheus会加载这些规则主要包括集群节点异常、K8s资源状态异常、etcd异常等类别。以Pod频繁重启为例内置规则覆盖了很多常见场景比如死锁无法启动、异常状态持续过久等。实际业务场景往往需要自定义规则。下面是一个Deployment可用副本数不足的告警规则示例groups: - name: deployment-availability rules: - alert: DeploymentReplicasUnavailable expr: kube_deployment_status_replicas_available / kube_deployment_spec_replicas 1 for: 5m labels: severity: critical annotations: summary: 工作负载副本不足 description: Deployment {{ $labels.namespace }}/{{ $labels.deployment }} 可用副本数低于期望值当前可用副本数为 {{ $value }}这类规则通过kube-state-metrics提供的指标判断工作负载健康状况。我通常会为无状态服务配置副本数期望值校验一旦可用副本不等于预期立刻通知到人这是防止服务雪崩的第一道防线。5.3 Alertmanager路由与接收器配置Alertmanager收到Prometheus推送的告警后按照路由树决定发送给谁、用什么方式通知。路由配置的核心是分组和匹配。分组逻辑用group_by字段控制比如按告警名称分组可以将同一时间段内重复的同类告警合并为一条。这在节点故障时尤其有用一个节点挂了可能连带很多Pod都有问题不分组的话通知渠道会被刷爆。接收器支持邮件、Webhook、钉钉、企业微信等通道。下面是一个把高等级告警发到Webhook、普通告警发到邮件的最小路由配置route: group_by: [alertname, severity] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: default routes: - matchers: - severity critical receiver: critical-webhook receivers: - name: default email_configs: - to: ops-teamexample.com send_resolved: true - name: critical-webhook webhook_configs: - url: http://notify-service.monitoring.svc:8080/alertrepeat_interval控制相同告警的重复通知间隔设置4小时是比较稳妥的默认值既能持续提醒又不会每隔几分钟刷屏。send_resolved: true表示告警恢复时也要通知这样团队知道问题已经解除不用反复确认。5.4 告警去重、静默与值班运营技巧告警规则上线后第一个要紧的事是建一个静默机制。当执行运维变更时比如滚动升级节点你明明知道Pod会短暂不可用如果不去静默值班人员会收到一堆假告警。Alertmanager的Silence功能可以提前设置静默时间窗口按标签匹配要屏蔽的告警到期自动解除。我在这里的实践是每次重大变更前先在Alertmanager里创建静默匹配本次变更涉及的节点或命名空间变更结束确认无误后再手动解禁。这个习惯极大减少了告警疲劳。另外告警恢复通知也非常重要我见过太多团队只配了触发通知没配恢复通知导致值班人员看到告警后不知道是否已恢复只能再去查看板确认效率很低。建议把send_resolved全程开启。6. K8s监控实战中的坑与排查思路6.1 Targets一片红的完整排查链路部署完监控后最常出现的问题就是Prometheus Targets里大量目标显示Down。我遇到过一次node-exporter的Targets全红并且Targets页面上看到错误信息是tls handshake timeout。我先检查了node-exporter Pod的日志确认服务本身正常端口也在监听说明问题出在Prometheus到节点的网络链路或者证书上。继续排查发现集群里部分节点是通过一组中转节点接入的节点之间的网络策略没有放行10250端口导致Prometheus无法访问kubelet的HTTPS接口。我用kubectl get networkpolicy查看了命名空间策略发现monitoring命名空间被拒绝访问别的命名空间。解决方案是在NetworkPolicy里放行Prometheus对10250和9100端口的出站访问。这类问题在K8s环境里特别常见尤其是启用了严格网络策略的集群排错顺序建议是先看Targets页面的错误信息再查目标组件Pod是否正常接着查网络连通性最后检查RBAC。6.2 存储暴涨与高基数指标失控Prometheus TSDB的存储膨胀通常有两个原因数据保留时间太长和指标基数太大。前者好解决调整retention即可后者是隐蔽的坑。高基数意味着同一指标名下出现了海量标签组合例如把用户ID放在标签里每次查询都要扫描大量时间序列内存占用和磁盘增长速度完全是爆炸式的。我在某个项目上见过一次内存持续攀升排查后发现是某个业务指标把请求路径中的参数全部打成了标签一小时内产生了几百万条时间序列最终把Prometheus OOM。处理方法是设计promtool检查指标基数并在采集配置里对高基数指标做drop操作把URL参数、随机ID这类高离散值的标签去掉。经验法则很简单标签值的高基数通常是性能灾难的来源。另外有条件的建议配置远程存储把Prometheus的TSDB数据远程写出一份到长期存储这样本地保留周期可以缩短不用在磁盘和查询性能之间找平衡。6.3 Grafana图表空白的定位思路Grafana面板加载后一片空白这是排查链路中最容易绕弯路的问题。我的定位思路是分三段先在大盘面板的Query编辑器里直接看查询返回结果如果显示No data就把同样的PromQL拿到Prometheus里执行一遍。如果Prometheus能查到数据问题就出在参数上最常见的是$__rate_interval这种Grafana内置变量它在5m速率计算里有时会因为时间范围选择太小而返回空数组。如果是变量导致的空数据检查Dashboard的变量定义是否正确尤其是用label_values查询标签值时数据源选择是否指向了正确的Prometheus。还有一种很低级但很常见的坑时间范围越界。在Grafana右上角选了最近1小时而Prometheus数据保留时间较短或集群刚重启过时间范围内的数据确实不存在。这时候把时间范围拉大到7天再试一次基本就能区分是数据不存在还是查询逻辑错误。6.4 告警延迟与通知丢失Alertmanager的告警延迟一般出在路由配置上。我记得有一次发现告警要过5分钟才收到通知后来查配置发现group_wait设置的是5分钟它的作用是等同一分组的告警聚齐后再统一发送所以会有明显延迟。高优先级告警我建议单独路由分支设置较短的group_wait比如30秒。通知丢失需要检查三个点Prometheus到Alertmanager的推送是否成功在Prometheus的Alerts页面里能看到告警是否被选中Alertmanager的Receivers页面能看到通知发送结果和错误消息接收器本身是否配置正确比如Webhook URL访问不到或者邮件服务被限流。还有一种很容易忽略的情况Alertmanager自身的持久化存储如果丢失静默规则就会消失已被屏蔽的告警会突然全部发出来造成通知风暴。这也是前面为什么建议给Alertmanager配置持久卷的原因。运营监控系统很多时候不是功能缺失而是细节没配好告警链路尤其如此。最后说说我这几轮踩坑下来的实际体会监控系统部署起来并不难难的是把它运营好。我在给多个K8s集群部署Prometheus和Grafana后最大的感受是这套监控栈的稳定性和你的配置习惯强相关。先把分层指标梳理清楚再动手部署比装完再补数据源要高效得多告警规则要克制宁可少配几条精准的也不要堆一堆误报率高的规则值班团队对告警的信任一旦崩塌再重要的问题都会被忽略存储和资源配额要提前规划后期扩容的成本远比前期预估高。还有一个小技巧把PromQL查询沉淀成本团队的共享文档常用查询分类归档新同事接手监控时直接抄作业能少走很多弯路。监控是持续迭代的过程随着集群规模增长和业务形态变化指标维度和告警策略都需要反复调整。希望这篇文档能帮你把第一套K8s监控栈顺利落地。