Hacker News 中文摘要

RSS订阅

Ontology-Driven Observability: Building an End-to-End Knowledge Graph at Netflix

2024年11月,杰克·保罗(Jake Paul)与迈克·泰森(Mike Tyson)走进拳台时,网飞(Netflix)把比赛送到了约6500万并发流。平日里,Netflix 基础设施会接收超20亿次请求,每秒处理3800万以上实时日志事件。在这种规模下,全局视图无法塞进单个工程团队的头脑,也无法放进单个仪表盘。随着直播、播客和实时流增加,快速理解和更新系统变得紧迫;在时限内理解系统,常像被蒙眼在黑暗丛林里找一根针。

正文

2024 年 11 月,当 Jake Paul 和 Mike Tyson 走进拳台时,我们把这场比赛传送给了大约 6500 万个并发流。平常一天里,我们的基础设施会接收超过 20 亿次请求,每秒处理超过 3800 万个实时日志事件。在这种规模下,全貌既装不进任何一支工程团队的脑子,也放不上一块仪表盘。随着直播内容、播客和实时流越来越多,尽可能快地理解和更新系统,对我们提供良好用户体验变得至关重要。在这种规模和我们拥有的时间限制下理解系统,往往变成了一项大海捞针般令人畏惧的工作——而且是在漆黑的丛林里,还被蒙着眼睛。

Image 1: Netflix Technology Blog

我们要讲的,就是这次寻找那根针的过程。

修复前的四个小时

假设我们遇到某一周里常见的一类回归问题。主屏幕——上面是一个个电影列表组成的列表——开始比平时多花约 1.5 秒来完成渲染。警报触发。接着,我们就要去寻找是什么导致了这个问题,以及该如何修复它?

事实上,应用层的一个回归问题可能与系统中任何地方有关——新版本部署或 A/B 测试放量导致的客户端应用回归、基础设施中的负载削减、由于下游六跳之外的某个服务上线而导致 GraphQL 查询返回了错误数据,此时这些都是嫌疑对象。因此,回答“是什么导致了这个问题?”需要来自整个系统的洞察,而这又涉及以极其细致的方式梳理一层层数据。

MELT 层

“是什么导致了这个问题?”

指标、事件、日志和追踪,按业界的简称就是 MELT,是进行这类调查的合适原材料,它们每一个大约回答了问题的四分之一。我们围绕这四大支柱采集可观测性数据,并统一到单一的可观测性数据层中。这让我们能够实时观察系统:用户在经历什么、我们的应用程序运行得如何,以及后端基础设施和服务在处理和响应什么。

MELT——指标、事件、日志与追踪,以及它们是如何在系统中被采集的

接下来的调查在每一层中可能是这样展开的:

MELT 每一层告诉了我们什么,又遗漏了什么

调查的时间线

那么,调查一个问题、找出根本原因并修复它,从而让用户不再遇到这个问题,需要多长时间?

一次调查的时间线:约 4 小时

在我们选的这个例子里,是四个小时。其中只有极少部分花在计算什么难题上。大部分时间花在了重建各种关系上——这些关系我们的可观测性系统本来就知道,但我们从来没有把它们写在某个人类或机器可以直接读取和发现的地方。

底层的分歧

在 2023 年前后,我们的本能可能是去获取更多数据或做更好的仪表盘;虽然两者在边缘上都有帮助,但都没有触及让这些调查变慢的根本原因。

现在是 2026 年,所以我们可能会说“嘿,智能体”,但如果没有把正确的上下文提供给智能体,AI 系统就会产生幻觉;尤其是当团队正在值班、需要“立刻”修复时,我们不能让不准确的智能体分析把我们引上错误的道路。我们需要的是正确的那根针,而不是随便哪根针。

我们的每一个系统都是内部自洽、外部隔离的。Atlas(我们的遥测平台)知道应用及其时间序列。Radar(我们的告警系统)知道阈值和触发历史。incident.io 知道严重级别和响应者。PagerDuty 知道轮值安排。Spinnaker 知道什么在什么时候上线了。而我们的 Slack 历史则以一种任何查询引擎都无法触及的形式,知道上个季度是哪位工程师解决了非常类似的问题、以及他发现了什么。

难点在于,这些系统对它们在谈论什么并没有达成一致。在一个系统里是 application(应用)的东西,在第二个系统里是 service(服务),在第三个系统里是 cluster(集群),名称上可能差一个后缀、一种大小写约定,或者一次只有少数几个人记得的重命名(SME,想起来了吗?)。要跨这些系统做关联,就需要有一个人把映射关系记在脑子里,而这个人就成了这类跨域调查中的瓶颈。端到端可观测性中最难的部分,或许更适合被理解为一个数据建模问题。

我们缺少的,是一份关于运维世界中各种实体的、明确的、机器可读的约定。

本体

这份约定有个名字:本体(ontology)。本体是对类型、类型所拥有的属性,以及它们之间可能存在的关系的一种形式化规约。它的基本单位是三元组(triple),即把一个事实表示为主体、谓词、客体。请看下面这些三元组示例。

api-gateway rdf:type ops:Application . api-gateway ops:ownedBy "Team Bedrock" . api-gateway ops:deployedIn "us-east-1" . INC-5377 rdf:type ops:Incident . INC-5377 ops:affects api-gateway . INC-5377 ops:severity "SEV 2" .

在这个示例中,我们已经编码了六个事实,并且可以用一个查询回答一个过去需要跨三个工具才能回答的问题。“哪些事故影响了 Team Bedrock 在 us-east-1 拥有的应用?”现在只是一个查询,而不用再去查事故、Spinnaker 和 Radar。

我们把我们的本体层称为可观测性本体(Observability Ontology)。它充当模式、规则集和共享约定,用于标准化不同系统对应用、服务、事故、部署等实体的理解方式。

为此,我们把它构建在 W3C 技术栈之上:用 RDF 作为数据模型,OWL 作为本体,SHACL 做校验,SPARQL 做查询。这些标准规范完善且被广泛实现,因此模型可以在不同图存储之间移植。

本体自身的数据以知识图谱的形式存储,持久化到 QuipuDB——我们自研的 RDF 图数据库,一个基于 redb(Rust 编写的嵌入式键值数据库)的分布式四元组存储。这里的数据是通过把原始遥测、日志和事故讨论解析成三元组(主体-谓词-客体事实)而生成的。

每一次采集还会生成一份完整的 Turtle 序列化用于归档,使其人类可读且便于 diff,这在调试中的作用超出了我们预期。

我们的本体被组织为十二个命名空间,每个命名空间掌管一个运维领域:

本体的十二个命名空间,每个对应一个运维领域

在构建这个本体时,有两个决定很重要,值得分享。

第一个是,我们拒绝了一个通用的 Service(服务)类型。“Service”在我们的工具链中有四种不同含义,因此应用只能解析为唯一一种类型,即 ops:Application;并且在实体创建之前,每一个应用都会对照我们约 49,000 条记录的软件目录进行校验。像用户名、团队名、从网络拓扑中抓取的依赖名这类未经验证的字符串,永远不会变成应用。我们还在两个方向上对依赖边设置了上限,因为一个热门服务可能带有数千个反向依赖,否则就会主导整个图。

第二个决定关于我们选择建模的内容。事故聊天承载了一次事故背后的推理。尝试过又被否掉的假设、奏效的缓解措施、谁知道为什么,这些都是事故 Slack 频道里的上下文;而当频道安静下来,这些知识就蒸发了。因此,一个包含 200 条消息、50 个人、15 个服务的事故频道会变成一个结构,把一次事故的运维混沌转换成有序的三元组:

将包含 200 条消息、50 人、15 个服务的事故频道转化为有序三元组

从非确定性中创造确定性

构建这个图意味着要使用本质上具有随机性的工具。LLM 负责总结讨论,模糊匹配负责解析服务名,概率引擎负责提出因果链。这些工具都无法确定地两次产出相同结果,但我们系统最终输出的是一个确定性的图,它对同一个 SPARQL 查询的响应每次都一样。

本体正是通过其流水线方式创造这种确定性的机制。我们把这条流水线看作一个把熵不断压低的梯子,这里的熵指的是仍被困在非结构化形式中的信息比例。例如,在流水线起点,一个 LLM 可以说任何话,但它无法发出违反类型系统的三元组。我们把输出空间约束得足够紧,一个随机过程就会产生一份确定性的契约:

category IN ["incident", "question", "discussion", "decision"] sentiment IN ["positive", "neutral", "negative", "urgent"] thread:123 slack:category "incident" . thread:123 slack:isIncidentRelated "true"^^xsd:boolean .

沿着这个梯子往下走共有五层。梯子上的任何单一步骤都是非确定性的。但聚合起来,整条链就变成了确定性的。这种反转就是全部的诀窍。下面我们来描述这五层:

从非确定性中创造确定性的五个层级

富化器:作为熵降低者的智能体

我们使用所谓的富化器(enrichers)来降低整个知识图谱的熵。有几十个富化器在图上跑。每一个都接收噪声输入,发出带类型的三元组,并且每一个都会读取前面的富化器写入的内容。富化器的契约被刻意设计得很窄:

class Enricher: async def enrichasync(self, ...): raw = await self.fetch(...) result = self.analyze(raw) graph.add((uri, RDF.type, OPS.X)) graph.add((uri, OPS.pred, Literal(result.value))) return EnrichmentResult(success=True, triples_added=count)

富化器普遍遵循三条不变量:

  • 每个实体都带有 rdf:type;
  • 每个 URI 都遵循单一的命名方案;
  • 软件目录会把关每一个 ops:Application。

这里还有两个运维上的选择值得一提。富化流水线是顺序执行的——加载图、添加三元组、保存。这种顺序执行消除了一整类竞争条件,代价是墙上时钟时间的增加,而我们很乐意付这个代价。对外部系统(如指标、日志、事件等)的调用可以尽力而为,这样即使下游 API 很慢,也不会阻塞图的采集,尽管会有一定降级。为此,我们还在努力把这些独立的系统翻译成本体,比如指标本体(Metrics Ontology)和告警本体(Alerts Ontology),从而让一切都可以直接从本体中查询。

关联引擎:寻找关联链

关联引擎吸收来自不同输入源的大量信号,并把它们压缩成因果或相关的事件链。如今,引擎读取十三种信号类型,包括日志错误、指标异常、缓慢和失败的追踪、告警、事故、部署、配置变更、实验、自动扩缩容、提交、端到端瓶颈和错误模式。然后它在这些信号之间搜索路径。某些有序对已知具有因果关系,并带有较高的先验权重,例如部署先于日志错误、配置变更先于指标异常、失败追踪先于事故等等。另一些表现出高相关性的则被视为关联链。这些链会在十分钟窗口内根据时间邻近性和严重性打分,置信度低于 0.6 的会被丢弃。

关联引擎

引擎在这里的角色是带着证据提出假设,而不是下结论。因此,引擎会把它的链以及用于生成这些链的图记录一起存下来。

上面例子中的一条 0.87 链,是一个值得人类或智能体去核实的强假设;图会把置信度和主张一起记录下来,这样消费者就能理解一条链为何如此,并权衡各种选项来做决策。

知识飞轮

每一次采集循环都会转动同样的四个步骤,每转一圈,图就会为下一圈变得更丰富。这就是知识飞轮(Knowledge Flywheel),它带来关于系统的上下文的持续富化和改进,这些上下文存储在知识图谱中。

知识飞轮

Observe(观察)采集信号。Enrich(富化)添加带类型的三元组。Infer(推断)发现原始数据中看不见的模式。Adapt(适应)把知识反馈回去,使得已确立的模式变成检测规则。这四个阶段叠加的结果,带来了一个自我改进的系统。在一次事故中发现的因果链,会成为下一次事故的已知模式;本体也逐渐不再只是一个模式(schema),而开始成为一种记忆。

多轮循环后,事故模式沉淀为智慧与主动洞察

以上面的例子来说。当我们第一次看到 api-gateway 中因超时导致的事故并解决它时,我们会把这一知识转换成本体中的条目。这就成了已编码的知识。随着我们经历多轮这样的编码,图开始学习模式。N 轮之后,模式变成了智慧——“api-gateway 的部署导致了 40% 的 SEV-2”,以及“api-gateway 中 30% 的部署问题来自连接超时”。把这点作为洞察呈现出来,我们就能主动开始减少事故,而不只是快速响应新事故。

让图发挥作用

在图之上是 AutoRCA,我们内部的智能体 AI 方案,由一组围绕同一个问题协同工作的专家智能体构成。以前面提到的首页回归问题为例,这个智能体系统的推理链可能是这样的:

AutoRCA 中多个专家智能体基于同一本体词汇协作分析首页回归问题的推理链

针对输入的每一种专业化能力,都是由深刻理解各自领域的人构建的。这种分工只有在专家们能够把发现传递给彼此、而无需就通信术语达成一致、甚至无需知道对方存在的情况下才有效。本体充当了接口。每个专家只学一种词汇——图的词汇。一个为追踪使用方式编写专业用例的团队,把他们的发现记录为关于某个 ops:Application 的三元组;而日志专业化模块读取的是关于同一个 ops:Application 的三元组,双方都不需要知道对方存在。一个新专家只需学一次同样的词汇,就能立刻读懂其他六个专家写下的所有内容。

与我们开头提到的那次调查相比,变化比听上去要窄。警报依然在下午 3:30 触发,工程师依然必须权衡证据,修复依然得由懂代码的人来写。过去花在拼装上下文上的一两个小时——找到负责团队、重建上线了什么、关联系统不同部分的信息、回忆起上个季度发生过类似的事情——所有这些工作,正是本体和图吸收掉的部分。我们还处于早期,排序后的假设有时会出错,这就是为什么每条假设都带着产生它的证据。但现在当工程师开始调查时,他们手里已经握着过去要花半个下午才能拼出来的图景。

如果你也想构建一个这样的系统

我们所构建的东西里有相当一部分,是先做错、然后才学会的。基于这些经验,下面是我们在第二次尝试时会带上的原则:

  • 首先建模身份。 为每一类实体选择一个权威目录,并在创建实体前对照它进行校验。
  • 把本体当作代码。 对它做版本管理、评审、测试,并让变更以拉取请求(pull request)的形式到来。把本体当作代码,有利于智能体开发,也让团队能够规模化地贡献。
  • 把概念和实例分开。 概念以人类的时间尺度变化,实例则持续变化,把两者混在一起会让二者都更难演进。
  • 构建在开放标准之上。 RDF、OWL、SHACL 和 SPARQL 都有规范,被实现过很多次,而且可移植。定制的图模型会把未来的消费者绑死在某一种实现上。
  • 用类型系统约束随机性工具。 这是我们会最坚定捍卫的原则。模型恰恰在确定性不可能实现的地方有用;而一个足够窄的输出契约,能把它们的输出转换成查询引擎可以信任的东西。
  • 让富化过程尽力而为且有序。 一个缓慢的依赖最多只能让图降级,绝不能阻塞它。把富化器排序,让每一个都构建在前一个之上,能换来更高的准确性,这值得用牺牲并行带来的速度去换。
  • 在机器遥测之外,也对人类推理建模。 我们图中最有价值的边来自讨论。遥测记录系统做了什么,讨论记录人们理解和解读了什么。把推理存储下来,并在需要时作为智慧提供服务,更有价值。
  • 让人类担任审批者。 让智能体提出修复、富化器和本体变更的建议,并把每一个建议路由到有人类参与的评审流程中。

下一步是什么

这个图让三件事成为可能。首先是自动根因分析:当一个回归问题到来时,会附带一组由图谱支撑的、排序过的假设。其次是自动修复:当诊断有足够高的置信度时,会携带一个建议动作,比如定向回滚、流量切换、代码变更。最终则是能够自我修复的基础设施:在用户感受到之前,就针对预测到的故障采取行动。

与此同时,我们计划把可观测性本体与 Upper 集成,Upper 是 Netflix 统一数据架构(UDA)核心的领域建模语言。Upper 让团队把业务和系统领域描述为相互关联、机器可读的模型,并在这些模型被投影到具体模式和系统时,保留它们的含义和约束。这一集成会把应用、告警、部署、事故等运维概念,与 Netflix 范围内建模的更广泛领域连接起来,为工程师和智能体提供一个共享的语义层,用于查询、校验和推理。我们将从一个用 Upper 对事故建模的概念验证开始,并利用学到的东西来决定这两个系统应该在何处融合。

评论

多数评论者对 Netflix 这套可观测性本体持怀疑态度,认为它要么重蹈 XML/UML 式过度工程、缺乏落地证据,要么已可被能直接调用系统工具的 AI 智能体取代。少数人则纠正「本体」被窄化成三元组的误解,或谨慎期待它最终能把运维数据连到业务损益问题;而在该由人类还是 AI 来定义本体上,还存在让 AI 自行拼装与担忧机器削平表达力的分歧。

认为本体不只是三元组,三元组只是 OWL/RDF 的实现细节

  • 论点:文章把本体说成主谓宾三元组,其实只抓住了 OWL/RDF 这种特定形式化里的实现细节;本体在哲学中本是研究存在与关系的形而上学分支,进入计算机科学后,应是领域概念、属性、关系以及公理/约束的形式化模型,远比三元组丰富。(少数人)
  • 引用: > 本体被投射到计算机科学后,应是领域概念、属性、关系及公理约束的形式化模型,三元组只是某种形式化手段的实现细节。(@antonvs)

认为本体/知识图缺乏工程落地证据,仍只是学术练习

  • 论点:若没有真正降低延迟的实例,这就只是学术而非工程,原引言里的 1.5 秒问题是否被解决仍存疑;企业界过去十五年炒作本体和知识图,砸下大量顾问费和资金,却始终没起飞,关系型数据库及现代云实现仍主导数据格局,成功故事多只存在于学术练习中。(较多人)
  • 引用: > 在本体与知识图炒作十五年后,企业里的成功故事依然罕见,关系型数据库仍主导数据格局,多数只是学术练习。(@smartmic)

嘲讽这是又一次 XML/UML 式的过度工程与职业自肥

  • 论点:有人直接问这是不是 XML 和 UML 重演,暗示又一轮复杂建模炒作;更尖锐的批评认为 Netflix 技术栈过度工程,拿高薪的人为了配得上薪酬而制造这类怪物,行业人浮于事,没人真正创造客户价值,只是在为自己职业晋升生产过度设计的垃圾。(较多人)
  • 引用: > 拿着高额现金薪酬的人会觉得有必要创造出这类怪物来配得上自己的薪酬,行业人浮于事,没人真正知道如何为客户创造价值,只生产有利于自身职业晋升的过度设计垃圾。(@dominotw)

认为人工强制统一本体已过时,应让 AI 智能体直接调用工具来发现实用本体

  • 论点:集中身份与协调系统仍关键,但在整条流水线里强制推行本体已不合时宜;更该让 AI 对每个系统拥有工具调用和技能接口并积累记忆,亲自用命令行等干净接口询问系统状态,从而比人类预设的本体更好地拼出实际可用的本体。既然智能体能直接在 Unix 上用 grep、fetch 等命令检索抓取且准确率更高,知识图是否还有存在必要也成了疑问。(较多人)
  • 引用: > 只要让 AI 对每个系统做工具调用并积累记忆,它比人类预设的本体更能拼出实际可用的本体。(@nout)

担忧机器主导本体会把表达力削平到平均值

  • 论点:若为了更便宜地和机器通信而让机器来定本体,机器会挑训练数据里最常见的术语,把交流规范化,砍掉承载细微差别的高端表达,虽省 token 却丢失表达力。关联数据生态里的本体本可连接不同理解系统,但那并不 token-efficient;理想中每个领域都该用贴切文化的 OWL 表达,但现实可能做不到,最后只会退到「shape」。(少数人)
  • 引用: > 机器会挑选训练数据中最常见的术语来定义本体,从而把交流规范化并削掉承载细微差别的顶层表达。(@threecheese)

谨慎期待本体最终能连接运维数据与业务损益

  • 论点:Netflix 文章末尾的愿景,是把可观测性本体与业务人员、数据工程团队使用的系统连起来,从而回答“某个运维或工程改动赚了或亏了多少钱”这类难题——至少理论上如此。可观测性厂商十多年来一直宣称运维数据里有未挖掘的业务价值,现在或许才接近检验它是否真实。(少数人)
  • 引用: > 把可观测性本体接到业务与数据工程系统上,是为了回答“某次工程改动到底赚了还是亏了多少钱”这类老难题。(@smithclay)

有价值的补充

  • 「本体」一词原本是哲学形而上学分支,研究存在的本质与关系,并非计算机科学原生术语;OWL/RDF 把它投影成形式化模型后,三元组只是其中一种表示手段。
  • threecheese 结尾的「Shape it is」暗指落地时可能只会采用 SHACL/ShEx 这类「形状」约束(只校验 RDF 数据结构),而不是表达能力更强的 OWL 公理本体。
  • nout 提到的集中身份并不只是分配 ID,还需要强大的 reconciliation 系统来处理合并、拆分、身份漂移,并能把每个三元组回溯到来源系统。