监控告警:Prometheus+Grafana+Alertmanager 黄金组合
一句话定位:K8s 该监控哪些指标?告警怎么不漏不噪?一套方法论加三件套(Prometheus + Grafana + Alertmanager)给你答案。
写在前面
我记得有一次凌晨 2 点被 On-Call 电话叫醒——生产 Deploy 正在"健康"运行(Readiness Probe 返回 200),但 user 的请求全部返回 503。后来复盘发现,监控只盯了"Pod 是否活着",但 Pod 处理请求的成功率和延迟完全没有监控。
这个事故让我深刻理解了那句话:你无法管理你没有度量的东西。
1 年下来,我迭代了三版监控体系,最终成型的就是 Prometheus + Grafana + Alertmanager 这套组合。这篇文章把我踩过的坑和方法论完整输出出来。
读完你能带走:
- USE/RED/四黄金信号三套监控方法论
- K8s 核心组件指标解读
- Prometheus Operator 生产部署方案
- PromQL 常用查询模式(TOP N / 分位 / 预测)
- P0/P1/P2 告警分级规范和 Alertmanager 路由配置
核心问题
K8s 该监控哪些指标?告警怎么不漏不噪?
先说结论:不漏靠方法论,不噪靠分级+抑制。下面我们先讲方法论,再讲落地工具。
一、原理剖析
1.1 三套监控方法论
方法论不是学术概念,是帮你缩小排查范围的导航。出问题时,你不需要想"该查什么",方法论的检查清单会告诉你。
USE 方法论(Brendan Gregg 提出)—— 面向资源
USE = Utilization + Saturation + Errors
检查清单(每个资源问三个问题):
┌───────────┬──────────────┬──────────────┬──────────────┐
│ 资源 │ Utilization │ Saturation │ Errors │
│ │ 使用率多少? │ 排队多长? │ 有错误吗? │
├───────────┼──────────────┼──────────────┼──────────────┤
│ CPU │ 使用率% │ run queue │ N/A │
│ Memory │ 使用率% │ swap/page │ OOM Kill │
│ Disk │ 使用率% │ I/O wait │ 坏块/SMART │
│ Network │ 带宽% │ drop/retry │ checksum │
│ FD │ 已用/上限 │ N/A │ 打开失败 │
└───────────┴──────────────┴──────────────┴──────────────┘
适用场景:基础设施、节点、中间件监控
RED 方法论(Tom Wilkie 提出)—— 面向服务
RED = Rate + Errors + Duration
每个服务监控三件事:
Rate —— 每秒请求数(QPS/RPS)
Errors —— 失败请求数(按 HTTP 5xx / 超时 / 业务错误码)
Duration —— 请求延迟分布(P50/P90/P99)
适用场景:微服务、API 网关、RPC 调用监控
四黄金信号(Google SRE Book):
四黄金信号 = Latency + Traffic + Errors + Saturation
与 RED 的区别:把 Saturation(饱和度)加了进来
Latency = 请求耗时(对应 RED 的 Duration)
Traffic = 请求量(对应 RED 的 Rate)
Errors = 错误率(对应 RED 的 Errors)
Saturation = 服务有多"满"(连接数、队列深度、goroutine 数等)
适用场景:SRE 体系下的服务健康度评估
实战中我怎么做:基础设施层用 USE,服务层用 RED + Saturation。K8s 是一个分层系统,所以分层监控:
┌─────────────────────────────────────────────────────────────┐
│ K8s 监控分层 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 业务层: 订单量/支付成功率 (业务埋点/Business Metrics) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 应用层: QPS/延迟/错误率 (RED 方法论) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ K8s层: Pod 状态/Deploy 可用副本/调度延迟 │ │
│ │ kube-state-metrics │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 节点层: CPU/Mem/Disk/Net (USE 方法论) │ │
│ │ node-exporter / cAdvisor │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 Prometheus 数据模型与 PromQL 精髓
Prometheus 时序数据模型:
指标名{标签1="值1", 标签2="值2"} 值@时间戳
示例:
http_requests_total{method="GET", handler="/api/users", status="200"} 10467 @1716000000
│ │ │ │
指标名 标签组 采样值 时间戳
PromQL 中最重要的几个概念,理解了就通了 80% 的查询:
瞬时向量 vs 范围向量:
瞬时向量 (Instant Vector):
http_requests_total ← 返回每个时间序列在查询瞬间的最新值
范围向量 (Range Vector):
http_requests_total[5m] ← 返回每个时间序列在过去 5 分钟的所有采样点
范围向量不能直接用,必须配合 rate()/avg() 等函数
rate() vs irate():
rate(http_requests_total[5m]) ← 5 分钟平均速率,平滑波动
irate(http_requests_total[5m]) ← 瞬时速率(最后两个采样点),敏感但易抖
生产建议:告警用 rate,排查问题用 irate
1.3 Prometheus Operator 架构
#mermaid-svg-i9bIp3KxLyyYbMzf{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-i9bIp3KxLyyYbMzf .error-icon{fill:#552222;}#mermaid-svg-i9bIp3KxLyyYbMzf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-i9bIp3KxLyyYbMzf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-i9bIp3KxLyyYbMzf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-i9bIp3KxLyyYbMzf .marker.cross{stroke:#333333;}#mermaid-svg-i9bIp3KxLyyYbMzf svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-i9bIp3KxLyyYbMzf p{margin:0;}#mermaid-svg-i9bIp3KxLyyYbMzf .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf .cluster-label text{fill:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf .cluster-label span{color:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf .cluster-label span p{background-color:transparent;}#mermaid-svg-i9bIp3KxLyyYbMzf .label text,#mermaid-svg-i9bIp3KxLyyYbMzf span{fill:#333;color:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf .node rect,#mermaid-svg-i9bIp3KxLyyYbMzf .node circle,#mermaid-svg-i9bIp3KxLyyYbMzf .node ellipse,#mermaid-svg-i9bIp3KxLyyYbMzf .node polygon,#mermaid-svg-i9bIp3KxLyyYbMzf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-i9bIp3KxLyyYbMzf .rough-node .label text,#mermaid-svg-i9bIp3KxLyyYbMzf .node .label text,#mermaid-svg-i9bIp3KxLyyYbMzf .image-shape .label,#mermaid-svg-i9bIp3KxLyyYbMzf .icon-shape .label{text-anchor:middle;}#mermaid-svg-i9bIp3KxLyyYbMzf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-i9bIp3KxLyyYbMzf .rough-node .label,#mermaid-svg-i9bIp3KxLyyYbMzf .node .label,#mermaid-svg-i9bIp3KxLyyYbMzf .image-shape .label,#mermaid-svg-i9bIp3KxLyyYbMzf .icon-shape .label{text-align:center;}#mermaid-svg-i9bIp3KxLyyYbMzf .node.clickable{cursor:pointer;}#mermaid-svg-i9bIp3KxLyyYbMzf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-i9bIp3KxLyyYbMzf .arrowheadPath{fill:#333333;}#mermaid-svg-i9bIp3KxLyyYbMzf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-i9bIp3KxLyyYbMzf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-i9bIp3KxLyyYbMzf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-i9bIp3KxLyyYbMzf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-i9bIp3KxLyyYbMzf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-i9bIp3KxLyyYbMzf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-i9bIp3KxLyyYbMzf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-i9bIp3KxLyyYbMzf .cluster text{fill:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf .cluster span{color:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-i9bIp3KxLyyYbMzf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-i9bIp3KxLyyYbMzf rect.text{fill:none;stroke-width:0;}#mermaid-svg-i9bIp3KxLyyYbMzf .icon-shape,#mermaid-svg-i9bIp3KxLyyYbMzf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-i9bIp3KxLyyYbMzf .icon-shape p,#mermaid-svg-i9bIp3KxLyyYbMzf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-i9bIp3KxLyyYbMzf .icon-shape .label rect,#mermaid-svg-i9bIp3KxLyyYbMzf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-i9bIp3KxLyyYbMzf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-i9bIp3KxLyyYbMzf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-i9bIp3KxLyyYbMzf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
核心 CRD
ServiceMonitor定义如何从 Service 采集指标
PodMonitor定义如何从 Pod 采集指标
PrometheusRule定义告警/录制规则
Prometheus Operator 工作流
开发者编写 ServiceMonitor YAML
Operator 监听 ServiceMonitor 资源
Operator 生成 Prometheus 配置
ConfigReloader 热重载配置
Prometheus 按配置拉取指标
ServiceMonitor 工作流(ASCII 示意):
ServiceMonitor
│
│ selector.matchLabels.app=nginx
▼
┌─────────────────┐
│ Service │
│ labels: │
│ app: nginx │
│ ports: │
│ – metrics:9113│
└────────┬────────┘
│
│ Endpoints
▼
┌─────────────────────────────┐
│ Pod 1 │
│ labels: │
│ app: nginx │
│ annotations: │
│ prometheus.io/scrape=true│
└──────────────┬──────────────┘
│
│ scrape http://10.1.2.3:9113/metrics
▼
┌─────────────┐
│ Prometheus │
│ Server │
└─────────────┘
二、实战操作
2.1 部署 kube-prometheus-stack
# 添加 Prometheus 社区 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 创建命名空间
kubectl create namespace monitoring
kube-prometheus-values.yaml —— 生产级配置:
# ============================================
# kube-prometheus-stack 生产部署配置
# Helm Chart 版本: 58.x (对应 Prometheus 2.50+, Grafana 10.x)
# ============================================
# ── Prometheus Operator ──
prometheusOperator:
enabled: true
image:
tag: v0.72.0
admissionWebhooks:
enabled: true
patch:
enabled: true
tls:
enabled: true
tolerations:
– key: node–role.kubernetes.io/control–plane
operator: Exists
effect: NoSchedule
# ── Prometheus Server ──
prometheus:
enabled: true
prometheusSpec:
# 镜像版本
image:
repository: quay.io/prometheus/prometheus
tag: v2.50.1
# 数据保留
retention: 30d
retentionSize: 45GB # 达到 45GB 自动清理旧数据
# 资源配额
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 2000m
memory: 8Gi
# 持久化存储
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: ssd–sc
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
# 远程写入(长期存储)
remoteWrite: []
# 生产建议配置 Thanos 或 VictoriaMetrics:
# remoteWrite:
# – url: http://victoriametrics.monitoring.svc:8428/api/v1/write
# 采集间隔
scrapeInterval: 30s
evaluationInterval: 30s
# 外部标签(所有指标都会带上)
externalLabels:
cluster: prod–k8s–1
region: cn–east–2
env: production
# 副本数(高可用)
replicas: 2
# 开启 Admin API(排查问题用)
enableAdminAPI: true
# 关闭不需要的 Etcd/KubeScheduler/KubeControllerManager 监控
# (托管 K8s 如 EKS/GKE 这些组件不可达)
kubeEtcd:
enabled: false
kubeScheduler:
enabled: false
service:
enabled: false
kubeControllerManager:
enabled: false
service:
enabled: false
# ServiceMonitor 选择器(空 = 采集所有)
serviceMonitorSelector: {}
podMonitorSelector: {}
# 告警规则选择器
ruleSelector:
matchLabels:
role: alert–rules
# Alertmanager 配置
alerting:
alertmanagers:
– namespace: monitoring
name: alertmanager–operated
port: web
apiVersion: v2
# 容忍度(允许调度到控制节点)
tolerations:
– key: node–role.kubernetes.io/control–plane
operator: Exists
effect: NoSchedule
# ── Service ──
service:
type: ClusterIP
port: 9090
annotations:
prometheus.io/scrape: "true"
# ── Alertmanager ──
alertmanager:
enabled: true
alertmanagerSpec:
image:
tag: v0.27.0
replicas: 3 # 集群模式
clusterAdvertiseAddress: true
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 1Gi
# Alertmanager 配置(外部化到 Secret)
configSecret: alertmanager–config
# 持久化存储(用于静默和通知状态)
storage:
volumeClaimTemplate:
spec:
storageClassName: ssd–sc
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
tolerations:
– key: node–role.kubernetes.io/control–plane
operator: Exists
effect: NoSchedule
# ── Grafana ──
grafana:
enabled: true
image:
tag: 10.4.0
# Admin 密码
adminPassword: "" # 留空则使用 Secret 管理
# 数据源(Loki、Jaeger 等在下一篇配置)
additionalDataSources:
– name: Prometheus
type: prometheus
url: http://prometheus–operated.monitoring.svc:9090
access: proxy
isDefault: true
jsonData:
timeInterval: 30s
# 资源
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
# 持久化
persistence:
enabled: true
size: 20Gi
storageClassName: ssd–sc
# 预装插件
plugins:
– grafana–piechart–panel
– grafana–polystat–panel
# Dashboard Providers
dashboardProviders:
dashboardproviders.yaml:
apiVersion: 1
providers:
– name: default
orgId: 1
folder: ""
type: file
disableDeletion: true
editable: true
options:
path: /var/lib/grafana/dashboards/default
# 核心看板(K8s 集群、节点、Pod)
dashboards:
default:
k8s-cluster-overview:
gnetId: 15757 # K8s Cluster Overview
revision: 1
datasource: Prometheus
k8s-nodes:
gnetId: 15759 # K8s Nodes Detail
revision: 1
datasource: Prometheus
k8s-pods:
gnetId: 15760 # K8s Pods Detail
revision: 1
datasource: Prometheus
# ── kube-state-metrics ──
kubeStateMetrics:
enabled: true
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 200m
memory: 400Mi
# ── node-exporter ──
nodeExporter:
enabled: true
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
# ── Prometheus Node Exporter ──
prometheus-node-exporter:
enabled: true
extraArgs:
– ––collector.filesystem.mount–points–exclude=^/(dev|proc|sys|var/lib/docker/.+|var/lib/kubelet/.+)($|/)
– ––collector.filesystem.fs–types–exclude=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
部署命令:
# 部署 kube-prometheus-stack
helm upgrade –install kube-prometheus-stack \\
prometheus-community/kube-prometheus-stack \\
–namespace monitoring \\
–values kube-prometheus-values.yaml \\
–timeout 15m \\
–wait
# 验证所有组件运行正常
kubectl get pods -n monitoring
# 预期输出示例:
# NAME READY STATUS
# alertmanager-kube-prometheus-stack-alertmanager-0 2/2 Running
# kube-prometheus-stack-grafana-xxxxx 3/3 Running
# kube-prometheus-stack-kube-state-metrics-xxxxx 1/1 Running
# kube-prometheus-stack-operator-xxxxx 1/1 Running
# kube-prometheus-stack-prometheus-node-exporter-xxxxx 1/1 Running
# prometheus-kube-prometheus-stack-prometheus-0 2/2 Running
2.2 核心指标解读
以下是每个 K8s 运维必须记住的核心指标,按监控分层组织:
节点层(node-exporter):
| node_cpu_seconds_total | CPU 各模式时间 | U | > 80% 持续 10min |
| node_memory_MemAvailable_bytes | 可用内存 | U | < 10% 总内存 |
| node_disk_io_time_seconds_total | 磁盘 I/O 时间 | S | > 80% 持续 10min |
| node_filesystem_avail_bytes | 文件系统可用空间 | U | < 20% |
| node_network_receive_drop_total | 网卡丢包 | E | > 0 |
K8s 对象层(kube-state-metrics):
| kube_deployment_status_replicas_available | 可用副本数 | 与 desired 对比 |
| kube_pod_status_phase | Pod 状态 | 过滤 Pending/Failed |
| kube_node_status_condition | 节点状态 | condition=Ready, status=true |
| kube_persistentvolume_status_phase | PV 状态 | 监控 Available/Bound 比例 |
| kube_job_status_failed | Job 失败计数 | > 0 |
容器层(cAdvisor):
| container_cpu_usage_seconds_total | 容器 CPU 总使用 |
| container_memory_working_set_bytes | 容器工作集内存(OOM 判断依据) |
| container_network_transmit_bytes_total | 容器网络发送字节 |
| container_fs_reads_bytes_total | 容器文件系统读取 |
2.3 PromQL 实战速查
# ── TOP N 查询 ──
# CPU 使用 Top 5 Pod(按 namespace 聚合)
topk(5,
sum(rate(container_cpu_usage_seconds_total{
container!="", namespace!="kube-system"
}[5m])) by (namespace, pod)
)
# 内存使用 Top 10 Pod
topk(10,
sum(container_memory_working_set_bytes{
container!="", namespace!="kube-system"
}) by (namespace, pod)
)
# ── 分位查询 ──
# P99 请求延迟(应用需暴露 histogram 指标)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, handler)
)
# P50/P90/P99 一起看(多分位)
histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.90, rate(http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# ── 错误率 ──
# 5xx 错误率
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
# ── 可用性 ──
# 服务可用副本率
sum(kube_deployment_status_replicas_available{namespace="production"})
by (deployment)
/
sum(kube_deployment_spec_replicas{namespace="production"})
by (deployment)
# ── 预测查询 ──
# 磁盘空间 24 小时预测(线性回归)
predict_linear(
node_filesystem_avail_bytes{mountpoint="/"}[6h],
24 * 3600
) < 0
# ── 变化率查询 ──
# Deployment 最近 1 小时扩容次数(判断是否有异常)
changes(kube_deployment_spec_replicas[1h])
# Pod 重启次数增长率(判断 CrashLoop 趋势)
rate(kube_pod_container_status_restarts_total[15m])
# ── 缺失检测 ──
# 服务超过 10 分钟没有收到请求(可能挂了)
absent_over_time(http_requests_total{service="payment"}[10m])
三、踩坑与排查
3.1 坑一:Prometheus 内存持续增长直到 OOM
现象:Prometheus 运行几小时后内存突破 8Gi limit,被 OOMKilled 重启,循环往复。
原因:某个 ServiceMonitor 配置的采集间隔过短(如 5 秒),且该服务暴露了大量时间序列(histogram 指标默认每个 bucket 对应一个时间序列,一个 histogram 可能有 50+ 个序列)。
定位:
# 1. 查看 Prometheus 当前序列数
kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090
curl -s http://localhost:9090/api/v1/status/tsdb | jq '.data'
# 输出示例(观察 headSeries 数量):
# {
# "headStats": {
# "numSeries": 8500000, ← 序列数 850 万!明显异常
# "chunkCount": 12300000,
# …
# }
# }
# 2. 找到序列数最多的目标
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | {labels: .labels, scrapeUrl: .scrapeUrl}' | head -20
# 3. 查询前 10 个序列数最多的指标
curl -s 'http://localhost:9090/api/v1/query?query=topk(10, count by(__name__)({__name__=~".+"}))' | jq
解决方案:
# 在 ServiceMonitor 或 PodMonitor 中配置 metric relabeling
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my–app
spec:
endpoints:
– port: metrics
interval: 30s
# 关键:只保留需要的指标
metricRelabelings:
# 仅保留 http_ 开头的指标
– sourceLabels: [__name__]
regex: 'http_.*'
action: keep
# 删除高基数的 histogram bucket(保留 summary 替代)
– sourceLabels: [__name__]
regex: '.*_bucket'
action: drop
同时在 Prometheus 全局配置中限制:
prometheus:
prometheusSpec:
# 限制每个目标的序列数
enforcedSampleLimit: 50000
# 目标标签数量限制
enforcedLabelLimit: 30
3.2 坑二:告警风暴
现象:集群网络抖动 2 分钟,所有服务的 P99 延迟告警同时触发,On-Call 手机被打爆,实际只有 2 个服务真正受影响。
原因:告警规则没有分级,没有做依赖链路的告警抑制。网络问题是根因,服务延迟是症状。
解决方案——Alertmanager 抑制规则:
# alertmanager-config.yaml
apiVersion: v1
kind: Secret
metadata:
name: alertmanager–config
namespace: monitoring
stringData:
alertmanager.yaml: |
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.company.com:587'
smtp_from: 'alertmanager@company.com'
smtp_auth_username: 'alertmanager@company.com'
smtp_auth_password: 'xxx'
# ── 路由树 ──
route:
receiver: 'default-receiver'
group_by: ['alertname', 'severity']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h # 同一条告警最多 4 小时重复一次
routes:
# P0 告警:立即通知
– match:
severity: P0
receiver: 'pagerduty-critical'
continue: true
# P1 告警:5 分钟分组后通知
– match:
severity: P1
receiver: 'wechat-p1'
group_wait: 10s
continue: true
# P2 告警:仅记录
– match:
severity: P2
receiver: 'null'
# ── 抑制规则 ──
inhibit_rules:
# 规则1: 节点 NotReady 时,抑制该节点上所有 Pod 的告警
– source_match:
alertname: NodeNotReady
severity: P0
target_match_re:
severity: 'P[12]'
equal: ['node']
# 规则2: 集群级别的网络告警触发时,抑制服务级别的延迟告警
– source_match:
alertname: ClusterNetworkIssue
target_match:
alertname: HighLatency
equal: ['cluster']
# 规则3: namespace 级别的告警触发时,抑制该 ns 下所有服务的告警
– source_match:
alertname: NamespaceQuotaExceeded
target_match_re:
alertname: 'PodCrashLooping|DeploymentReplicasMismatch'
equal: ['namespace']
# ── 接收器 ──
receivers:
– name: 'default-receiver'
webhook_configs:
– url: 'http://alert-webhook.company.com/alert'
– name: 'pagerduty-critical'
# 接入 PagerDuty / 电话通知
– name: 'wechat-p1'
wechat_configs:
– corp_id: 'ww1234567890'
to_party: '2'
agent_id: '1000001'
api_secret: 'xxx'
– name: 'null'
3.3 坑三:node-exporter 采集失败(托管 K8s)
现象:在 EKS/GKE/ACK 等托管 K8s 上,node-exporter 部署正常但 Prometheus 无法采集,targets 页面显示 context deadline exceeded。
原因:托管 K8s 的节点 IP 是云厂商 VPC 内网 IP,Prometheus 可能无法直接通过 Pod 网络访问。此外,安全组/防火墙可能没有放行 node-exporter 端口(9100)。
定位:
# 1. 手动测试 node-exporter 可达性
NODE_EXPORTER_POD=$(kubectl get pod -n monitoring -l app.kubernetes.io/name=prometheus-node-exporter -o jsonpath='{.items[0].metadata.name}')
NODE_IP=$(kubectl get pod -n monitoring $NODE_EXPORTER_POD -o jsonpath='{.status.hostIP}')
# 2. 从 Prometheus Pod 内测试网络
kubectl exec -n monitoring prometheus-kube-prometheus-stack-prometheus-0 — \\
wget -qO- http://${NODE_IP}:9100/metrics
解决方案:
prometheus-node-exporter:
# 使用 hostNetwork 模式(DaemonSet 默认使用)
hostNetwork: true
hostPID: true
# 或改用 Service 端点(推荐)
service:
enabled: true
type: ClusterIP
port: 9100
targetPort: 9100
annotations:
prometheus.io/scrape: "true"
四、最佳实践
告警分级清单
P0(5 分钟内响应) P1(30 分钟内响应) P2(工作时间处理)
├─ 集群核心组件不可用 ├─ 服务可用副本 < 期望 ├─ 证书即将过期
├─ Node NotReady > 5min ├─ P99 延迟 > 1s(5min) ├─ PV 容量 > 80%
├─ 核心服务错误率 > 5% ├─ 磁盘可用 < 20% ├─ HPA 达到最大副本
├─ 证书已过期 ├─ Pod CrashLoopBackOff ├─ 内存使用 > 80%(非紧急)
└─ PV 无法绑定 └─ CPU Throttling > 50% └─ ConfigMap/Secret 超额
告警规则设计原则
- 每条规则必须有 for 持续时间——过滤瞬时抖动,建议 P0 >= 1min,P1 >= 5min,P2 >= 10min
- summary 写清楚影响范围,“某服务错误率高” 不如 “payment 服务 P99 延迟 2.3s(阈值 1s)”
- description 写排查第一步,“检查 payment-* Pod 日志和 DB 连接”
- 多用 rate(),少用 irate()——平滑告警,减少噪点
- 告警规则做减法——如果你从来没响应过某条告警,删掉它
- 一次告警风暴后必须做 RCA 和规则优化
看板设计清单
- 使用变量(Variables):namespace、deployment、pod 做成下拉选择
- 第一行放 RED 指标:Rate / Errors / Duration 一眼看到服务健康
- 下方放 USE 指标:CPU/Mem/Disk/Network 时间序列
- 右上角加部署标注(Annotations):发布事件在 Grafana 上标竖线
- 告警阈值画水平线:Prometheus Alert 阈值在图表上可视化
- 每个面板有 Description:解释指标含义和排查思路
长期存储选型
| Thanos | Prometheus 原生兼容,Sidecar 模式无侵入 | 组件多,运维复杂 | 大型集群(>100节点) |
| VictoriaMetrics | 单二进制,压缩率极高(7x),查询快 | 非 Prometheus 官方项目 | 中小规模首选 |
| Mimir | Grafana 官方,对象存储原生,多租户 | 较新,生态不如 Thanos | 多租户 SaaS |
| Cortex | 成熟,水平扩展 | 运维复杂,Mimir 替代中 | 不推荐新项目 |
推荐方案:中小规模用 VictoriaMetrics,大规模式用 Thanos。
五、小结
监控体系的建设是迭代出来的,不是设计出来的。我的建议:
另外记住一句话:告警的价值 = 响应行动的有效性。如果你看到一条告警后不知道该做什么,说明这条告警定义有问题。
思考题
延伸阅读
- Prometheus 官方文档 – 最佳实践
- Google SRE Book – 监控分布式系统
- Robust Perception – PromQL 进阶
- USE 方法论原文(Brendan Gregg)
- RED 方法论(Tom Wilkie)
网硕互联帮助中心

评论前必须登录!
注册