AI时代的Kubernetes教程36招-第29招-Prometheus指标、日志、事件和Tracing怎么建立可观测体系?

Kubernetes 四层可观测:Metrics、Logs、Events、Tracing 的取舍

资源面板全绿,用户却说没听到回答。四层可观测要解决的就是这件事。

[读者] 在 Kubernetes 上跑 AI 推理与语音服务的平台工程师
[痛点] 节点资源全绿,业务却超时;Pod 卡住了,却没有任何日志
[现在读] 四层各自都接了一部分,故障时却拼不成一条链路
[读完] 能按 Metrics / Logs / Events / Tracing 分层定位,并落一套最小可用可观测
[旧方案] Metrics Server + kubectl top + 几张零散 Grafana 面板
    |
    v
[新需求] ASR/TTS 延迟、LLM token、GPU 利用率、会话数、错误率都要能告警能归因
    |
    v
[冲突] 资源读数是分钟级聚合,解释不了秒级业务抖动;Events 又默认不持久
    |
    v
[后果] 扩容靠试、排障靠猜,SLO 无法定量,成本无法归因

我是老李,在一个 AI 语音交互平台做平台工程。集群上跑着三类负载:GPU 节点上的 ASR/TTS 推理服务、CPU 节点上的 LLM 网关与会话编排服务,以及 CNI、CSI、CoreDNS 这些谁都不愿意出事的基础组件。

早期这套集群的可观测只有两样东西:一个 Metrics Server,加上几张从 Grafana 上随手拉出来的资源面板。白天看着挺好看,晚上出故障时基本没用——面板是绿的,客服群里已经炸了。

官方材料其实早已把边界划清楚:Kubernetes 系统指标文档说明控制面组件与 kubelet 各自暴露什么,Metrics Server 只提供 metrics.k8s.io 这一层聚合读数,OpenTelemetry 的 Kubernetes 文档给出节点、Pod、容器与日志的统一语义约定,Prometheus 明确定位为时序采集、查询与告警引擎。四份材料拼在一起,正好是四条线。

可如果只看 Metrics,为什么 Pod 卡在 ContainerCreating 的时候,资源面板永远是绿的?

可如果四层都接上了,为什么还是拼不出一条「用户说了一句话、五秒后没听到回答」的链路?

可当 GPU 显存、ASR 延迟、LLM token 数同时异常时,先看哪一层,才能不把排查变成撞运气?

01、故事

上周二下午四点,客服反馈:语音助手「说了一句话,五秒没反应」。这类反馈最麻烦——它没有错误码,没有告警,日志里只有一条看起来正常的请求记录。

我先把集群拉了一遍。节点资源正常,GPU 显存有余量,ASR 服务的 CPU 使用率不高,Prometheus 上的请求计数曲线平滑得像是没出过事。唯一的异常是几个新扩出来的 Pod,状态停在 ContainerCreating 已经两分多钟。

真正的问题这时候才浮出来:我手上只有资源读数,没有任何一层能告诉我这两分钟里集群在等什么。任务是,在不增加节点数量的前提下,把这套集群的可观测补齐到能回答三个问题——资源够不够、链路慢在哪、状态为什么没推进。

旧方案的毛病是结构性的。Metrics Server 给我的是一份聚合读数,kubectl top 给我的是同一份读数的另一个视图,Grafana 面板上的东西也来自同一条线。这条线擅长回答「现在用了多少」,完全不擅长回答「为什么没往前走」。

变化来自业务本身。语音交互是一条长链路:客户端上行音频、ASR 转写、LLM 生成、TTS 合成、下行播放。任何一个环节卡住,用户感知都是同一句话——没反应。而这条链路的每一段,恰好需要不同层的数据来解释:调度与挂载要靠 Events,资源争抢要靠 Metrics,组件内部行为要靠 Logs,跨服务耗时分解要靠 Tracing。

冲突就在这里:四层各接一部分的时候,最贵的那部分能力——归因——反而是零。

现场先看灯
再看事件流
分层不为美
只为能溯源

02、问题

旧方案失效的第一个表现是告警缺失。集群里跑着 GPU 推理 Pod,却没有一条告警能覆盖「Pod 长时间未 Ready」。资源类告警只能告诉你节点忙不忙,告诉不了你 Pod 有没有真正对外提供服务。

业务影响是可量化的:语音会话从用户说话到听到回答,通常按端到端延迟定义 SLO;中间任何一个环节被拉长,SLO 就直接被吃掉,而重试又会反过来放大 GPU 侧压力。

技术表现可以分三类。状态类:Pod 停在 ContainerCreating 或 Pending,资源面板正常,日志为空。延迟类:TTS 的 P99 出现抖动,分钟级聚合的指标把它平均掉了。链路类:trace 在 Ingress 之后断开,跨服务只剩孤立 span。

既然旧路子靠不住,那完成标准必须能被验证,而不是靠感觉:

  1. 人为触发一次镜像拉取失败,必须在 1 分钟内产生一条可被告警规则命中的事件记录。
  2. 人为注入一段 TTS 合成延迟,必须能在 tracing 里把总耗时拆到具体服务与具体阶段。
  3. 一个 Pod 从创建到 Ready 的耗时,必须能拆成调度、拉镜像、挂载卷、容器启动四段,并各自可查。
  4. GPU 节点上的容器指标,必须与节点指标在同一时间轴上可对齐。

这四条标准后面会逐条验证。

旧图看资源
新问看链路
标准先立好
再谈工具链

03、原理

四层可观测不是四套系统,是四种提问方式。它们的语义差异,直接决定了各自能回答什么。

Metrics 是有序数值加标签,可聚合、可长期存储、可做统计。它回答的是有多少、多快、多满。代价是标签组合会膨胀,指标基数直接决定存储与查询成本。

Logs 是无结构或半结构化的文本流,信息密度高、上下文完整。它回答的是它到底做了什么。代价是量大、查询贵、字段不规范就没法聚合。

Events 是 Kubernetes API 里的一等对象,记录的是状态转换的因果。它回答的是为什么没往前走。这里有个容易被忽略的事实:Events 默认是有寿命的,kube-apiserver 的 --event-ttl 默认只是小时级,过期就被 etcd 清掉,而且它默认不落外部存储——也就是说,集群出问题的证据链,如果不主动采集,可能在你开会复盘的工夫自己就消失了。

Tracing 是带父子关系的 Span 树,能把一次请求的时间拆到每一跳。它回答的是时间花在哪一段。代价是采样策略一旦定错,最该看的那条链反而没被采到。

这里要讲的第一个反直觉判断是:Metrics Server 不是监控系统。它是 API 聚合层的一部分,输出走 metrics.k8s.io,服务于自动扩缩容和 kubectl top 这类简单资源读数场景,采集分辨率是分钟级聚合,官方 README 本身就把定位说得很清楚(具体措辞以当前 README 为准)。把它当成告警与容量分析的数据源,等于用一个为自动扩缩容而生的东西去承担 SLO 职责。

第二个反直觉的地方在数据来源路径。容器级指标可以直接从 kubelet 的 cAdvisor 端点抓到细粒度数据,而 Metrics Server 走的是 kubelet 的 Summary 接口做聚合。两条路径在指标名称、标签和精度上都不一样,把它们混在一张图上做对比,得到的结论往往是错的。

理解了这层差异,架构上其实只剩一个选择:四层各归其位,再用同一套资源标识把它们钉在一起。

读数非监控
事件有寿命
粒度定边界
语义定归因

04、架构

[输入] 语音会话请求 / Pod 生命周期状态转换
    |
    v
[模块] 集群侧:API Server、Scheduler、Controller Manager、kubelet、etcd、CoreDNS、CNI/CSI
       应用侧:ASR 转写、TTS 合成、LLM 网关、会话编排
    |
    v
[数据/状态] Metrics(时序) / Logs(文本流) / Events(对象) / Traces(Span 树)
    |
    v
[处理] Prometheus 抓取与告警 · OTel Collector 统一语义与转发 · Event 采集器落盘 · OTLP 后端汇聚
    |
    v
[输出] SLO 看板 · 分层告警 · 根因链路 · 成本与容量视图

边界很清楚:集群侧采集只覆盖控制面与节点代理暴露的端点,以及 kubelet 的容器指标;应用侧采集只覆盖被 OTel SDK 或 Collector 埋点的进程。没有埋点的组件,在这四层里就是不存在的。

收益有三条:状态类问题由 Events 接手,不用再登节点;延迟类问题由 Tracing 拆分,不用在多个日志文件之间对时间戳;资源类问题交给 cAdvisor 那条路径,精度与告警语义对得上。组件内部的异常,再由 Logs 补齐最后一公里。

代价也要写清楚:每个节点上跑一个 Collector DaemonSet,会占用节点 CPU 和内存,必须设资源上限;指标基数、日志量、trace 采样率三者共同决定后端成本,任何一个失控都会让成本非线性增长;Events 需要额外一条采集链路,也就多了一个会失败的组件。

适用条件:链路长、跨语言、状态类故障频繁的集群最划算;如果只有单体应用和固定机器,这套分层是过度设计。

四层各有眼
一图定边界
收益写在纸
代价记在心

05、实战一次

目标是先跑通一条最小的、能自证的线:集群侧 Metrics 与 Events 落地,应用侧能打出 trace 和自定义指标。

环境与版本:Kubernetes 集群、Helm 3、kube-prometheus-stack、Metrics Server、OpenTelemetry Collector。具体版本以当前官方文档与 Chart 仓库为准。

目录约定:

deploy/observability/
├── metrics-server/values.yaml
├── kube-prometheus-stack/values.yaml
├── otel-collector/daemonset.yaml
├── rules/slo-rules.yaml
└── events/event-exporter.yaml

依赖安装:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm repo update

kubectl create namespace observability

helm upgrade --install metrics-server metrics-server/metrics-server \
  -n observability \
  -f deploy/observability/metrics-server/values.yaml

helm upgrade --install kps prometheus-community/kube-prometheus-stack \
  -n observability \
  -f deploy/observability/kube-prometheus-stack/values.yaml

Metrics Server 的 values 里最关键的是采集分辨率与 kubelet 证书校验。测试集群里 kubelet 证书常常不自签,需要显式关闭校验;生产环境应改成正确的 CA 配置:

# deploy/observability/metrics-server/values.yaml
args:
  - --kubelet-insecure-tls
  - --metric-resolution=15s
resources:
  requests:
    cpu: 100m
    memory: 200Mi

采集分辨率的默认值请以当前官方 README 为准;把它调小意味着更细的读数,也意味着更大的 kubelet 压力。

Prometheus 侧的关键配置是服务发现范围与抓取间隔:

# deploy/observability/kube-prometheus-stack/values.yaml
prometheus:
  prometheusSpec:
    scrapeInterval: 30s
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    retention: 15d

Events 的采集用一个 event exporter 类组件,把 API 中的 Event 对象转成可检索的记录,落到日志后端或时序后端:

# deploy/observability/events/event-exporter.yaml(节选核心配置)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: event-exporter
  namespace: observability
spec:
  replicas: 1
  selector:
    matchLabels:
      app: event-exporter
  template:
    metadata:
      labels:
        app: event-exporter
    spec:
      serviceAccountName: event-exporter
      containers:
        - name: event-exporter
          image: ghcr.io/resmoio/kubernetes-event-exporter:latest
          args:
            - -conf=/data/config.yaml
          volumeMounts:
            - name: cfg
              mountPath: /data
      volumes:
        - name: cfg
          configMap:
            name: event-exporter-cfg

应用侧的 OTel Collector 以 DaemonSet 形式跑,负责收节点容器指标、收本节点容器日志、收应用 OTLP:

# deploy/observability/otel-collector/daemonset.yaml(节选 receivers 与 service)
receivers:
  kubeletstats:
    collection_interval: 30s
    auth_type: serviceAccount
    endpoint: ${env:K8S_NODE_NAME}:10250
    insecure_skip_verify: true
  filelog:
    include: [/var/log/pods/*/*/*.log]
    start_at: end
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  k8sattributes: {}
  batch: {}

exporters:
  prometheusremotewrite:
    endpoint: http://kps-prometheus.observability:9090/api/v1/write
  otlphttp:
    endpoint: http://tempo.observability:4318

service:
  pipelines:
    metrics:
      receivers: [kubeletstats, otlp]
      processors: [k8sattributes, batch]
      exporters: [prometheusremotewrite]
    logs:
      receivers: [filelog, otlp]
      processors: [k8sattributes, batch]
      exporters: [otlphttp]
    traces:
      receivers: [otlp]
      processors: [k8sattributes, batch]
      exporters: [otlphttp]

启动:

kubectl apply -f deploy/observability/events/event-exporter.yaml
kubectl apply -f deploy/observability/otel-collector/daemonset.yaml
kubectl apply -f deploy/observability/rules/slo-rules.yaml

第一条验证:确认 metrics.k8s.io 这条聚合读数可用。

kubectl top pods -n voice

示例输出(未在当前环境实测,以下为预期结果):

NAME                        CPU(cores)   MEMORY(bytes)
asr-inference-0             1820m        14200Mi
tts-synthesis-0             940m         6100Mi
llm-gateway-0               310m         980Mi

第二条验证:确认容器指标与节点指标都能查到。

kubectl -n observability port-forward svc/kps-prometheus 9090:9090
# 容器级 CPU 使用
rate(container_cpu_usage_seconds_total{namespace="voice"}[5m])

# GPU 侧细粒度可见性依赖 DCGM exporter 或等价组件,指标命名需按当前官方文档核验

第三条验证:确认 Events 被采集。

kubectl -n voice get events --sort-by=.lastTimestamp | tail -5

预期能看到类似 Scheduled、Pulling、Pulled、Created、Started 的状态转换序列。这条序列本身就是 Pod 启动耗时四段拆分的依据。

V1 到这里是通的:Metrics 有数、Events 有记录、Trace 有入口。但 V1 只跑通了「有」,还没解决「准」和「快」。

先跑一条线
再谈全覆盖
最小可验证
才有下一步

06、排查

现象一:Pod 卡在 ContainerCreating,资源面板全绿

现象:一次扩容后,两个 ASR Pod 停在 ContainerCreating 超过两分钟,Prometheus 上节点 CPU、内存、GPU 显存都在正常区间。

怀疑:资源不足导致调度后无法启动。

检查:先看节点的可分配资源与 Pod 的 resource requests,再看 GPU 分配情况。

证据:节点剩余可分配资源充足;kubectl describe pod 的 Events 段里有一段被忽略过的记录:

Warning  FailedMount  2m  (x12 over 2m)  kubelet  MountVolume.MountDevice failed
  for volume "pvc-a1b2" : rpc error: code = DeadlineExceeded desc = context deadline exceeded

错误尝试:先去扩容节点。这是错的,因为 FailedMount 是 CSI 挂载超时,属于存储控制面侧的等待,加节点不会让挂载变快,只会让更多 Pod 一起排队等同一个 CSI 控制器。

根因:CSI 的 external provisioner 与 attacher 在短时间内收到大量 MountVolume 请求,Controller Manager 侧对应的 workqueue 出现积压,单个卷的挂载进入重试退避。

修复:把 CSI 相关控制器的日志与 workqueue 指标纳入集群侧采集,并给卷挂载类失败单独建一条告警。

现象二:TTS 的 P99 抖动,Metrics 上看不出来

现象:用户反馈偶发卡顿,但 Grafana 上 TTS 的 CPU、内存曲线平稳。

怀疑:网络抖动。

检查:先看 Metrics Server 与 Prometheus 两条路径上容器指标的采集间隔。

证据:Metrics Server 输出的是一份聚合读数,分辨率是分钟级;Prometheus 抓 cAdvisor 的间隔被设成了 60s。一次持续 8 秒的抖动,在这两条线上都会被平均掉。

错误尝试:把 metric-resolution 调到最小,然后指望它做延迟告警。这同样是错的——它输出的是资源用量读数,不是应用延迟指标,分辨率再小也不会凭空产生 TTS 的 P99。

根因:延迟类问题被交给了只能回答资源问题的层。

修复:在应用侧用 OTel SDK 埋点,把 ASR 转写、LLM 首 token、TTS 首包这几个阶段各自记成 Histogram 指标,并在 tracing 里对齐。

现象三:Trace 链路在 Ingress 之后断开

现象:一次端到端请求在 tracing 后端里只看到入口 span 和 TTS 服务的孤立 span,父子关系缺失。

怀疑:采样率过低导致中间 span 丢失。

检查:查看采样配置与 span 的 traceparent 传递情况。

证据:采样率是全量采集,不是采样问题;真正的问题在 Ingress 到第一个业务服务之间没有注入 traceparent,跨语言调用时下游 SDK 各自新建了 trace id。

根因:上下文传播缺失,而不是采样策略。

修复:在 Ingress 与网关层统一注入 traceparent,要求所有跨语言调用遵循同一套 W3C Trace Context 规范。

怀疑要留证
试错要留迹
根因在少数
现象在多数

07、优化

V2 的每一条修改,都对应第 06 章的一条证据。

根因一:状态类问题的证据链在 Events 里,但 Events 默认不持久。

修改:Event 采集器接持久化后端,并对 FailedMount、FailedScheduling、ImagePullBackOff 三类原因建独立告警规则。

# deploy/observability/rules/slo-rules.yaml(节选)
groups:
  - name: cluster-state
    rules:
      - alert: PodStuckContainerCreating
        expr: |
          kube_pod_status_phase{phase="Pending"} == 1
          and on(pod, namespace) kube_pod_container_status_waiting_reason{reason="ContainerCreating"} == 1
        for: 3m
        labels:
          severity: warning
          layer: events
        annotations:
          summary: "Pod 长时间未进入 Running"

新行为:Pod 卡住超过 3 分钟触发告警,且告警自带 layer 标签,值班同学一眼知道该去查哪一层。

验证:人为制造一次不可调度——把 requests 设成超过节点容量,观察告警是否在 for 时长后触发。

根因二:延迟类问题被交给了资源层。

修改:应用侧接入 OTel SDK,对三个关键阶段打 Histogram。

# 应用侧埋点示意
from opentelemetry import trace, metrics

tracer = trace.get_tracer("voice.pipeline")
meter = metrics.get_meter("voice.pipeline")
asr_latency = meter.create_histogram("asr.transcribe.duration", unit="ms")
tts_first_byte = meter.create_histogram("tts.first_byte.duration", unit="ms")
llm_tokens = meter.create_counter("llm.tokens.total")

with tracer.start_as_current_span("pipeline.turn"):
    with tracer.start_as_current_span("asr.transcribe"):
        pass

新行为:延迟告警可以直接建立在应用侧 Histogram 上,不用再从资源曲线里猜。

验证:注入一段人为延迟,确认 P99 曲线出现对应台阶,并且 trace 上该阶段 span 耗时同步上升。

根因三:上下文传播缺失。

修改:统一 W3C Trace Context,在入口层强制注入。

新行为:一次请求在 tracing 后端里形成完整的父子树。

验证:用同一个 trace id 在 tracing 后端查完整 span 树,确认 span 数等于链路阶段数。

成本上有一条红线:会话 id、请求 id 这类高基数字段绝不能进 Metrics,只能留在 Logs 和 Traces 里。

改在证据后
验在新行为
不猜不夸大
只认可复现

08、演进

[同一输入:一次语音会话请求]
    |
    +--[V1] 只看资源读数与零散日志
    |      代价:Pod 为何未就绪不可知;延迟抖动被分钟级聚合抹平;trace 断链
    |
    +--[V2] Metrics 走 cAdvisor / kubeletstats 细粒度路径 · Events 持久化并分层告警
    |      · 应用侧阶段化 Histogram · W3C Trace Context 统一传播
    |      代价:Collector 常驻节点开销 · 基数与采样需要治理 · 多一个可失败的采集组件
    |
[Trade-off]
得到:可归因的分层证据链,告警自带 layer 语义,SLO 可以定量
失去:节点上多一份常驻开销,后端存储成本随基数与采样率上升
适用边界:链路长、跨语言、状态类故障频繁的集群;单体应用与固定机器上不划算

正确性:V1 在资源维度上并不算错,只是答非所问;V2 把每类问题交给了能回答它的那一层。

稳定性:V2 多了一个 Collector DaemonSet 和一条 Events 采集链路,这两者本身会成为故障点,必须设资源上限并暴露自身健康指标。

复杂度:从一条数据线变成四条,随之而来的是标签规范、命名规范、采样规范三份需要长期维护的约定。

成本:主要来自指标基数、日志量和 trace 采样率。三者的成本模型不同,不能用一个预算统一压。

适用范围:四层是上限,不是起点。小集群可以先跑 Metrics 加 Events 两层,把「为什么没往前走」解决掉,再补 Logs 和 Tracing。

遗留问题:GPU 侧的细粒度指标依赖厂商 exporter,其指标命名与采集方式需按当前官方文档核验;控制面组件指标的采集端点与鉴权方式也随版本演进。

旧路通旧景
新路通新景
取舍在边界
不在口号里

09、洞见

9.1 指标层的第一原则是减标签,不是加指标

反直觉判断:可观测建设的成本大头不在采集,而在基数。加一个会话 id 标签,就能让一条时间线变成几十万条,而它带来的信息量还不如一条日志。

工程判断:Metrics 层只保留可聚合、可枚举、基数可控的维度;凡是用于单次定位的标识,一律留在 Logs 与 Traces 里。

9.2 Events 是最便宜的一层,也是最容易被忽略的一层

反直觉判断:解释 Pod 为什么没起来,最有用的往往不是资源曲线,而是一条会被自动清理掉的事件。

工程判断:Events 必须主动落盘。默认情况下它存在 etcd 里且带 TTL,集群最需要复盘的时候,这类证据常常已经过期。

9.3 四层必须共享同一套资源标识

反直觉判断:让四层各自「能用」比让四层「能对齐」容易得多,但只有对齐之后才有归因能力。

工程判断:namespace、pod、container、node 这四个维度必须在四层里命名一致。OTel 的 k8sattributes 这类处理器存在的意义就在于此——把集群元数据自动附加到所有信号上。

9.4 采样策略要按业务价值分层,而不是按流量比例

反直觉判断:全量采集不等于信息完整。全量 trace 会把后端压垮,反而让真正要看的慢请求查不出来。

工程判断:对错误、慢请求、关键会话路径做保留式采样,对正常快请求做低比例采样。采样决策要在入口处做完,中途改采样率会产生断链。

9.5 SLO 应该建在应用侧指标上,而不是资源指标上

反直觉判断:资源利用率高不高,与用户是否满意之间没有稳定关系。GPU 利用率 95% 可能一切正常,40% 也可能已经在排队。

工程判断:SLO 用 ASR/TTS 延迟、LLM token 吞吐、会话成功率这些业务侧指标来定义,资源指标只作为解释变量的辅助证据。

洞见出自证
判断落于行
反常识常对
顺直觉常错

10、系统落地

原来有什么:一个 Metrics Server,加上几张资源面板,外加 kubectl 命令行。集群能回答「现在用了多少」,不能回答「为什么没往前走」。

本篇新增了什么:集群侧补齐了 API Server、Scheduler、Controller Manager、kubelet、etcd、CoreDNS、CNI/CSI 的指标采集与分层告警;Events 从 API 对象变成可检索、可告警的记录;应用侧把 ASR 转写、TTS 首包、LLM token 三个阶段拆成可告警指标,并统一了跨语言 trace 上下文。

现在能做什么:Pod 长时间未就绪可以自动告警并指出是哪一类等待;语音会话的延迟可以按阶段拆分;GPU 节点上的容器指标能与节点指标对齐在同一时间轴。

还缺什么:GPU 侧细粒度利用率依赖厂商 exporter,需要单独确认指标命名与采集方式;日志侧字段规范尚未统一,跨服务检索仍依赖关键字;采样策略目前是静态的,还不能按会话价值动态调整。

下一步如何演进:先把日志字段规范化,让 Logs 也能按同样的四个资源标识做聚合;再把静态采样升级为基于错误的保留式采样;最后把 SLO 计算规则固化到规则文件里,让告警直接由 SLO 余额驱动。

原来有什么
现在多什么
还缺怎么补
一步一台阶

11、小结

Q1 → 因为资源面板回答的是有多少,而 Pod 卡住属于状态推进问题,证据在 Events 里,不在 Metrics 里
Q2 → 因为四层若各自采集、各自命名,就只是一堆数据;只有共享同一套资源标识并对齐时间轴,才能拼出一条链路
Q3 → 先看 Events 判断状态是否推进,再看 Metrics 确认资源是否争抢,再看 Tracing 拆时间,最后用 Logs 定位组件内部行为
状态 → 已交付一套可分层定位的最小可观测:集群侧指标与事件落地,应用侧阶段化指标与统一 trace 上下文

三问皆有答
能力已交付
边界仍清楚
演进不停步

12、作业

12.1 理解题:为什么 Metrics Server 不适合做告警数据源?

参考答案:它的定位是提供 metrics.k8s.io 这一层聚合资源读数,服务于自动扩缩容和简单资源看板;采集分辨率是分钟级聚合,指标语义是资源用量而非业务表现。把它用于告警,等于用资源读数去代理业务健康度,覆盖不全且精度不够。

12.2 实战题:如何验证 Events 采集链路是通的?

参考答案:人为制造一次可预期的失败,例如引用一个不存在的镜像触发 ImagePullBackOff,或提交一个 requests 超过节点容量的 Pod 触发 FailedScheduling,然后在采集后端按 namespace 与 reason 检索,确认事件在预期时间内出现且字段完整。关键是把验证做成可重复的动作,而不是等真实故障上门。

12.3 排障题:一次请求在 tracing 后端只看到入口 span 和下游孤立 span,怎么查?

参考答案:先排除采样问题,确认采样率与采集链路配置;再检查跨进程上下文传播,确认入口是否注入了 W3C Trace Context 的 traceparent;然后逐个检查中间跳的 SDK 是否读取并透传了该头。出现孤立 span 说明下游新建了 trace id,问题在传播而不在采样。

12.4 架构判断题:只允许先上两层,应该选哪两层?

参考答案:选 Metrics 和 Events。Metrics 解决资源与容量视角,Events 解决状态推进视角,这两层覆盖了绝大多数「Pod 起不来、节点不对劲」的日常问题,且成本可控。Logs 与 Tracing 信息量更大但成本也更高,适合在链路长度和跨服务复杂度真正成为瓶颈时再补。

题在纸上练
功在集群见
四题四层意
一层一重关

13、思考

回到最初那两分钟:Pod 停在 ContainerCreating,资源面板是绿的。这件事的核心不是指标不够多,而是问题被交给了回答不了它的那一层。

四层可观测的价值,不在数据量,而在每一层都有明确的提问方式。Metrics 问的是有多少,Logs 问的是做了什么,Events 问的是为什么没往前走,Tracing 问的是时间花在哪一段。一旦把问题问错了层,再多的数据也只能支撑猜测。

工程上的取舍同样清楚:分层带来归因能力,也带来采集开销、基数治理和组件可靠性这三笔账。判断这套体系是否值得上,不看集群规模,看链路的长度和状态类故障的频率。

一念辨边界
一证定根因
一改知取舍
一役见真章

posted @ 2026-10-12 09:40  李福春  阅读(3)  评论(0)    收藏  举报