Kubernetes 基础体系 · 第 66/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes 指标与监控:Metrics Server、Prometheus、告警和 SLO
在 Kubernetes 中,“指标”不是单一数据源。kubectl top 能看到的 CPU 和内存、Prometheus 中保存的业务请求时延、控制器暴露的副本数,以及基于这些数据发送的告警,分别属于不同层次。
可以先建立如下关系:
flowchart LR
Kubelet[Kubelet / 容器运行时] -->|资源指标| MS[Metrics Server]
MS --> MA[Metrics API]
MA --> KT[kubectl top]
MA --> HPA[Horizontal Pod Autoscaler]
App[应用 / Exporter] -->|Prometheus exposition| P[Prometheus]
KSM[kube-state-metrics] -->|对象状态指标| P
Node[Node Exporter] -->|节点指标| P
Kubelet2[Kubelet / cAdvisor 指标] --> P
P -->|PromQL| Dash[Grafana / 查询客户端]
P -->|告警规则| AM[Alertmanager]
AM --> Notify[邮件 / PagerDuty / 飞书等]
P --> SLO[SLO 计算与错误预算]
这张图的关键点是:Metrics Server 和 Prometheus 解决的问题不同,Metrics Server 通常不替代 Prometheus,Prometheus 也不自动提供 Kubernetes 的资源指标 API。
一、先区分 Kubernetes 中的三类指标
1. 资源指标:回答“当前用了多少资源”
资源指标主要包括:
- Pod 或容器当前使用的 CPU;
- Pod 或容器当前使用的内存;
- 节点当前使用的 CPU 和内存。
它们通过 Kubernetes 的资源指标 API 提供,例如:
metrics.k8s.io/v1beta1
这里的 v1beta1 是 Kubernetes 资源指标 API 的 API group 版本,不代表 Metrics Server 本身的版本。该 API 主要面向:
kubectl top;- Horizontal Pod Autoscaler(HPA);
- 其他需要短期资源使用数据的控制器或工具。
资源指标通常是短期、近实时数据,不是完整的历史监控数据库。
2. 对象状态指标:回答“Kubernetes 对象处于什么状态”
Kubernetes API 中的 Deployment、Pod、Job、Node 等对象包含期望状态和当前状态。例如:
- Deployment 期望副本数是 5,当前可用副本数是 3;
- Pod 是否处于 Ready;
- Job 是否失败;
- Node 是否因为磁盘压力被标记为异常;
- 某个 Pod 是否频繁重启。
这些状态可以由 kube-state-metrics 转换为 Prometheus 指标。它不是 Kubernetes 内置控制器,也不是 Metrics Server 的一部分,而是一个独立组件,读取 Kubernetes API 对象并暴露状态指标。
例如,常见的 Deployment 指标可能表示:
kube_deployment_spec_replicas
kube_deployment_status_replicas_available
二者的含义不同:
spec_replicas是期望值;status_replicas_available是当前可用值。
Prometheus 可以据此发现“期望副本数和可用副本数长期不一致”,而 kubectl top 不能回答这个问题。
3. 应用和系统观测指标:回答“服务运行得怎么样”
Prometheus 通常采集以下数据:
- 应用请求数、错误数、延迟;
- JVM、Go、Python 等运行时指标;
- 节点 CPU、内存、磁盘、网络;
- Kubernetes API Server、Scheduler、Controller Manager、kubelet 指标;
- 数据库、消息队列、Ingress、负载均衡器等组件指标。
这些指标一般通过 HTTP endpoint 暴露,常见格式是 Prometheus exposition format:
http_requests_total{method="GET",route="/users",code="200"} 12345
这类指标的核心价值是保留时间序列,使工程师能够回答:
- 过去一小时错误率是否上升;
- 发布前后的延迟是否变化;
- 某个节点的磁盘空间是否持续下降;
- 某类请求在不同区域的成功率是否不同。
二、Metrics Server 的组件、数据流和边界
2.1 Metrics Server 做什么
Metrics Server 是 Kubernetes 资源指标 API 的实现之一。它通常执行以下流程:
- 通过 Kubernetes API 发现节点和 Pod;
- 向各节点上的 kubelet 请求资源使用数据;
- 对采集结果进行聚合和短期缓存;
- 通过扩展 API Server 暴露
metrics.k8s.io; - 由
kubectl top或 HPA 读取。
sequenceDiagram
participant K as kubectl / HPA
participant A as kube-apiserver
participant M as Metrics Server
participant L as kubelet
K->>A: GET /apis/metrics.k8s.io/v1beta1/...
A->>M: API Aggregation 转发请求
M->>A: 查询节点和 Pod 信息
M->>L: 请求 kubelet resource metrics
L-->>M: CPU / memory 使用数据
M-->>A: 返回 Metrics API 响应
A-->>K: 返回资源指标
API Aggregation 是这里的关键机制。Metrics Server 并不是把 API Server 的二进制代码改掉,而是注册一个 APIService,由 kube-apiserver 将特定 API group 的请求转发给 Metrics Server。
可以检查相关对象:
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes" | jq .
kubectl top nodes
kubectl top pods -A
正常情况下:
APIService的AVAILABLE应为True;- raw API 请求应返回 JSON;
kubectl top应显示节点或 Pod 资源使用量。
如果 kubectl top 失败,不能直接得出“应用没有指标”的结论。它只说明资源指标 API 链路存在问题,或者当前没有可用的资源数据。
2.2 安装与验证
Metrics Server 的发布版本需要与目标 Kubernetes 版本、网络和 TLS 配置兼容。测试环境可以使用项目发布页提供的清单,例如:
kubectl apply -f \
https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
生产环境不应长期依赖 latest URL,因为它会随上游发布变化。应选择经过验证的具体版本,将清单纳入版本控制,并在升级前验证:
- Kubernetes 版本兼容性;
- kubelet 认证和授权;
- Metrics Server 到 kubelet 的网络连通性;
- APIService 的证书和聚合层配置;
- 节点地址类型选择是否符合云环境。
安装后检查:
kubectl -n kube-system get deploy metrics-server
kubectl -n kube-system get pods -l k8s-app=metrics-server
kubectl -n kube-system logs deploy/metrics-server
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl top nodes
如果 Pod 已经处于 Running,但 APIService 仍是不可用,常见原因包括:
- Metrics Server 无法访问 kubelet;
- kubelet serving certificate 不被 Metrics Server 信任;
- 节点向 kubelet 提供的地址不可达;
- kubelet 的认证或授权配置拒绝请求;
- 集群网络策略阻断了访问;
- API Aggregation 层的证书配置错误。
应先看 APIService 的事件和描述:
kubectl describe apiservice v1beta1.metrics.k8s.io
再查看 Metrics Server 日志:
kubectl -n kube-system logs deploy/metrics-server --since=15m
不要一开始就加入 --kubelet-insecure-tls。这个参数会跳过 kubelet serving certificate 的校验,只适合在明确理解风险的临时诊断中使用。生产环境应修复证书签发、信任链或 kubelet 地址配置,否则中间人攻击可能伪造资源指标。
2.3 Metrics Server 的数据为什么不能替代 Prometheus
Metrics Server 的设计目标是为资源指标 API 和 HPA 提供数据,而不是提供通用监控数据库。它通常具有以下边界:
- 不负责长期保存历史时间序列;
- 不提供 PromQL;
- 不负责业务指标采集;
- 不提供完整的标签维度和跨组件关联;
- 不负责告警路由、静默和通知;
- 不等同于节点监控或容器监控平台。
因此:
kubectl top pods
只能回答“现在大约用了多少 CPU 和内存”,不能回答:
过去 24 小时该 Deployment 的 P99 请求延迟是多少?
这两个问题分别对应资源指标和历史时序监控。
三、HPA 为什么需要 Metrics Server
HPA 根据指标调整工作负载的副本数。以 CPU 利用率为例,HPA 不是简单地把“当前 CPU 百分比”乘以某个固定系数,而是根据当前指标和目标指标计算期望副本数。
简化形式为:
其中:
currentReplicas是当前副本数;currentMetricValue是当前平均 CPU 利用率;desiredMetricValue是 HPA 配置的目标值;ceil表示向上取整。
例如:
- 当前副本数为 3;
- 平均 CPU 利用率为 80%;
- 目标 CPU 利用率为 50%。
则:
HPA 还会考虑稳定窗口、缩容行为策略、缺失指标和正在删除的 Pod,因此实际行为不一定每次都立即变成 5 个副本。伸缩控制是一个带有延迟和抑制机制的反馈回路:
资源消耗上升
-> kubelet 产生资源数据
-> Metrics Server 聚合
-> HPA 读取指标
-> HPA 修改 Deployment 的期望副本数
-> ReplicaSet 创建 Pod
-> Pod 调度和启动
-> 应用处理能力增加
这条链路的任何延迟都会影响伸缩效果。尤其是:
- 应用启动时间很长时,扩容来不及;
- CPU 不是瓶颈时,CPU HPA 可能无法改善请求延迟;
- 没有为容器设置合理的
resources.requests时,CPU 利用率的计算可能不符合预期; - 指标缺失时,HPA 会采用保守处理,而不是盲目扩缩。
一个最小的 CPU HPA 示例:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
behavior:
scaleUp:
stabilizationWindowSeconds: 0
scaleDown:
stabilizationWindowSeconds: 300
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
这里的 averageUtilization 是相对于容器 CPU request 的百分比,不是相对于节点全部 CPU 的百分比。因此,应先为容器定义 request:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
如果一个容器 request 为 250m,当前使用量为 125m,那么相对于 request 的利用率是:
即使节点总共有 16 个 CPU,这个 Pod 在 HPA 中仍可能被视为 50% CPU 利用率。
四、Prometheus 的采集模型
4.1 Pull 模型
Prometheus 主要采用 pull 模型:Prometheus 主动定期访问目标的指标 endpoint。
一个典型流程是:
- 服务暴露
/metrics; - Prometheus 通过静态配置或服务发现找到目标;
- 按 scrape interval 发起 HTTP 请求;
- 解析指标名称、标签和样本值;
- 写入本地 TSDB;
- 用 PromQL 查询或计算;
- 根据告警规则产生告警事件。
与“应用主动把每个指标推送到监控系统”相比,pull 模型有几个重要特征:
- Prometheus 可以判断目标是否无法访问;
- 采集间隔由监控系统统一控制;
- 目标不必维护监控系统连接;
- 短生命周期批处理任务可能在被抓取前就结束,不能仅依赖普通 pull 采集。
Prometheus 的 up 指标就是采集结果的基础信号:
up{job="web"}
通常:
1表示最近一次 scrape 成功;0表示目标被发现了,但 scrape 失败;- 没有时间序列则可能表示目标根本没有被发现,或者相关序列已过期。
up == 0 与“应用一定不可用”不是同一个命题。可能是 metrics endpoint 失败,而业务端口正常;也可能是网络策略只阻断了监控路径。
4.2 Kubernetes 服务发现与抓取目标
Prometheus 可以通过 Kubernetes API 发现:
- Node;
- Pod;
- Service;
- Endpoints 或 EndpointSlice;
- Ingress 等对象。
Prometheus 发现对象后,通常还需要 relabel 规则决定:
- 使用哪个地址和端口;
- 是否保留该目标;
- 将 Namespace、Pod、Service 等元数据转成标签;
- 是否丢弃某些目标。
在 Kubernetes 中,业务应用通常有以下几种暴露指标的方式:
- 应用自身暴露
/metrics; - 使用 sidecar 或独立 exporter 转换协议;
- 由节点 exporter 暴露节点指标;
- 由 kube-state-metrics 暴露对象状态;
- 由 Kubernetes 组件暴露控制面或 kubelet 指标。
Prometheus Operator 生态中常用 ServiceMonitor 和 PodMonitor 简化配置,但它们不是 Kubernetes 内置 API,而是 Operator 安装的 CRD。使用前需要确认:
- 集群中是否安装了对应 Operator;
- Prometheus 实例是否选择了该
ServiceMonitor; - CRD 版本是否兼容;
- label selector 是否匹配;
- Prometheus 是否有权限访问目标。
因此,看到一个 ServiceMonitor 对象并不意味着一定会发生抓取。
五、Prometheus 指标类型与 PromQL 基础
5.1 Counter:只增不减的累计量
Counter 适合表示:
- 请求总数;
- 错误总数;
- 处理任务总数;
- 重启次数。
例如:
http_requests_total{method="GET",code="200"} 1200
Counter 会在进程重启时归零。因此直接查询:
http_requests_total
通常没有业务意义。常见做法是使用 rate 或 increase:
rate(http_requests_total[5m])
它表示过去 5 分钟内的每秒平均增长速率。
如果 Counter 从 1200 变为 20,Prometheus 不会简单认为它减少了 1180,而会尝试识别这是一次重置。若指标本身不是 Counter,却错误地使用 rate,结果会失真。
5.2 Gauge:可上下变化的瞬时值
Gauge 适合表示:
- 当前内存使用量;
- 当前队列长度;
- 当前活跃连接数;
- 温度;
- 当前副本数。
例如:
container_memory_working_set_bytes
对 Gauge 使用 rate 通常是不正确的,因为 Gauge 的变化本身不是累计过程。要看当前值、最大值或平均值,应使用:
max_over_time(queue_depth[15m])
avg_over_time(queue_depth[15m])
5.3 Histogram:用于分布和分位数估计
Histogram 会把观测值放入预先定义的桶,并暴露:
_bucket:累计桶计数;_count:观测次数;_sum:观测值总和。
例如:
http_request_duration_seconds_bucket{le="0.1"} 950
http_request_duration_seconds_bucket{le="0.5"} 990
http_request_duration_seconds_bucket{le="+Inf"} 1000
http_request_duration_seconds_count 1000
http_request_duration_seconds_sum 180
le 表示 bucket 的上界,并且 bucket 是累计的。这里 1000 个请求中:
- 950 个请求耗时不超过 0.1 秒;
- 990 个请求耗时不超过 0.5 秒;
- 1000 个请求被计入无限上界桶。
P95 可以用:
histogram_quantile(
0.95,
sum by (le) (
rate(http_request_duration_seconds_bucket[5m])
)
)
histogram_quantile 不是从原始请求样本精确计算分位数,而是根据 bucket 分布进行估计。桶越粗,估计误差可能越大。如果 P95 跨越了一个很宽的 bucket,结果的解释需要谨慎。
聚合 Histogram 时必须保留 le:
sum by (le) (rate(metric_bucket[5m]))
如果先把 le 聚合掉,就失去了桶边界,无法再计算分位数。
5.4 Summary:客户端侧分位数
Summary 通常在客户端计算分位数并暴露结果。它可以直接提供某个实例的 P95,但不同实例之间通常不能像 Histogram 那样可靠地聚合分位数。
例如,不能一般性地认为:
所有 Pod 的 P95 = 各 Pod P95 的平均值
所以在需要跨实例、跨区域聚合时,Histogram 通常更适合。Summary 并非错误,只是它的聚合语义不同。
六、标签、时间序列和高基数风险
Prometheus 中一条时间序列由以下组合确定:
指标名 + 所有标签键值
因此:
http_requests_total{route="/users",code="200",method="GET"}
和:
http_requests_total{route="/orders",code="200",method="GET"}
是两条不同的时间序列。
如果把 user_id、完整 URL、订单号或 trace ID 作为标签,时间序列数量可能随请求量增长。其结果可能是:
- Prometheus 内存和磁盘占用上升;
- 查询变慢;
- scrape 响应变大;
- 服务本身生成指标的成本增加;
- 监控系统因资源耗尽影响其他查询和告警。
因此,路由标签应使用模板化路径:
/users/:id
而不是:
/users/8d1f...
常见做法是:
- 保留有限的业务维度,如
service、region、method、status_class; - 对 HTTP 状态码使用
2xx、4xx、5xx等分类; - 对用户、请求、订单等高基数标识使用日志或 Trace;
- 通过 recording rule 预聚合常用查询。
这正是日志、指标和追踪的边界:指标适合低基数聚合,日志适合保存具体事件,Trace 适合关联一次请求在多个服务中的路径。
七、Prometheus 告警的生命周期
Prometheus 告警通常包含两个部分:
- Prometheus 通过 PromQL 判断条件;
- Alertmanager 负责分组、去重、静默、抑制和通知。
一个告警规则示例:
groups:
- name: web.rules
rules:
- alert: WebHighServerErrorRate
expr: |
(
sum(rate(http_requests_total{
job="web",
code=~"5.."
}[5m]))
/
sum(rate(http_requests_total{
job="web"
}[5m]))
) > 0.05
for: 10m
labels:
severity: page
service: web
annotations:
summary: "web 5xx 错误率过高"
description: "过去 5 分钟错误率超过 5%,且持续 10 分钟"
这个表达式的分子是每秒 5xx 请求数,分母是每秒全部请求数。二者相除得到错误率。使用 rate 而不是直接使用 Counter,是因为我们关心的是一个时间窗口内的变化速率。
for: 10m 表示条件必须连续成立 10 分钟,才从 pending 进入 firing。状态变化可以表示为:
inactive
|
| 条件首次成立
v
pending
|
| 持续满足 for 时长
v
firing
|
| 条件不再成立
v
inactive
for 的作用是抑制瞬时毛刺,但也会增加检测延迟。如果表达式计算结果为空,或者指标暂时缺失,告警是否恢复取决于 Prometheus 的规则结果和 keep_firing_for 等配置,不能简单等同于“应用恢复”。
检查规则是否被加载:
curl -s http://prometheus.example/api/v1/rules | jq .
检查告警当前状态:
curl -s http://prometheus.example/api/v1/alerts | jq .
在 Prometheus UI 的 /rules 和 /alerts 页面,也可以查看规则加载、表达式结果和告警状态。
八、Alertmanager 解决什么问题
Prometheus 适合判断“是否满足告警条件”,Alertmanager 适合处理“应该通知谁以及如何通知”。
Alertmanager 的核心能力包括:
- 分组:把同一服务或同一集群的多个告警合并;
- 去重:避免多个 Prometheus 副本重复发送;
- 路由:按照
severity、team、service选择接收者; - 静默:维护窗口期间暂时停止通知;
- 抑制:例如节点整体故障时,抑制该节点上大量 Pod 的次级告警;
- 恢复通知:告警条件恢复后发送 resolved 事件。
例如,Prometheus 发送的告警标签可能是:
labels:
alertname: WebHighServerErrorRate
service: web
severity: page
namespace: production
Alertmanager 可以使用这些标签决定:
severity=page -> 值班电话
severity=warning -> 团队聊天
service=database -> 数据库团队
namespace=staging -> 不发送生产值班通知
告警系统最容易出现的失败不是“没有规则”,而是:
- 告警数量过多,值班人员无法判断优先级;
- 告警没有明确的处理动作;
- Alertmanager 自身不可用却没有监控;
- Prometheus 与 Alertmanager 网络断开;
- 告警通知渠道失效;
- 规则依赖的指标被改名或采集失败。
因此应分别监控:
up{job="prometheus"}
up{job="alertmanager"}
prometheus_notifications_errors_total
prometheus_rule_evaluation_failures_total
具体指标名称可能随组件版本和部署方式变化,生产环境应以当前组件暴露的 /metrics 为准,而不是盲目复制旧版本规则。
九、从监控阈值到 SLO
9.1 SLI、SLO 和 SLA
SLI(Service Level Indicator) 是实际测量的服务指标,例如:
- 成功请求比例;
- 请求延迟;
- 任务按时完成比例;
- 数据新鲜度。
SLO(Service Level Objective) 是对 SLI 的目标,例如:
生产 API 在 30 天窗口内,99.9% 的有效请求应成功返回。
SLA(Service Level Agreement) 是对外合同或承诺,通常包含违约责任。SLO 可以作为内部目标,不一定等同于 SLA。
SLO 必须先明确分母和适用范围。“99.9% 可用性”至少需要回答:
- 哪些请求属于有效请求;
- 4xx 是否计为失败;
- 5xx 是否计为失败;
- 超时如何处理;
- 健康检查请求是否排除;
- 按请求数、按时间还是按用户计算;
- 多区域服务如何聚合。
如果分母未定义,后续的错误率和错误预算都可能没有稳定含义。
9.2 错误预算的计算
设目标成功率为 ,则错误预算比例为:
如果 30 天可用性目标为 99.9%,则:
30 天总时间为:
允许不可用时间为:
也就是约 43 分 12 秒。
如果过去 30 天累计失败请求占比为 0.0004,则消耗了:
的错误预算。
错误预算的意义在于把“稳定性目标”转换成可决策的量:
- 预算充足时,可以更积极地发布或进行架构变更;
- 预算快速消耗时,应降低变更风险并优先修复可靠性问题;
- 预算耗尽不意味着系统立即停止服务,而是说明当前实现已经超出约定目标。
9.3 请求型 SLO 的 PromQL
假设应用暴露:
http_requests_total{service="checkout",code="200"}
http_requests_total{service="checkout",code="500"}
30 天成功率可以写成:
1 -
(
sum(increase(http_requests_total{
service="checkout",
code=~"5.."
}[30d]))
/
sum(increase(http_requests_total{
service="checkout"
}[30d]))
)
错误预算消耗比例可以写成:
(
sum(increase(http_requests_total{
service="checkout",
code=~"5.."
}[30d]))
/
sum(increase(http_requests_total{
service="checkout"
}[30d]))
)
/
(1 - 0.999)
当结果大于 1 时,表示错误预算已经消耗超过 100%。
这个表达式成立的前提是:
- 所有相关实例都暴露相同的指标;
code的分类规则稳定;- 计数器没有因为部署、迁移或标签变更产生语义断裂;
- 分子和分母使用同一组流量;
- 30 天数据在 Prometheus 的保留期内可查询。
如果 Prometheus 只保留 15 天,就无法直接从本地数据计算完整的 30 天 SLO。生产环境通常使用 recording rules、远程存储或专门的 SLO 组件保存更长时间范围。
十、为什么只按“错误率超过 5%”告警不够
固定阈值告警没有表达错误预算的速度。
设:
- 目标 SLO 为 99.9%;
- 允许错误率为 0.1%;
- 当前错误率为 1%。
则错误预算消耗速度,也称 burn rate,为:
含义是:如果这个错误率持续不变,错误预算将以正常速度的 10 倍消耗。
对于 30 天窗口,若希望 1 小时内消耗掉全部错误预算,则所需 burn rate 为:
实际告警会使用更短的检测窗口和更低的 burn rate。例如,常见的多窗口思路是:
- 1 小时窗口发现快速恶化;
- 5 分钟窗口确认当前问题仍然存在;
- 两个窗口同时满足才触发高优先级告警。
PromQL 示例:
(
sum(rate(http_requests_total{
service="checkout",
code=~"5.."
}[1h]))
/
sum(rate(http_requests_total{
service="checkout"
}[1h]))
) > 14.4 * (1 - 0.999)
and
(
sum(rate(http_requests_total{
service="checkout",
code=~"5.."
}[5m]))
/
sum(rate(http_requests_total{
service="checkout"
}[5m]))
) > 14.4 * (1 - 0.999)
这里:
也就是错误率超过 1.44% 时,按 14.4 倍速度消耗 99.9% SLO 的错误预算。
两层窗口的原因是:
- 仅看 5 分钟,容易被瞬时流量尖峰误触发;
- 仅看 1 小时,可能在问题已经严重影响用户后才触发;
- 长窗口和短窗口同时成立,可以兼顾持续性和响应速度。
实际 burn rate 阈值不是 Kubernetes 或 Prometheus 的规范保证,而是 SLO 设计选择。应根据业务的用户影响、响应时间和通知渠道调整。
十一、延迟 SLO 与 Histogram
如果 SLO 是:
99% 的有效请求应在 300ms 内完成
可以直接使用 Histogram 的桶:
1 -
(
sum(rate(http_request_duration_seconds_bucket{
service="checkout",
le="0.3"
}[5m]))
/
sum(rate(http_request_duration_seconds_count{
service="checkout"
}[5m]))
)
这个表达式计算的是:
超过 300ms 的请求比例
如果超过 300ms 的请求比例为 0.8%,那么满足 300ms 延迟目标的请求比例为 99.2%。
这种“阈值型延迟 SLO”通常比直接监控 P99 更适合错误预算,因为它可以明确划分:
- 分母:所有有效请求;
- 好请求:耗时不超过阈值;
- 坏请求:耗时超过阈值。
P99 则回答“分位点在哪里”,不直接表示有多少请求超标。并且 P99 对 Histogram 的桶边界敏感,必须结合实际桶配置解释。
十二、缺失数据、低流量和查询边界
12.1 低流量时错误率不稳定
如果 5 分钟内只有一个请求,而它失败了,错误率就是 100%。这可能足以说明一次真实失败,但未必适合立即触发高优先级告警。
可以同时增加请求量条件:
(
error_rate > 0.01
)
and
(
sum(rate(http_requests_total{service="checkout"}[5m])) > 1
)
具体阈值应根据服务流量和业务影响确定。低流量服务也可以采用按时间窗口的可用性计算,但要清楚“请求比例”和“时间比例”是不同 SLI。
12.2 指标缺失不是零
以下两个结果不同:
metric_name == 0
表示指标存在且值为零。
absent(metric_name)
表示查询结果中没有该指标。
如果服务下线、标签变更或抓取失败,指标可能消失。此时把空结果当成零,可能掩盖真实故障。对关键采集链路,应单独设计 up、absent 或目标数量告警。
例如:
absent(up{job="checkout"})
可以发现整个 job 没有任何目标,但不能发现某个实例少了一个。后者需要结合服务发现目标数量、期望副本数和实际抓取目标数量进行判断。
12.3 Prometheus 不是无限历史数据库
Prometheus 本地存储具有保留时间和容量限制。长时间窗口查询会受到:
- 本地数据保留期;
- 磁盘容量;
- 时间序列数量;
- 查询复杂度;
- 压缩和写入负载
的限制。
生产环境通常通过以下方式扩展:
- Prometheus remote write;
- 兼容 Prometheus API 的长时存储;
- 多 Prometheus 分片;
- 按集群或区域分层;
- recording rules 预计算长期查询所需的聚合结果。
这些方案不是 Kubernetes API 的一部分,各自的可靠性、成本、查询一致性和故障恢复行为需要单独验证。
十三、从“服务正常”到“用户正常”的监控层次
仅监控 Pod 是否 Running 不足以代表服务可用。一个 Pod 可以处于 Running,但仍然:
- Readiness 检查失败;
- 能接受 TCP 连接但无法处理请求;
- 依赖的数据库不可用;
- 只对某个区域或某类用户失败;
- 延迟已经超过用户可接受范围。
因此监控通常至少分为几层:
基础设施层
关注:
- Node 是否 Ready;
- CPU、内存、磁盘、网络;
- kubelet 和容器运行时;
- 节点压力和资源耗尽。
Kubernetes 控制面层
关注:
- API Server 请求错误和延迟;
- Scheduler 调度失败;
- Controller Manager 队列和同步延迟;
- etcd 延迟、容量和 leader 状态。
工作负载层
关注:
- Deployment 可用副本;
- Pod Pending、CrashLoopBackOff、频繁重启;
- HPA 是否达到最大副本;
- PodDisruptionBudget 是否阻止维护;
- Service 和 EndpointSlice 是否符合预期。
应用层
关注:
- 请求成功率;
- 请求延迟;
- 饱和度,如线程池、连接池、队列;
- 依赖错误;
- 业务操作成功率。
用户体验层
关注:
- 真实用户或合成探针是否能够完成关键流程;
- 跨区域、跨网络路径的访问结果;
- DNS、TLS、Ingress、负载均衡和应用联合结果。
SLO 应尽量建立在用户可感知的应用层指标上,基础设施指标更适合解释原因和提前发现风险。把“CPU 超过 80%”直接定义成“用户不可用”是常见误区,因为高 CPU 可能没有影响响应时间,也可能应用在 CPU 很低时因数据库连接耗尽而完全不可用。
十四、一个故障诊断路径
假设收到“checkout 服务 5xx 错误率过高”告警,可以按数据流逐层验证。
第一步:确认告警表达式本身有数据
sum(rate(http_requests_total{service="checkout"}[5m]))
如果结果为空,先不要诊断应用代码,应检查:
- Prometheus 规则是否加载;
- 指标名称是否改变;
- job 和标签是否匹配;
- scrape 是否成功;
- Prometheus 是否有时间序列保留问题。
第二步:确认目标是否可抓取
up{job="checkout"}
如果 up=0,检查:
kubectl get pods -n production -l app=checkout -o wide
kubectl describe pod -n production <pod-name>
kubectl logs -n production <pod-name> --since=15m
注意 up=0 说明指标抓取失败,不一定说明业务请求失败。
第三步:区分应用错误和依赖错误
按标签拆分错误:
sum by (code, route) (
rate(http_requests_total{
service="checkout",
code=~"5.."
}[5m])
)
如果某个路由集中失败,再结合日志中的 request ID 或 Trace ID 检查数据库、支付服务、消息队列等依赖。
第四步:确认 Kubernetes 是否正在替换 Pod
kubectl get events -n production --sort-by=.lastTimestamp
kubectl get pods -n production -l app=checkout
kubectl rollout history deployment/checkout -n production
kubectl describe deployment checkout -n production
如果故障紧接着发布发生,应检查:
- 新旧 ReplicaSet 的错误率;
- Readiness 是否过早成功;
- 新版本配置和 Secret;
- 滚动更新期间是否保留足够可用副本;
- 是否触发了回滚或 CrashLoopBackOff。
第五步:检查是否只是监控链路故障
如果用户访问正常,但 Prometheus 告警异常,应检查:
- 指标 endpoint 是否超时;
- Prometheus 到 Pod 的网络策略;
- ServiceMonitor 或 scrape 配置;
- Prometheus 自身资源是否不足;
- Alertmanager 是否收到告警;
- 通知渠道是否失败。
监控系统本身也必须被监控,否则“没有告警”可能只是告警系统已经失效。
十五、监控配置的生产风险
15.1 只监控 kubectl top
这会导致团队拥有资源使用数据,却没有:
- 历史趋势;
- 业务错误率;
- 时延分布;
- 告警路由;
- SLO 和错误预算。
Metrics Server 应被看作 Kubernetes 资源指标接口,而不是完整可观测性平台。
15.2 只监控平均延迟
平均值可能掩盖尾部延迟。例如 99 个请求耗时 10ms,1 个请求耗时 10s:
平均延迟约 110ms,看起来可能正常,但 P99 已经接近 10 秒。面向用户的延迟 SLO 通常需要 Histogram 和分位数或阈值桶。
15.3 用 Pod 状态直接定义可用性
Running 只表示容器进程存在,不表示请求能够成功。应至少结合 Readiness、服务请求指标和用户路径探测。
15.4 告警没有行动语义
告警应能回答:
- 影响哪个服务;
- 影响范围多大;
- 何时开始;
- 违反了哪个目标;
- 值班人员第一步该检查什么;
- 是否需要回滚、扩容或切换流量。
summary 和 description 不是装饰字段,它们决定告警到达值班人员后能否快速行动。
15.5 通过扩大采集频率解决所有问题
缩短 scrape interval 会增加:
- Prometheus CPU;
- 网络流量;
- 存储样本数量;
- 查询和压缩负载。
如果目标是发现 30 秒内的高频变化,应先确认指标是否真的需要这么高的采样频率,还是应该由应用内部聚合、日志或 Trace 补充。采集频率、告警窗口和业务响应时间必须共同设计。
十六、指标、日志和追踪如何关联
指标适合发现问题,日志适合解释单个事件,Trace 适合还原一次请求跨服务的路径。
一个可操作的关联方式是:
- Prometheus 发现
checkout的 5xx 错误率升高; - 按
route、status、region等低基数标签缩小范围; - 从应用日志检索对应时间段、实例和错误类型;
- 从日志中的
trace_id跳转到 OpenTelemetry Trace; - 在 Trace 中查看数据库、远程服务和队列调用的时延;
- 将基础设施指标与该时间段的节点、网络和容器状态对齐。
不要把 trace_id 作为 Prometheus 标签,否则每个请求都可能产生一条新时间序列。Trace ID 应留在日志字段、Trace span 或专门的关联系统中。
日志采集链路中的 stdout、Node Agent、Sidecar、采集和留存,解决的是事件文本及结构化记录的传输与保存;分布式追踪中的 Context 和采样,解决的是请求跨服务传播与链路保留;Prometheus 解决的是低基数、可聚合、可查询的时间序列。三者相互补充,但不能互相替代。
十七、最终的边界判断
可以用下面的问题选择工具:
| 问题 | 主要工具 |
|---|---|
| 当前 Pod 用了多少 CPU 和内存 | Metrics Server、kubectl top |
| HPA 应根据什么资源指标扩缩 | Metrics API,通常由 Metrics Server 提供 |
| Deployment 当前可用副本是否不足 | kube-state-metrics + Prometheus |
| 过去 30 天成功率是多少 | Prometheus 或长时存储 |
| P95/P99 延迟是多少 | Prometheus Histogram + PromQL |
| 什么时候通知值班人员 | Prometheus 告警规则 + Alertmanager |
| 是否达到 99.9% SLO | SLI 计算、错误预算和长期存储 |
| 某次请求为什么失败 | 日志和分布式 Trace |
| 节点是否因为磁盘压力导致 Pod 异常 | 节点指标、Kubernetes 状态指标、事件和日志 |
最重要的因果关系是:
资源指标 API ≠ 历史监控系统
Prometheus 查询 ≠ 告警通知
告警阈值 ≠ SLO
Pod Running ≠ 用户请求成功
指标异常 ≠ 故障根因
一个可维护的 Kubernetes 监控体系,应让资源指标支撑伸缩,让 Prometheus 保存和计算时序,让 Alertmanager 处理通知,让 SLO 约束“什么程度的问题值得打断值班人员”,再用日志和 Trace 完成根因定位。只有这些层次的数据定义一致、采集链路可验证、告警状态可解释,监控才不仅能“看到数字”,还能够支持发布、故障响应和可靠性决策。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes etcd 备份恢复:Snapshot、证书、Revision 和灾难演练
- 下一篇:Kubernetes 日志架构:stdout、Node Agent、Sidecar、采集和留存
- 延伸:Kubernetes 分布式追踪:OpenTelemetry、Context、采样和基础设施关联
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论