Kubernetes容器监控实战指南PrometheusGrafana搭建生产级集群性能可视化与告警系统
为什么你要关心这件事
想象一下,你有一整个城市的供水系统在运行——管道纵横交错,水泵日夜不停,阀门随时可能出问题。如果没有任何监控,你不知道哪里漏水、哪里水压不足、哪里快要爆管。Kubernetes集群就是这座城市,容器就是水管,Prometheus和Grafana就是你安装在水网各个节点上的传感器和显示屏。
没做监控的时候,我亲眼见过一个生产集群,因为一个Pod内存泄漏,慢慢撑爆了节点,导致整个业务中断。运维团队花了两个小时才定位问题,而用户已经流失了一大片。从那以后我就明白了一个道理:监控不是可选项,是生存线。
今天这篇文章,我会带着你从零开始,搭建一套生产级的Prometheus+Grafana监控体系。不是那种跑在笔记本上玩的玩具,而是能扛住真实业务压力的系统。
前置知识:你需要什么
在动手之前,我们先搞清楚手边有哪些工具:
Kubernetes集群
- 至少v1.20以上版本
- 集群要正常运行,有Node Exporter权限(通常是ClusterRole)
helm
- 用于快速部署Prometheus Operator和Grafana
- 版本3.0+
kubectl
- 用来操作K8s资源
浏览器
- 用于访问Grafana界面
第一部分:Prometheus Operator —— 监控的大脑
什么是Prometheus Operator
普通的Prometheus需要你自己手动配置 scrape configs、alerting rules、recording rules。在Kubernetes集群里,Pod的IP地址是动态变化的,手动维护这些配置几乎是不可能的任务。
Prometheus Operator解决了这个问题。它像一个”元控制器”,你只需要告诉它”我想要监控这个命名空间”,它就会自动发现Pod、Service、Endpoint,生成正确的配置,然后去拉取指标。
简单说:你只管定义目标,Operator负责发现和执行。
第一步:安装Prometheus Operator
我们用Helm来安装,这是最干净的方式。
# 添加Prometheus社区Helm仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 创建监控命名空间
kubectl create namespace monitoring
# 安装Prometheus Operator
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--set prometheus.prometheusSpec.retention=30d \
--set grafana.persistence.enabled=true \
--set grafana.persistence.size=10Gi
让我解释一下这几个关键参数:
- retention=30d:数据保留30天。生产环境我建议至少7天,数据量大的话30天很合理
- grafana.persistence.enabled=true:给Grafana开启持久化存储,避免重启后仪表盘丢失
- grafana.persistence.size=10Gi:10GB足够存放仪表盘配置和少量dashboard JSON文件
查看安装结果
# 查看Pod是否全部运行
kubectl get pods -n monitoring
# 你应该看到类似这样的输出:
# NAME READY STATUS RESTARTS AGE
# prometheus-kube-prometheus-operator-xxxx 1/1 Running 0 5m
# prometheus-kube-prometheus-prometheus-0 2/2 Running 0 5m
# prometheus-grafana-xxxxx 3/3 Running 0 5m
# prometheus-kube-state-metrics-xxxx 1/1 Running 0 5m
# prometheus-prometheus-node-exporter-xxxx 1/1 Running 0 5m
如果所有Pod都显示Running状态,说明安装成功。
第二部分:配置ServiceMonitor —— 告诉Prometheus监控什么
什么是ServiceMonitor
ServiceMonitor是Prometheus Operator提供的一种自定义资源(CRD)。它的作用是声明”我要监控这个Service关联的所有Pod”。
举个例子:假设你部署了一个Nginx服务,你想监控它的QPS、延迟、错误率。你不需要去改Prometheus的配置文件,只需要创建一个ServiceMonitor,Prometheus Operator会自动帮你完成发现、抓取、路由配置。
实际案例:监控一个Web应用
假设你有一个电商应用,部署在production命名空间下。
# webapp-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: webapp-monitor
namespace: production
labels:
app: webapp
team: ecommerce
spec:
selector:
matchLabels:
app: webapp
namespaceSelector:
matchNames:
- production
endpoints:
- port: metrics
interval: 15s
path: /metrics
scrapeTimeout: 10s
关键参数说明:
- selector.matchLabels:匹配带有
app: webapp标签的Service - namespaceSelector:指定要监控哪些命名空间
- port:Service中暴露metrics的端口名称
- interval:每15秒拉取一次指标,生产环境建议10-30秒
- scrapeTimeout:超时时间,建议设置为interval的2/3
部署ServiceMonitor
kubectl apply -f webapp-servicemonitor.yaml
部署后,你可以在Prometheus的Web界面里验证:
http://<prometheus-service>:9090/targets
你应该能看到新添加的target,状态为UP,说明Prometheus已经成功开始抓取这个应用的指标了。
第三部分:Grafana仪表盘 —— 让数据看得见
为什么需要Grafana
Prometheus有自己的Web界面,也能看一些基本数据。但它的图表功能非常简陋,而且不支持告警规则管理。Grafana是专门用来做可视化的,它支持多种数据源,图表类型丰富,交互体验好。
导入官方仪表盘
Prometheus Operator安装后,Grafana已经内置了。你不需要额外安装。
进入Grafana的方式:
# 端口转发,本地访问
kubectl port-forward -n monitoring svc/prometheus-grafana 3000:80
# 然后浏览器打开 http://localhost:3000
默认用户名和密码:
用户名: admin
密码: 在secret里找
kubectl get secret -n monitoring prometheus-grafana -o jsonpath="{.data.admin-password}" | base64 -d
推荐的仪表盘
这里有几个在生产环境验证过的仪表盘模板,我从社区整理出来的:
1. Kubernetes集群总览(Kubernetes Cluster Monitoring)
- Prometheus ID: 315
- 查看CPU、内存、磁盘、网络的总体使用情况
- 适合给老板看的大屏
2. Pod监控仪表盘
- Prometheus ID: 13105
- 展示每个Pod的CPU、内存、网络、文件系统指标
- 运维人员日常排查问题的利器
3. 应用性能监控(应用层)
- Prometheus ID: 11829
- 针对具体应用的延迟、错误率、吞吐量
- SRE团队必备
导入方法很简单:点击Grafana左侧菜单的”+” → Import → 输入Prometheus ID → 选择数据源 → Import。
第四部分:告警系统 —— 问题发生前先知道
什么是Prometheus Alertmanager
Prometheus负责收集指标和评估告警规则,但真正发送告警通知的是Alertmanager。两者配合工作:
Prometheus(评估规则) → Alertmanager(发送通知) → 钉钉/飞书/企业微信/Slack
配置告警规则
Prometheus Operator允许你通过PrometheusRule资源来定义告警规则,和Kubernetes资源一样管理。
# alert-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: k8s-alerts
namespace: monitoring
labels:
team: sre
spec:
groups:
- name: kubernetes
rules:
# Pod重启次数过多
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 5 > 0
for: 15m
labels:
severity: critical
team: sre
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 正在频繁重启"
description: "过去15分钟内,Pod重启了{{ $value | printf \"%.2f\" }}次"
# 节点内存压力
- alert: NodeMemoryPressure
expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10
for: 5m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.node }} 内存可用空间不足10%"
# 磁盘空间不足
- alert: NodeDiskPressure
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 < 15
for: 10m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.node }} 磁盘空间不足15%"
# 集群中Pod处于Pending状态超过5分钟
- alert: PodPendingLong
expr: kube_pod_status_phase{phase="Pending"} > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 处于Pending状态已超过5分钟"
部署告警规则:
kubectl apply -f alert-rules.yaml
配置Alertmanager通知
Alertmanager的配置文件通过alertmanager-main这个Secret来管理。
# alertmanager-config.yaml
apiVersion: monitoring.coreos.com/v1
kind: Alertmanager
metadata:
name: main
namespace: monitoring
spec:
replicas: 3
logLevel: info
config:
global:
resolve_timeout: 5m
receivers:
- name: 'wechat-team'
wechat_configs:
- corp_id: '你的企业微信CorpID'
to_user: '@all'
agent_id: '你的AgentID'
api_secret: '{{ .wechat_api_secret }}'
api_url: 'https://qyapi.weixin.qq.com/cgi-bin/'
- name: 'email-team'
email_configs:
- to: 'your-email@example.com'
from: 'alert@example.com'
smarthost: 'smtp.example.com:587'
auth_username: 'alert@example.com'
auth_password: '{{ .smtp_password }}'
route:
receiver: 'wechat-team'
group_by: ['alertname', 'namespace']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'wechat-team'
continue: true
- match:
severity: warning
receiver: 'email-team'
这里有个关键点:企业微信的API密钥不要硬编码在YAML文件里。你应该把它们放在Kubernetes Secret中,然后通过Prometheus Operator的values配置引用。
在Helm values文件中配置:
alertmanager:
alertmanagerSpec:
secret:
- key: wechat_api_secret
name: wechat-secret
- key: smtp_password
name: smtp-secret
然后创建Secret:
# 创建企业微信密钥Secret
kubectl create secret generic wechat-secret \
--from-literal=wechat_api_secret='你的企业微信API密钥' \
-n monitoring
# 创建SMTP密码Secret
kubectl create secret generic smtp-secret \
--from-literal=smtp_password='你的邮箱密码' \
-n monitoring
第五部分:生产环境的关键配置
1. 数据持久化
Prometheus默认数据存储在emptyDir里,Pod重启数据就没了。生产环境必须用持久化存储。
prometheus:
prometheusSpec:
retention: 30d
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
storageClassName: "nfs"
存储类型选择:
- SSD/NVMe:性能最好,适合数据量大的场景
- NFS:成本低,适合中小规模集群
- 云盘:AWS EBS、阿里云ESSD等,按需扩容
2. 高可用配置
Prometheus单实例有单点故障风险。生产环境建议部署两个副本:
prometheus:
prometheusSpec:
replicas: 2
ruleSelectorNilUsesHelmValues: false
serviceMonitorSelectorNilUsesHelmValues: false
alertmanager:
enabled: true
alertmanagerSpec:
replicas: 3
两个Prometheus实例之间通过联邦(federation)或Thanos/Cortex进行数据聚合。Thanos是我推荐的生产方案,它解决了Prometheus联邦的复杂性,同时提供了全局查询能力。
3. 资源限制
不要让监控组件抢光集群资源。给每个组件设置合理的资源请求和限制:
prometheus:
prometheusSpec:
resources:
requests:
memory: 4Gi
cpu: 2000m
limits:
memory: 8Gi
cpu: 4000m
storage:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 100Gi
内存设置要根据你的集群规模来定。一个粗略的估算公式:每秒摄入的指标数 × 保留天数 × 0.5Bytes ≈ 所需存储。如果你有1000个Pod,每个Pod每秒2个指标,保留30天,大约需要100GB存储。
4. 日志聚合
监控和日志是两个不同的概念,但需要配合工作。Prometheus处理指标数据, Loki或ELK处理日志数据。
loki:
singleBinary:
replicas: 1
gateway:
enabled: true
在Grafana里添加Loki数据源后,你可以在同一个界面里看指标和日志,排查问题时效率提升巨大。
第六部分:指标采集的最佳实践
Node Exporter:节点级指标
Node Exporter是Prometheus官方维护的节点 exporter,每个节点部署一个DaemonSet:
# node-exporter-ds.yaml(虽然helm已经内置了,但理解原理很重要)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true
hostPID: true
containers:
- name: node-exporter
image: prom/node-exporter:v1.6.1
port: 9100
args:
- --collector.diskstats
- --collector.filesystem
- --collector.loadavg
- --collector.memory
- --collector.network
- --collector.processes
resources:
requests:
memory: 64Mi
cpu: 50m
limits:
memory: 128Mi
cpu: 100m
关键参数说明:
- hostNetwork: true:使用主机网络,确保Prometheus能访问到
- hostPID: true:需要访问宿主机的进程信息
- args:指定要采集哪些指标,可以按需开启或关闭
Kube State Metrics:K8s资源状态
Kube State Metrics把Kubernetes集群的状态转化为Prometheus指标。比如:
kube_deployment_status_replicas_available:可用副本数kube_pod_status_phase:Pod当前状态kube_node_status_condition:节点状态
# 这是helm自动部署的,不需要手动管理
kubectl get pods -n monitoring | grep kube-state-metrics
cAdvisor:容器级指标
cAdvisor是Google开源的容器监控工具,Kubernetes内置了对它的支持。你不需要单独部署,Prometheus会自动从kubelet的cAdvisor endpoint抓取指标。
如果你想覆盖默认的指标集合,可以配置额外的采集:
prometheus:
prometheusSpec:
additionalScrapeConfigs:
- job_name: 'kubernetes-cadvisor'
kubernetes_sd_configs:
- role: node
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
metric_relabel_configs:
- source_labels: [__name__]
regex: 'container_(network|cpu|memory|fs|tasks|perf_event)'
action: keep
第七部分:排查问题 —— 当监控本身出问题
问题1:Prometheus抓取失败
现象:Prometheus的targets页面显示DOWN或unknown。
排查步骤:
# 1. 检查Pod状态
kubectl get pods -n monitoring -l app=prometheus
# 2. 查看Pod日志
kubectl logs -n monitoring prometheus-kube-prometheus-prometheus-0 -c prometheus
# 3. 检查ServiceMonitor是否创建成功
kubectl get servicemonitor -n production
# 4. 测试目标是否可达
kubectl exec -n monitoring prometheus-kube-prometheus-prometheus-0 -- wget -qO- http://<target-ip>:<port>/metrics
常见原因:
- Target的port名称和ServiceMonitor里配置的不一致
- 网络策略(NetworkPolicy)阻止了Prometheus访问target
- Target的metrics endpoint路径不对
问题2:指标数据缺失
现象:某些指标在Grafana里查不到。
排查步骤:
# 在Prometheus查询接口测试
# 访问 http://prometheus-service:9090/api/v1/query?query=up
# 或者直接kubectl exec进去
kubectl exec -n monitoring prometheus-kube-prometheus-prometheus-0 -- \
/bin/sh -c 'wget -qO- http://localhost:9090/api/v1/query?query=up'
如果指标确实缺失,检查:
- exporter是否在运行
- scrape配置是否正确
- metric_relabel_configs是否过滤掉了指标
问题3:告警风暴
现象:一条故障触发几百条告警。
解决方案:使用Alertmanager的inhibit_rules。
alertmanager:
alertmanagerSpec:
config:
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'namespace']
这条规则的意思是:如果一个critical告警已经触发,那么相同namespace和相同alertname的warning告警就会被抑制(不发送)。这样就能避免一条根因告警下面挂着几十条衍生告警。
第八部分:告警规则的设计哲学
写告警规则不是越多越好,质量比数量重要。
好的告警规则应该满足:
- 每个告警都能指向一个具体的行动 —— 收到告警后你知道该做什么
- 告警频率合理 —— 每天几到十几条,太多会被忽略,太少会错过问题
- 区分severity —— critical必须马上处理,warning可以排期
- 包含足够信息 —— annotation里写明发生了什么、可能原因、建议操作
告警规则分层设计
# 第一层:基础设施告警
- name: infrastructure
rules:
- alert: NodeDown
expr: up{job="node-exporter"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.instance }} 不可达"
runbook: "https://wiki.internal/runbooks/node-down"
- alert: DiskSpaceRunningLow
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 < 20
for: 10m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} 磁盘空间低于20%"
# 第二层:K8s资源告警
- name: kubernetes
rules:
- alert: DeploymentReplicasUnavailable
expr: kube_deployment_spec_replicas != kube_deployment_status_replicas_available
for: 5m
labels:
severity: critical
annotations:
summary: "部署 {{ $labels.namespace }}/{{ $labels.deployment }} 副本不足"
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 5 > 0
for: 15m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 持续重启"
# 第三层:应用层告警
- name: application
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100 > 5
for: 5m
labels:
severity: critical
annotations:
summary: "应用 {{ $labels.namespace }}/{{ $labels.service }} 错误率超过5%"
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 10m
labels:
severity: warning
annotations:
summary: "应用 {{ $labels.namespace }}/{{ $labels.service }} P99延迟超过2秒"
告警规则的生命周期管理
告警规则不是一成不变的。建议每季度review一次:
- 哪些告警从来没有触发过?可能是配置错误,或者根本不需要
- 哪些告警太频繁?可能是阈值需要调整
- 哪些告警从来没有被处理过?可能是没有对应的runbook
第九部分:从0到1的部署顺序
如果你现在要从零开始搭建,按照这个顺序不会出错:
1. 创建监控命名空间
2. 安装Prometheus Operator(helm)
3. 配置持久化存储
4. 配置高可用(可选但建议)
5. 部署Node Exporter(helm已内置)
6. 部署Kube State Metrics(helm已内置)
7. 创建ServiceMonitor,开始抓取业务指标
8. 导入Grafana仪表盘
9. 配置告警规则和Alertmanager
10. 接入通知渠道(钉钉/飞书/企业微信)
11. 配置Loki日志聚合(可选)
12. 编写runbook文档
13. 做故障演练,验证告警有效性
最后一步特别重要。很多团队搭好了监控,但从来没有验证过告警是否能真正触达。你可以故意制造一个故障,比如删除一个Pod,看看告警是否正确发出、是否有人响应、响应是否及时。
一些生产环境的真实经验
经验1:不要过度采集
每个额外的scrape目标都会增加Prometheus的存储和计算负担。开启太多不用的指标(比如cAdvisor的perf_event指标),不仅浪费资源,还会让查询变慢。按需开启,定期review。
经验2:标签是双刃剑
Prometheus的标签可以增加查询的维度,但标签基数太高会导致存储爆炸。一个简单的规则:避免在标签里放高基数字段,比如HTTP请求的path、用户ID、transaction ID等。这些应该放在metric name里,或者用labels的基数限制来控制。
# metric_relabel_configs限制标签基数
- action: labeldrop
regex: __meta_kubernetes_pod_label_(.+)
# 保留需要的标签
- action: labelkeep
regex: __meta_kubernetes_pod_label_(app|namespace|pod|service)
经验3:Grafana权限管理
不要给所有人admin权限。Grafana支持角色管理:
- Admin:可以管理用户、数据源、仪表盘
- Editor:可以编辑仪表盘,但不能管理配置
- Viewer:只读
生产环境建议至少分成Editor和Viewer两个角色。
经验4:备份Grafana配置
Grafana的仪表盘、数据源、报警规则都存在数据库里。定期备份很重要:
# 备份Grafana数据库
kubectl exec -n monitoring <grafana-pod> -- /bin/sh -c 'sqlite3 /var/lib/grafana/grafana.db ".backup /backup/grafana.db"'
# 导出仪表盘JSON
kubectl exec -n monitoring <grafana-pod> -- curl -s http://localhost:3000/api/dashboards/home
总结
搭建一套生产级的监控体系不是一两天的事。上面提到的所有内容,你可以根据集群规模和业务需求逐步完善。
最核心的一点:监控是为了让问题暴露得更早,让响应更快。所以告警规则的质量比数量重要,仪表盘的设计要面向具体的使用场景,runbook要写清楚每一步操作。
如果你在搭建过程中遇到问题,最常见的排查思路是:
- 先看Prometheus targets页面,确认指标是否被采集
- 再看Prometheus查询接口,确认指标是否存在
- 最后看Grafana数据源配置,确认图表是否正确绑定
这套体系搭建好之后,你的Kubernetes集群就有了”眼睛”和”神经”。问题还没影响到用户,告警就已经发出去了。这就是监控的价值。
祝你的集群稳定运行。
