返回

Netflix规模下的本体驱动可观测性:构建端到端知识图谱

RSS Netflix Technology Blog AI 逐段翻译 发布 2026-10-17 00:01
DataHot 速览

Netflix工程博客介绍其超大规模可观测性实践:2024年11月一场直播支持约6500万并发流,日常基础设施每秒接收超20亿请求和超3800万实时日志事件。面对首页渲染变慢约1.5秒等回归,团队将Metrics、Events、Logs、Traces(MELT)统一为可观测性数据层。文章进一步介绍如何用本体驱动的端到端知识图谱,在复杂系统中定位根因并加快系统理解与更新。

为什么值得关注:对构建大规模数据平台、可观测性体系和知识图谱/本体建模的团队有直接参考价值,涉及MELT数据整合、根因定位与语义一致性。

本文目录 13 节
  1. 修复前的四个小时
  2. MELT层
  3. 一次调查的时间线
  4. 底层的不一致
  5. 本体
  6. 从非确定性中创造确定性
  7. 富化器:作为熵减少器的智能体
  8. 关联引擎:寻找关联链
  9. 知识飞轮
  10. 让图谱发挥作用
  11. 如果你想构建一个这样的系统
  12. 接下来会怎样
  13. 致谢

译文

AI 逐段翻译

Prasanna Vijayanathan,Renzo Sanchez-Silva

2024年11月,当Jake Paul和Mike Tyson踏入拳击台时,我们将这场比赛送达到了大约6500万个并发流。在平常的一天里,我们的基础设施接收超过20亿次请求,每秒处理多达3800万个实时日志事件。在这样的规模下,整体图景既装不进单个工程团队的脑子里,也放不进一块仪表盘中。随着更多直播内容、播客和实时流的出现,我们尽快理解和更新系统的需求变得至关重要,这样才能提供良好的用户体验。在这种规模下、在有限的时间约束内理解系统,往往变成一项如同蒙着眼睛、在漆黑丛林中大海捞针般令人生畏的任务。

这篇文章讲的就是那次寻找那根针的过程。

修复前的四个小时

设想一下我们在某一周会遇到的那类回归问题。你看到电影列表的列表的那个主屏幕,开始需要多花大约1.5秒来渲染。一个告警触发了。然后我们就去寻找是什么导致了这个间题,以及我们该如何修复它?

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

MELT层

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

指标、事件、日志和追踪,用行业里的简写来说就是MELT,是这类调查的正确原材料,它们各自大致回答了该问题的四分之一。我们在四大支柱上收集可观测性数据,统一到单个可观测性数据层中。这让我们能够实时观察我们的系统,跨用户正在经历什么、我们的应用表现如何,以及我们的后端基础设施和服务正在处理什么并响应什么。

接下来的调查在每一层可能看起来是这样的:

一次调查的时间线

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

在我们选定的例子中,四个小时。其中极少部分花在计算任何困难的事情上。大部分时间花在重建那些我们的可观测性系统早已知道、但我们从未以任何人类或机器可以直接阅读并发现的形式写下来的关系上。

底层的不一致

这里的本能反应本可能是,在2023年前后,去获取更多数据或更好的仪表盘,而尽管两者在边缘上有帮助,它们都没有解决让这些调查变得缓慢的根源。

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

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

困难之处在于,它们对于自己谈论的东西并没有一致的看法。一个在某个系统中是application的东西,在第二个系统中是service,在第三个系统中是cluster,名称相差一个后缀、一种大小写约定,或者一次只有少数几个人记得的重命名(领域专家,耳熟吗?)。跨它们进行关联需要一个人把映射关系记在脑子里,而那个人就成了这类跨域调查中的瓶颈。端到端可观测性的难点,也许更好地被理解为一个数据建模问题。

我们所缺乏的是对我们运营世界中实体的一个明确的、机器可读的一致约定。

本体

这种约定有一个名字:一个本体。本体是对类型、它们所携带的属性以及它们可能拥有的关系的形式化规范。它的单位是三元组,即一个事实,表达为主语、谓语、宾语。考虑以下示例三元组。

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" .

在这里的例子中,我们已经编码了六个事实,并且可以在一次查询中回答一个过去需要跨越三个工具的问题。

“哪些事件影响了us-east-1中Team Bedrock拥有的应用?”是一次单一查询,取代了查询incidents、Spinnaker和Radar。

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

我们为此把它构建在W3C技术栈上:RDF用于数据模型,OWL用于本体,SHACL用于验证,SPARQL用于查询。这些标准被良好地规范并被广泛实现,这使模型能够跨图存储可移植。

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

每次采集还会生成一份完整的Turtle序列化用于归档,使其人类可读且对差异友好,这已被证明在调试中比我们预期更有用。

我们的本体被组织为十二个命名空间,每个命名空间拥有一个运营领域:

关于这个本体,有两个决策在我们构建它时至关重要,所以值得分享。

第一个是我们拒绝了一个通用的 Service 类型。“Service”在我们的工具链中意味着四种不同的东西,因此应用程序精确解析为一种类型:ops:Application,并且在实体创建之前,每一个都会根据我们约 49,000 条目的软件目录进行验证。未经校验的字符串,例如用户名、团队名、从网络拓扑中抓取的依赖名,永远不会成为应用程序。我们还在两个方向上限制依赖边的数量,因为一个热门服务可能承载数千个反向依赖,否则会主导整个图。

第二个决策涉及我们选择建模的内容。事故聊天承载了推理,它位于事故背后。尝试过并已放弃的假设、奏效的缓解措施、谁知道原因,这些都是事故 Slack 频道中的上下文,而当频道沉寂下来时,这些知识就蒸发了。因此,一个 200 条消息、50 人、15 个服务的事故频道会变成一种结构,将事故中的运营混乱转换为有序三元组:

从非确定性中创造确定性

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

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

# The model may produce any prose. Its output MUST fit these predicates:
category  IN ["incident", "question", "discussion", "decision"]
sentiment IN ["positive", "neutral", "negative", "urgent"]

# Result — a deterministic triple:
thread:123  slack:category          "incident" .
thread:123  slack:isIncidentRelated "true"^^xsd:boolean .

有五层让我们沿着这架梯子下降。这架梯子的任何单独一步都是非确定性的。但总体上,这条链会变得确定。这种反转就是整个诀窍。我们下面来描述它们:

富化器:作为熵减少器的智能体

我们使用所谓的富化器来减少整个知识图中的熵。数十个富化器在图上运行。它们中的每一个都接收嘈杂输入并发出类型化三元组,并且每个都读取更早的富化器写下的内容。富化器的契约被刻意设计得很窄:

class Enricher:
    async def _enrich_async(self, ...):
        raw    = await self.fetch(...)      # 1. read noisy input
        result = self.analyze(raw)          # 2. process (may be stochastic)
        graph.add((uri, RDF.type, OPS.X))   # 3. emit TYPED triples
        graph.add((uri, OPS.pred, Literal(result.value)))
        return EnrichmentResult(success=True, triples_added=count)

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

  • 每个实体都带有一个 rdf:type,
  • 每个 URI 都遵循单一命名方案,以及
  • 目录对每个 ops:Application 进行把关。

这里也有两个运营选择值得点名。富化流水线按顺序运行——加载图、添加三元组并保存。

这种顺序化消除了一整类竞态条件,代价是墙钟时间,而我们乐意付出这个代价。并且,对外部系统(如指标、日志、事件等)的调用可以是尽力而为的,所以缓慢的下游 API 不会阻塞图采集,尽管会有一些退化。为此,我们也在努力将这些分离的系统转换到本体中,例如指标本体和告警本体,这样一切都可以从本体本身进行查询。

关联引擎:寻找关联链

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

该引擎在这里的角色是有证据地提出假设,而不是下结论。因此,引擎会存储它的链以及用于创建这些链的图记录。

一个 0.87 链在上面的例子中,是一个供人或智能体检查的强假设,并且图会把置信度与声明一起记录下来,这样消费者就能理解某条链为何如此,并权衡自己的选择来做出决策。

知识飞轮

每个采集周期都会转动同样的四步,而每一轮旋转都会让图对下一轮更加丰富。这就是知识飞轮,它带来了关于我们系统上下文的持续富化和改进,并存储在知识图中。

观测收集信号。富化添加类型化三元组。推理发现原始数据中不可见的模式。适应把知识反馈回去,使已建立的模式变成检测规则。这四个阶段的复利结果带来一个自我改进的系统。在一次事故中发现的因果链会成为下一次的已知模式,而本体也渐渐不再是模式,而开始成为记忆。

考虑上面的例子。当我们第一次看到 api-gateway 中由超时导致的事故并解决它时,我们会把这些知识转换为本体条目。这就成为被编码的知识。随着我们经历这种编码的多个循环,图开始学习模式。经过 N 个循环后,模式变成智慧——“api-gateway 部署导致 40% 的 SEV-2” 以及 “api-gateway 中 30% 的部署问题来自连接超时”将其作为洞见呈现出来,我们就能主动开始减少事故,而不仅仅是对新出现的事故快速做出反应。

让图谱发挥作用

在图谱之上是AutoRCA,这是我们内部的智能体AI解决方案,由一组专家智能体围绕一个问题协同构建。回顾本文开头的主页回归问题,该智能体系统的推理链大致如下:

对输入的每一项专业化处理都由深入理解各自领域的人构建。这种分工只有在专家们能够将发现传递给彼此、而无需就沟通术语达成一致、甚至无需知晓彼此存在的情况下才能奏效。本体充当了接口。每位专家学习同一套词汇——图谱的词汇。一个团队为他们如何使用追踪编写专门用例,将其发现记录为关于某个ops:Application的三元组,日志专业化模块读取关于同一个ops:Application的三元组,而两者都不需要知道对方的存在。一位新专家只需学习一次相同的词汇,就能立即读取其他六位专家所写的一切。

与我们开头所描述的那次调查相比,这一变化比听起来要小。警报仍然在下午3:30触发,工程师仍然必须权衡证据,修复仍然必须由理解代码的人来编写。过去用于汇集上下文的那一两个小时——找到负责团队、重建已发布的内容、关联来自系统不同部分的信息、回想起上个季度发生过类似的事情——所有这些工作正是本体和图谱所吸收的部分。我们还处于早期阶段,排名靠前的假设有时是错误的,这就是为什么每一个假设都附带了产生它的证据。但当工程师现在开始调查时,他们已经掌握了过去需要半个下午才能拼凑出的全貌。

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

我们所构建的很多东西,都是先做错才学会的。基于这些经验,以下是我们会在第二次尝试中遵循的原则。

  • 先建模身份,再建模其他一切。为每种实体类型选择一个权威目录,并在创建实体之前依据它进行验证。
  • 将本体视为代码。对其版本化、审查、测试,并让变更以拉取请求的形式到来。将本体视为代码有利于智能体式开发,并让团队能够扩展和贡献。
  • 将概念与实例分离。概念按人类时间尺度变化,而实例持续变化,将两者混为一谈会使两者的演进都更加困难。
  • 构建在开放标准之上。RDF、OWL、SHACL和SPARQL都有规范、有多次实现,且可移植。定制的图模型会将未来的消费者绑定到某一种实现上。
  • 用类型系统约束随机性工具。这是我们会最坚决捍卫的原则。模型恰恰在确定性不可能的地方有用,而一个足够狭窄的输出契约能将它们的输出转化为查询引擎可以信任的东西。
  • 让富化尽力而为且有顺序。缓慢的依赖最多只应降级图谱,绝不应阻塞它。并且让富化器按顺序排列,使每一个都建立在前一个之上,能换来更高的准确性,这值得用并行所带来的速度作为权衡。
  • 将人类推理与机器遥测一并建模。我们图谱中最有价值的边来自讨论。遥测记录系统做了什么,讨论记录人们理解和解读了什么。这些推理更值得存储,并在我们需要时作为智慧来提供。
  • 让人类保持审批者角色。让智能体提出修复、富化器和本体的变更,并将每一个提案都通过有人类参与的审查流程。

接下来会怎样

图谱使三件事成为可能。第一,自动根因分析,即回归问题出现时附带一组有图谱支撑的排序假设。接下来,自动修复,即足够确信的诊断会附带一个建议操作,如针对性回滚、流量切换、代码变更。最终是能够自我修复的基础设施,在成员感受到之前就对预测到的故障采取行动。

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

致谢

如果没有那些与早期系统分享数据、专业知识和耐心的团队,这一切都不会存在。我们要特别感谢我们自己的团队成员,他们在这个生命周期的早期就使这一切成为可能,Kaela Marsheck和Aditya Gaur。我们也特别感谢Netflix本体工作组的合作者Peter Royal、Alex Hutter,以及Alexandre Bertails和统一数据架构(UDA)/知识基础团队的成员,感谢他们分享Upper及其领域建模专业知识,并帮助我们探索可观测性本体如何成为Netflix更广泛语义基础的一部分。

本体驱动的可观测性:在Netflix规模上构建端到端知识图谱最初发表于Netflix TechBlog(Medium),人们正在那里通过高亮和回应这个故事来继续对话。

这篇内容对你有用吗?

反馈只用于改善内容筛选,不等同于收藏

分享这条资讯
分享海报
保存图片
iOS 也可以长按图片保存