深夜,你家小区突然停电。保安通过对讲机喊了一句“3 号楼停电了”——这是监控,它告诉你“出事了”。但如果你是物业经理,想真正解决停电问题,你需要三样东西:配电房的运行记录(什么时候跳闸、谁报修过)、电压电流的实时读数(是过载还是短路)、以及电工顺着线路从总闸一路查到 3 号楼配电箱的排查路径。这三样东西,对应到云原生系统里,就是可观测性三大支柱:日志、指标与追踪。 日志记录系统在何时、何地、发生了什么;指标把系统健康状态量化成体温、血压一样的连续数字;追踪则像一张病灶扩散地图,让你看清一次请求如何穿越层层微服务。三者配合,SRE(站点可靠性工程师)才能像医生一样,在系统“生病”时快速诊断出根因,而不是只会对着报警铃干瞪眼。这篇文章会用一次“系统全身体检”的故事,带你完整认识三大支柱。

岗位关联

掌握日志、指标、追踪三大支柱,你就能从“系统坏了只能等告警”进化到“十分钟定位根因”——这是 SRE、云架构师与 DevOps 工程师最核心的排障能力。

目录
  1. 可视化演示
  2. 可观测性概述
  3. 日志数据模型
  4. 指标数据结构
  5. 链路追踪模型
  6. 关联分析技术
  7. 采集管道工具
  8. SRE实践落地
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示日志/指标/追踪三大支柱数据模型、采集流水线及跨支柱关联分析时序。

本节配有交互动效(进入视口后自动加载,约 3KB)。

三大支柱:日志/指标/追踪数据模型 日志记录离散事件,数据量最大,适合根因定位与审计追踪 追踪记录请求链路,数据量最低,适合性能瓶颈与依赖分析 Observability=Logs∪Metrics∪Traces 三大支柱互补关联,共同构建系统全栈可观测性

可观测性概述

小区里养一只看门狗,陌生人靠近它就叫,但不会告诉你“这个人是快递员还是小偷”。监控就是这只看门狗——它能发出“系统出问题了”的报警,却答不出“为什么出问题”。而可观测性(Observability)是一位真正的医生:它通过系统的外部输出,推断出系统内部的状态。这里的“外部输出”,就是三大支柱:日志、指标与追踪。

从控制理论中走出来的“可观测性”一词,在云原生语境里被赋予了更具体的使命:只靠外部数据,还原系统内部发生了什么。日志回答“发生了什么”,指标回答“当前数据状态如何”,追踪回答“请求经历了什么路径”。三者互为补充,组合起来才能完成一次完整的“病情诊断”。

注意,监控与可观测性不是替代关系,而是上下层关系:监控是建立在可观测数据之上的告警能力。一套完整排障工作流是:告警触发 → 查指标看趋势变化 → 查日志找异常线索 → 查追踪定位跨服务链路。整个流程浓缩成一个公式:

从“看门狗”进化到“医生”,不是换一只更聪明的狗,而是给整个小区装上病历、监护仪和检查单——这正是三大支柱各自要承担的角色。

T恢复=T发现+T定位+T处理T_{\text{恢复}} = T_{\text{发现}} + T_{\text{定位}} + T_{\text{处理}}
故障恢复总时间等于告警发现、根因定位、修复处理三段时间之和,三大支柱主要压缩的是最耗时的“定位”环节。
  1. 恢复总时长从故障发生到服务完全恢复的总时间,是衡量排障效率最直接的指标。
    T恢复T_{\text{恢复}}
  2. 发现时间从故障发生到告警触发的间隔,依赖指标采集频率与告警规则是否灵敏。
    T发现T_{\text{发现}}
  3. 定位时间从收到告警到确认根因的间隔,这一项决定排障成败,是三大支柱的主战场。
    T定位T_{\text{定位}}
  4. 处理时间根因确认后执行修复动作的间隔,依赖预案完备度与自动化能力。
    T处理T_{\text{处理}}

日志数据模型

医生看病,第一件事是翻病历本:上次发烧是什么时候、吃了什么药、有没有过敏史。日志(Logs)就是系统的病历本,忠实记录每一次“就诊事件”。一条日志说“2024-05-01 10:03:02 订单服务报错:数据库连接超时”,这就是一次完整的事件叙述。

日志是带时间戳的离散事件记录。生产环境里,日志通常以结构化 JSON 输出,字段必须规范化,方便机器检索。常见字段包括时间戳(timestamp)、级别(level)、服务名(service)、消息体(message)以及关联用的 trace_id。级别从 DEBUG 到 FATAL 依次加重,生产环境一般保留 INFO 及以上,ERROR 级别应单独对接告警。

日志的采集方式很有讲究。在 Kubernetes 中部署应用时,惯例是让应用把日志打到标准输出(stdout),再部署一个采集 Agent(如 Fluent Bit、Filebeat)从容器里“偷听”输出流,解析后转发到日志平台。日志天然信息密集但噪音极大,一个大规模集群每秒可产生百万条日志,所以要分级存储:热数据进搜索引擎供全文检索,冷数据压缩后进对象存储。日志五元组可以写成:

病历本写得越规范,医生越容易快速翻到关键页。日志结构越统一,排障时的检索效率就越高——这是第一条支柱的核心价值。

L=(t,level,service,message,trace_id)L = (t, \text{level}, \text{service}, \text{message}, \text{trace\_id})
一条日志由时间、级别、服务、消息和链路 ID 五个核心字段构成,其中链路 ID 是后续与追踪关联的关键钥匙。
  1. 时间索引日志发生的那一刻,精确到毫秒,是所有时间对齐和排序的基准。
    tt
  2. 日志级别DEBUG/INFO/WARN/ERROR/FATAL 五档,决定日志的重要程度和处理优先级。
    level\text{level}
  3. 服务归属哪台机器、哪个服务产生的,排障时第一时间锁定责任方。
    service\text{service}
  4. 关联钥匙请求的唯一标识,让这条日志能被“拼回”完整的调用链。
    trace_id\text{trace\_id}

指标数据结构

重症监护室的墙上挂着一台监护仪,心率、血压、血氧饱和度汇成一条条连续曲线,护士扫一眼就知道病人状态是否稳定。指标(Metrics)就扮演这个角色:它们是可聚合的数值序列,以固定时间间隔采集,回答“现在数值是多少”。

指标有四种核心类型,就像体检报告上的不同项目。Counter(计数器) 只增不减,适合统计累计量,比如“今天总请求数 12 万次”;Gauge(仪表) 可增可减,描述当前瞬时状态,比如“当前 CPU 使用率 73%”;Histogram(直方图) 把样本分桶,用于计算延迟分布;Summary(汇总) 直接在客户端算好分位数。有了这些类型,监控系统才能在告警时回答“慢了”——至于“为什么慢”,需要交给另外两根支柱。

Prometheus 是当前指标领域的事实标准,采用拉取模型:应用暴露一个 /metrics 端点,Prometheus 定期“上门取样”。通过 PromQL 查询语言,可以即时算出每秒请求数。比如,一分钟内计数器增长了 6000,那么 QPS 就是 100。公式表达如下:

监护仪的优点是数据量小、存储便宜、趋势直观,最适合做告警触发器。但它只能告诉你“病人心率飙到 180 了”,说不清是紧张、发烧还是失血——查明原因,需要看追踪。

QPS=ΔcounterΔt\text{QPS} = \frac{\Delta \text{counter}}{\Delta t}
把两个时刻的请求计数差值,除以经过的时间,就得到这一秒的平均请求量,相当于给监护仪画出“心率曲线”。
  1. 计数器累计型指标,记录从启动至今的总数,比如总请求数。
    counter\text{counter}
  2. 求差值取两个时间点读数的差,得到这段时间内的新增量。
    Δcounter\Delta \text{counter}
  3. 时间窗口两次读数之间的间隔,可以是 1 分钟或 5 分钟。
    Δt\Delta t
  4. 除法求速率用新增量除以时间,得到平均每秒的请求速率。
    ΔcounterΔt\frac{\Delta \text{counter}}{\Delta t}

链路追踪模型

医生怀疑病灶已经扩散,会开一张增强 CT 检查单:往血管里注入造影剂,然后追踪造影剂流经全身器官的影像路径。链路追踪(Tracing)就是这个思路——给一次请求注入“造影剂”(TraceID),观察它流经每个微服务时留下的“影像”(Span)。

Trace 是一条端到端的请求链,代表“一次从用户点到应用”的完整旅程。Span 是旅程中的单个步骤:网关收到了请求、订单服务查了一次数据库、支付服务调了一次外部 API,各自都是一个 Span。Span 之间通过 parent_id 形成树形结构——支付 Span 的父节点是订单服务里发起调用的那个 Span。一条 Trace 就是这棵树的合集:

关键技术是上下文传播:请求进入第一个服务时生成 TraceID,之后通过 HTTP Header(W3C Trace Context 的 traceparent 字段)逐跳携带,每经过一个服务就追加一个 Span。由于请求量巨大,全量追踪的存储成本难以承受,生产环境通常采用采样策略——只保留 1% 到 10% 的请求追踪,头部采样按入口决定,尾部采样则优先保留出错请求。OpenTelemetry 是行业标准 SDK,一次埋点即可输出三大支柱数据。

造影剂流到哪里,病灶的扩散路径就清晰到哪里。有了这张路径图,排查跨二十个微服务的慢请求,不再需要靠猜。

Trace={span1,span2,…,spann}\text{Trace} = \{ \text{span}_1, \text{span}_2, \dots, \text{span}_n \}
一条完整调用链由多个操作跨度(Span)组成,Span 通过父子关系把一次跨服务请求组织成一棵调用树。
  1. 操作跨度Span 是调用链中的一个具体操作,承载服务名、操作名、开始时间与耗时。
    spani\text{span}_i
  2. 集合符号大括号表示“一组 Span 的集合”,说明 Trace 是多个操作拼出的完整旅程。
    {… }\{ \dots \}
  3. 端到端链路从入口到出口的所有 Span 组合起来,就是一次请求的完整路径。
    Trace\text{Trace}

关联分析技术

医生的办公桌上摊着三份材料:病历本(日志)、监护仪曲线(指标)、CT 影像(追踪)。单独看任何一份都难以确诊,把三份叠在一个灯箱上,病灶才无所遁形。排障的过程,本质是一场关联分析。

最核心的关联键是 TraceID:日志平台从日志字段中提取 TraceID,点击即可跳转到对应追踪链路;反过来,从追踪链路的某个 Span 也能一键查看该服务在那几毫秒打出的相关日志。第二个关联维度是时间:指标曲线显示 10:03:02 出现延迟尖峰,就去日志系统检索这 30 秒窗口内的 ERROR 日志,这种“指标先锁定、日志再深挖”的打法非常高效。

一个标准排障故事是这样的:Prometheus 告警“订单服务 P99 延迟超阈值” → Grafana 看指标锁定异常窗口 → Loki 查该窗口日志,发现大量“数据库连接池耗尽” → 点击 TraceID 跳转 Tempo,看到慢请求全部命中同一条数据库调用链。三大支柱的关联可以用关系运算表达:

三份材料叠在一起,才是一张完整的诊断报告。三大支柱不是三个孤岛,而是建在同一套 TraceID 之上的三块透视镜。

σtraceID(L)⋈σtraceID(T)\sigma_{\text{traceID}}(L) \bowtie \sigma_{\text{traceID}}(T)
让日志数据集和追踪数据集都按同一个 trace_id 筛选后连接起来,就拼出了这次请求的“完整病历+影像报告”。
  1. 日志数据集全部日志记录构成的集合。
    LL
  2. 选择运算σ 像聚光灯,只保留满足条件的记录,这里条件是 trace_id 匹配。
    σtraceID\sigma_{\text{traceID}}
  3. 追踪数据集全部 Span 记录构成的集合。
    TT
  4. 自然连接⋈ 符号表示把两堆记录按同一个字段(trace_id)拼成一张大表——日志和链路由此打通。
    ⋈\bowtie

采集管道工具

抽完血之后,样本要打包、贴码、物流转运、实验室分拣,最后才出现在检验报告上。可观测性的采集管道就是这套体检物流系统,负责把三大支柱的数据从“产生地”安全、高效地运到“查询端”。

完整管道要过四关。第一关采集:每个节点部署 Agent(如 Fluent Bit、Vector、OpenTelemetry Collector),从容器 stdout、系统文件、网络探针中侧写数据。第二关缓冲:数据先进入内存队列或 Kafka,防止突发流量压垮下游。第三关处理:解析 JSON、丢弃 DEBUG 日志、按 TraceID 采样、脱敏敏感字段。第四关存储与查询:日志进入 Loki 这类低成本全文索引,指标进入 Prometheus,追踪进入 Tempo/Jaeger,前端通过 Grafana 统一呈现。

管道设计的关键是采样闸门,否则成本会失控。日志通常全量保存(因为需要查细节),指标天然紧凑所以全量采集,追踪则严格控制采样率。每条数据都要算存储账:

物流系统通畅,医生才能十分钟内拿到全部报告。采集管道的目标不是把所有数据搬到存储——而是以可承受的成本,把最关键的数据在关键时刻送到该出现的地方。

D=∑iri×bi×Ti×piD = \sum_{i} r_i \times b_i \times T_i \times p_i
总存储量等于每类数据的每秒条数、单条字节数、保留时长和采样率四者乘积之和。这是规划存储预算的万能公式。
  1. 产生速率每秒产生多少条日志或指标点,取决于系统规模。
    rir_i
  2. 单条体积一条日志或一个指标点占多少字节,JSON 日志通常几百字节。
    bib_i
  3. 保留时长数据要存多久,热数据 7 天、冷数据 90 天是常见配置。
    TiT_i
  4. 采样率0 到 1 之间的系数,1 表示全量保留,0.01 表示只留 1%。
    pip_i
  5. 求和汇总把日志、指标、追踪三类数据的成本逐项相加,得出总存储预算。
    ∑i\sum_{i}

SRE实践落地

体检中心不只要出报告,还要根据报告给客户定制健康管理方案。SRE(站点可靠性工程)干的正是这件事:把三大支柱的原始数据,转化成一整套系统健康管理制度。

第一件工具是黄金四信号:延迟(Latency)、流量(Traffic)、错误(Errors)、饱和度(Saturation)。这四项是系统的“生命体征”,对应体检必测的心率、血压、体温和血氧。第二件工具是 SLO 与错误预算:SLO(服务等级目标)是给系统定的健康红线,比如“99.9% 的请求延迟低于 500ms”;SLI(服务等级指标)是实际达成率的测量值;错误预算则是允许超标的额度。发布新版本就像病人试吃新菜——如果错误预算被快速消耗,说明身体吃不消,必须停止发布,优先恢复稳定:

第三件工具是告警分级:页面级告警要求立即处理(打电话、影响用户);票据级告警生成工单(不影响主流程);通知级告警进周报(仅供观察)。每条告警都必须附上对应指标的链接和日志的查询入口,让接收人一步进入排障现场。

好的体检套餐不是越贵越好,而是覆盖了最重要的指标、形成了“体检—诊断—治疗—复查”的闭环。SRE 的终极目标,就是把排障变成一道流水线——数据自动汇聚,告警自动携带上下文,恢复动作自动执行。

Error Budget=100%−SLO\text{Error Budget} = 100\% - \text{SLO}
错误预算等于百分之百减去服务等级目标。SLO 定 99.9%,错误预算就是 0.1% 的时间或请求量,用完了就冻结上线、全力修稳定性。
  1. 服务目标SLO 是服务可靠性承诺,用百分比或时延阈值表达。
    SLO\text{SLO}
  2. 余量计算从 100% 中减去承诺值,剩余部分就是允许出错的空间。
    100%−SLO100\% - \text{SLO}
  3. 预算消耗每次故障、每个超时请求都在消耗预算,消耗速度就是系统健康度的警告灯。
    Error Budget\text{Error Budget}

AI 应用场景

微服务故障定位

阶段:故障诊断与根因分析

解决的问题:传统排障靠 SSH 登录服务器翻日志,一次请求跨越 20 个微服务,平均定位耗时 3 小时。接入三大支柱后,指标告警锁定时间窗口,日志定位报错服务,追踪还原完整调用链,排障效率从小时级提升到分钟级。

具体案例:某电商平台大促期间“订单提交缓慢”。Prometheus 指标曲线显示 10:03 延迟陡增,Loki 检索该时间窗内订单服务日志,发现大量“数据库连接池耗尽”,点击日志中的 TraceID 跳转 Tempo,看到请求打到数据库服务后排队 2 秒。最终定位为促销脚本打满了连接池。

容量规划与扩容决策

阶段:性能分析与容量规划

解决的问题:凭经验扩容容易过度采购或容量不足。利用指标的长期趋势数据预测峰值流量,结合历史大促数据估算需要扩容的规模,让每一台服务器都花在刀刃上。

具体案例:云厂商运维团队导出 Prometheus 中 90 天的 QPS、CPU 和连接数曲线,叠加业务增长曲线,预测下一季度的流量峰值,提前扩容并验证瓶颈服务。追踪数据进一步揭示哪些链路占用了最多资源,为架构优化提供依据。

发布质量管控

阶段:变更管理与发布管控

解决的问题:应用上线后出现问题难以第一时间发现,或无法快速决策是否回滚。用 SLO/错误预算量化发布对稳定性的冲击,配合追踪对比新旧版本的延迟与错误率,让发布决策从“拍脑袋”变成“看数据”。

具体案例:某团队做 10% 灰度发布时,实时监控新版本的 P99 延迟和错误率指标,同时对比新旧版本链路的追踪数据。发现新版本在支付环节错误率上升导致错误预算加速消耗,系统自动暂停灰度并触发告警,避免了全量故障。

Python 演示

python108 行
import randomfrom collections import defaultdict  class Span:    """追踪里的一个跨度,代表一次服务调用"""    def __init__(self, trace_id, span_id, parent_id, service):        self.trace_id = trace_id        self.span_id = span_id        self.parent_id = parent_id        self.service = service        self.duration_ms = 0  class Logger:    """日志采集器:所有服务把事件写入这里"""    def __init__(self):        self.lines = []     def write(self, trace_id, level, service, msg, duration_ms):        self.lines.append({            "trace_id": trace_id, "level": level,            "service": service, "msg": msg, "duration_ms": duration_ms        })  class Metrics:    """指标采集器:计数器和延迟观测"""    def __init__(self):        self.counter = defaultdict(int)        self.observations = defaultdict(list)     def count(self, name, value):        self.counter[name] += value     def observe(self, name, value):        self.observations[name].append(value)  def generate_trace(chain, logger, metrics):    """模拟一次请求生成追踪链路"""    trace_id = "t%d" % random.randint(100000, 999999)    spans = []    parent_id = None     for i, service in enumerate(chain):        span = Span(trace_id, "s%d" % i, parent_id, service)        parent_id = span.span_id         duration = random.uniform(5, 60)        if service == "payment":            duration = random.uniform(80, 300)         span.duration_ms = duration        if i == 0:            metrics.count("requests_total", 1)        metrics.observe("latency_ms", duration)         if service == "order" and duration > 50:            logger.write(trace_id, "ERROR", service, "timeout", duration)        else:            logger.write(trace_id, "INFO", service, "ok", duration)         spans.append(span)     if spans:        total_ms = sum(s.duration_ms for s in spans)        logger.write(trace_id, "INFO", "gateway", "total", total_ms)    return trace_id, spans  def percent_99(values):    """计算 P99 分位数"""    values = sorted(values)    return values[int(len(values) * 0.99) - 1]  def main():    logger = Logger()    metrics = Metrics()    chain = ["gateway", "order", "payment"]     for _ in range(300):        generate_trace(chain, logger, metrics)     total_requests = metrics.counter["requests_total"]    p99 = percent_99(metrics.observations["latency_ms"])    errors = [line for line in logger.lines if line["level"] == "ERROR"]     trace_map = defaultdict(list)    for line in logger.lines:        trace_map[line["trace_id"]].append(line)     slowest = sorted(trace_map.items(),                     key=lambda kv: kv[1][-1]["duration_ms"],                     reverse=True)[:3]     print("总请求数:", total_requests, "P99延迟: %.1f ms" % p99)    print("ERROR日志数:", len(errors))    for trace_id, lines in slowest:        print("慢请求", trace_id, "总耗时 %.1f ms" % lines[-1]["duration_ms"])        for line in lines[:-1]:            print("  ", line["service"], line["msg"],                  "%.1f ms" % line["duration_ms"])  if __name__ == "__main__":    main()
目的:这段代码模拟了 300 个请求依次穿过网关、订单、支付三个服务,同时生成三大支柱数据——日志(Logger)、指标(Metrics)、追踪(Span),并按 TraceID 完成关联排障。
  1. span = Span(trace_id, "s%d" % i, parent_id, service)每经过一个服务就创建一个 Span,parent_id 形成调用树结构,相当于追踪链路的一个节点。
  2. logger.write(trace_id, "ERROR", service, "timeout", duration)日志必须带上 trace_id 和耗时,这是后续关联分析的“钥匙”。
  3. metrics.observe("latency_ms", duration)把每个 Span 的耗时存进观测列表,供最后计算 P99 分位数。
  4. trace_map[line["trace_id"]].append(line)按 trace_id 把日志分组,实现日志与追踪的关联,这就是“拼图游戏”在代码里的落地。
  5. sorted(trace_map.items(), key=lambda kv: kv[1][-1]["duration_ms"], reverse=True)[:3]按每条链路的网关汇总耗时排序,找出最慢的三个请求。
  6. values[int(len(values) * 0.99) - 1]延迟样本排序后取第 99 百分位的下标,就是 P99 延迟。

常见误区

误区我已经上了 Prometheus 指标监控,CPU、内存、QPS 都能看到,日志和追踪没必要再搞了,纯属浪费钱。

正解指标是“体温计”,能告诉你发烧了,却说不出是病毒还是细菌感染。日志记录症状细节,追踪绘制病灶传播路径。只靠指标排障,就像只盯着监护仪却拒绝看病历和 CT,效率极低。三大支柱不是可选项,而是互补的必选项。

为何常见:指标最容易接入、存储成本低、告警效果立竿见影,让人误以为“有了告警就等于有了一切”。

误区日志是排障的“真相”,删了怕出事的时候找不到,所以全部保留,越多越安心。

正解日志是海量事件流,全量保存的成本呈线性爆炸。正确做法是分层:热数据 7 天进搜索引擎,冷数据压缩后进对象存储。同时日志在采集端就要过滤采样——DEBUG 日志直接丢弃,ERROR 日志全量保留。存“最可能有用”的日志,而不是“所有”日志。

为何常见:排障时遇到“日志被采样掉了”的懊恼经历,让运维走向另一个极端。

误区我们配了告警规则,告警也能正常发出,所以系统已经具备可观测性了。

正解监控回答“是否出问题”,可观测性回答“为什么出问题”。告警只是可观测性数据的上层应用。真正的可观测性要求数据能跨支柱关联——告警发出后,接收人能不能十分钟内找到根因?不能的话,就还停留在监控阶段。

为何常见:二者在日常语境中经常混用,工具链也高度重叠,让新人误以为是一回事。

误区追踪就是查“哪个服务慢了”,平时没慢请求的时候完全用不上。

正解追踪的价值远不止性能排查。它生成的调用拓扑可以用于服务依赖分析(发现隐藏的循环调用)、容量规划(找出瓶颈节点)、安全审计(还原异常请求的完整路径)。把追踪看成“系统地图”,而不是“交警测速仪”。

为何常见:多数人第一次接触追踪正是从“慢接口排查”开始的,容易把工具的价值窄化。

误区日志进 ES,指标进 Prometheus,追踪进 Jaeger,各查各的,互不相通,反正都能查到数据。

正解可观测性的核心价值在于关联。日志里的 TraceID 跳不到追踪,指标尖峰对不上日志时间窗,三大支柱就退化成三个孤立的“查看器”。度量一个可观测性平台的好坏,不是看数据全不全,而是看关联顺不顺。

为何常见:搭建期按组件分别落地最容易,团队缺乏统一设计,导致建完才发现数据之间“没有接线”。

面试题

Q1. 监控与可观测性的核心区别是什么?

单选题 · easy

  • A.监控关注是否异常,可观测关注为何异常← 正确答案
  • B.监控只采集指标,可观测只采集日志
  • C.可观测性必须使用开源工具才能实现
  • D.监控适合单体,可观测适合微服务

答案:A。监控是看门狗,负责报警“出事了”;可观测性是医生,负责诊断“为什么出事”。A 精确描述了两者的核心区别。B 错在把数据源割裂,两者都依赖三类数据;C 与工具选型无关;D 与架构形态无关。

Q2. 生产环境中日志最常见的采集方式是什么?

单选题 · easy

  • A.应用直接输出到标准输出再捕获← 正确答案
  • B.日志需要人工手动复制到报表系统
  • C.日志只能通过数据库备份来实现
  • D.日志采集必须依赖商业软件才能做

答案:A。Kubernetes 场景下,应用把日志打到 stdout,采集 Agent 从容器输出流捕获转发,这是云原生标准做法,对应“护士从病房收集标本送检”。B 没有自动化;C 混淆日志与数据备份;D 开源工具遍地都是。

Q3. Counter 类型的指标有什么特征?

单选题 · mid

  • A.只增不减,适合统计累计总量← 正确答案
  • B.可增可减,用于描述当前状态
  • C.只记录字符串,不记录数值信息
  • D.只记录当前值,不记录历史数据

答案:A。Counter 像跑步计数器,只向前累计,如“总请求量”。B 描述的是 Gauge 的特征;C 指标必须是数值类型;D 与类型定义无关,任何指标都按时间序列落库。

Q4. 关于 Trace 与 Span 的关系,下列说法正确的是?

多选题 · mid

  • A.一个 Trace 由多个 Span 组成← 正确答案
  • B.Span 之间可以有父子关系← 正确答案
  • C.Span 数量必须与容器数量相同
  • D.TraceID 贯穿所有相关调用← 正确答案

答案:ABD。Trace 是一次请求的完整旅程,Span 是旅程中的每一步,一个 Trace 由多个 Span 组成(A 对)。Span 通过 parent_id 形成树形父子结构(B 对)。TraceID 作为“造影剂”贯穿整条链路(D 对)。C 错在 Span 与容器数量无关,一个服务可产生多个 Span。

Q5. P99 延迟指标的含义是什么?

单选题 · mid

  • A.99% 的请求延迟不超过该值← 正确答案
  • B.每秒最多能处理 99 个请求
  • C.系统可靠性达到了 99% 的水平
  • D.99% 的日志都是错误日志

答案:A。把一千个请求的延迟从小到大排序,第 990 个的延迟就是 P99——它代表“绝大多数请求都落在这个时间内”。B 混淆延迟与吞吐;C 混淆性能指标与可靠性目标;D 与日志完全无关。

Q6. 请求上下文跨服务传播依靠什么机制?

单选题 · easy

  • A.通过 HTTP 请求头传递标记← 正确答案
  • B.通过文件系统共享全局变量
  • C.通过数据库定时同步来实现
  • D.通过重启服务来重新初始化

答案:A。TraceID 借 HTTP Header(如 W3C Trace Context 的 traceparent 字段)逐服务传递,像“接力棒”一样跑到终点。B、C 都不符合无状态服务的调用方式;D 更是南辕北辙。

Q7. 以下哪些属于 SRE 实践中的黄金四信号?

多选题 · hard

  • A.延迟,比如请求响应耗时← 正确答案
  • B.流量,比如每秒请求数量← 正确答案
  • C.错误,比如请求失败占比← 正确答案
  • D.饱和度,比如队列积压程度← 正确答案

答案:ABCD。黄金四信号是 Google SRE 定义的四个核心维度:延迟(Latency)、流量(Traffic)、错误(Errors)和饱和度(Saturation),相当于系统的生命体征四项,恰好对应体检单上的心率、血压、体温和血氧。

Q8. 错误预算(Error Budget)的计算方式是什么?

单选题 · hard

  • A.100% 减去 SLO 目标值← 正确答案
  • B.SLO 目标值乘以请求总量
  • C.错误日志数除以告警总数
  • D.99.9% 是固定的错误预算值

答案:A。错误预算是允许系统不达标的时间额度。若 SLO 定为 99.9% 可用,错误预算就是 0.1%——一年约 8.76 小时。预算耗尽就冻结发布,优先做稳定性。B 没有依据;C 与错误预算无关;D 把 SLO 常见值错当成固定预算。

Q9. 日志与追踪进行关联分析的主要方式有哪些?

多选题 · mid

  • A.在日志中写入 TraceID← 正确答案
  • B.按时间窗口对齐查找← 正确答案
  • C.用正则匹配所有错误堆栈
  • D.通过 SpanID 反向关联日志← 正确答案

答案:ABD。日志带上 TraceID,就能与追踪链路互相跳转(A 对);指标曲线锁定时间窗后去日志里搜该窗口是常用打法(B 对);SpanID 也可以作为细粒度关联键(D 对)。C 只是日志解析手段,不构成关联分析。

Q10. 追踪采样率设为 10%,对总请求量做统计时需要注意什么?

单选题 · extreme

  • A.统计结果仍需按采样率放大← 正确答案
  • B.追踪数据丢失概率是百分之十
  • C.日志必须全部保留不能采样
  • D.采样只影响延迟不影响请求

答案:A。采样只保留了 10% 的追踪数据,若想用样本量化全量请求的特征(如错误率),需要把统计值除以采样率做放大推算,像“抽查质检后按比例估算整批良品率”。B 混淆采样与丢失;C 日志与追踪策略独立;D 采样对各类统计都有影响。