6.2.1 通用模式
日志的关键挑战是在所有组件中采用统一的消息格式,由于集成在一个复杂系统中的各种组件往往是独立开发的,因此这一要求变得复杂起来。Fluentbit 通过支持一组过滤器在规范这些消息方面发挥了作用。这些过滤器解析由组件编写的"原始"日志信息(ASCII 字符串),并将"规范的"日志信息输出为结构化的 JSON。虽然还有其他选择,但 JSON 作为文本是合理可读的,这对于人类调试仍然很重要,同时也得到了工具的良好支持。
例如,SD-Fabric 组件的开发人员可能会写这样的日志消息:
2020-08-18 05:35:54.842Z INFO [DistributedP4RuntimeTableMirror] Synchronized TABLE_ENTRY mirror for device:leaf1: 0 removed, 2 updated, 4 added
Fluentbit 过滤器将其转换成如下结构:
{ "time": "2020-08-18 05:35:54.842Z", "logLevel": "INFO", "component": "DistributedP4RuntimeTableMirror", "log": "Synchronized TABLE_ENTRY mirror for device:leaf1: 0 removed, 2 updated, 4 added" }
这是一个简化的例子,但确实有助于说明基本的想法,同时还强调了 DevOps 团队在建立管理平台时面临的挑战,即为整个系统决定一套有意义的名/值对。换句话说,必须为这些结构化的日志信息定义通用模式。Elastic 通用模式(Elastic Common Schema) 是一个很好的开始,除此之外,还有必要建立一套公认的日志级别,以及使用每个级别的惯例。例如,在 Aether 中,日志级别是: FATAL, ERROR, WARNING, INFO, 以及 DEBUG。
延伸阅读:
6.2.2 最佳实践
当然,建立一个共享日志平台没有什么价值,除非所有单独的组件都被正确的检测以写入日志消息。编程语言通常带有编写日志消息的库支持(例如 Java 的 log4j),但这只是一个开始。只有当组件遵循以下最佳实践时,日志才是最有效的。
- 平台处理日志传送。组件应该假设 stdout/stderr 被 Fluentbit(或类似工具)吸收到日志记录系统中,并避免通过尝试路由自己的日志使工作变得更复杂。除了管理平台无法控制的外部服务和硬件设备,在部署过程中,必须确定这些系统如何将日志发送到日志聚合器。
- 应该禁用文件日志。将日志文件直接写入容器的分层文件系统被证明是 I/O 效率低下的,并可能成为性能瓶颈。如果日志也被发送到 stdout/stderr,通常也没有必要这样做。通常,当组件在容器环境中运行时,不鼓励将日志记录到文件中。相反,组件应该将所有日志传输到收集系统中。
- 鼓励异步日志。在可伸缩环境中,同步日志可能成为性能瓶颈,组件应该异步写入日志。
- 时间戳应该由程序的日志记录器创建。组件应该使用所选的日志库来创建时间戳,并且使用日志框架所允许的尽可能精确的时间戳。由应用程序或日志处理程序记录时戳可能会比较慢,在接收时创建时间戳也可能会产生延迟,从而使得在日志聚合之后在多个服务之间对齐事件时产生问题。
- 必须能够在不中断服务的情况下更改日志级别。组件应该提供在启动时设置日志级别的机制,以及允许在运行时更改日志级别的 API。基于特定子系统确定日志级别的范围是一个有用的特性,但不是必需的。当一个组件由一组微服务实现时,日志配置只需应用于一个实例,就可以应用于所有实例。
6.3 分布式跟踪
运行时跟踪是遥测数据的第三个来源。在云环境中,由于需要跟踪跨多个微服务的每个用户发起的请求的控制流,因此是一个挑战。好消息是,可以通过微服务的底层语言运行时系统(通常在 RPC stub 中)中激活跟踪支持,而不需要应用程序开发人员在程序中支持。
一般模式类似于我们已经看到的指标和日志: 代码执行过程中生成数据,然后收集、聚合、存储这些数据,并用于显示和分析。主要区别在于我们想要收集的数据类型,对于跟踪来说,通常是 API 从一个模块到另一个模块的调用序列,这些数据为我们提供了重建调用链所需的信息。原则上,我们可以利用日志系统跟踪,只需要勤奋的记录必要的接口调用信息,但这是一个通用用例,足以保证拥有自己的词汇表、抽象和机制。
在较高的级别上,跟踪(trace) 描述了事务在系统中的移动,由一系列 span(每个 span 表示在服务中完成的工作)和一组 span 上下文(span context)(每个 span 上下文表示通过网络从一个服务到另一个服务的状态)交织组成。图 28 中显示了一个跟踪示例,但从抽象意义上讲,跟踪是一个有向图,其中节点对应于 span,而边对应于 span 上下文。然后,节点和边被打上时间戳,并用端到端执行路径的相关事实(键/值标签)进行注释,包括何时运行以及运行了多久。每个 span 还包括在执行过程中生成的带时间戳的日志消息,从而简化了将日志消息与跟踪关联的过程。
图 28. 横跨两个网络服务的跟踪示例。
同样,就像指标和日志信息一样,细节很重要,而这些细节是由一个商定的数据模型来指定的。OpenTelemetry 项目现在正在定义这样的模型,它建立在早期 OpenTracing 项目之上(该项目又受到 Google 开发的 Dapper 分布式追踪机制的影响)。除了定义一个能够捕获最相关的语义信息的模型这一挑战之外,还有一个务实的问题: (1)尽量减少跟踪的开销,以免对应用程序性能产生负面影响,但(2)从跟踪中提取足够的信息,以便使收集这些信息变得有价值。采样是一种被广泛采用的技术,因此被引入到数据收集流水线中来管理取舍。这些挑战的一个结果是,分布式跟踪是正在进行的研究主题,我们可以期待模型定义和采样技术在可预见的未来不断发展和成熟。
延伸阅读:
B. Sigelman, et al. Dapper, a Large-Scale Distributed Systems Tracing Infrastructure. Google Technical Report. April 2010.
OpenTelemetry: High-quality, ubiquitous, and portable telemetry to enable effective observability.
在机制方面,Jaeger 是一个被广泛使用的开源跟踪工具,最初由 Uber 开发(Jaeger 目前不包含在 Aether 中,但在 ONF 的前身边缘云中使用)。Jaeger 包含了用于实现应用程序的语言的指令、收集器、存储,以及用于诊断性能问题并进行根因分析的查询语言。
6.4 集成仪表板
仪表化应用软件生成的指标、日志和跟踪使得收集有关系统健康状况的大量数据成为可能。但是,只有在正确的时间(需要采取行动的时候)将正确的数据显示给正确的人(那些有能力采取行动的人)时,这种工具才有用。创建有用的面板并将其组织成直观的仪表板是解决方案的一部分,但跨管理平台的子系统集成信息也是必要的。
统一所有这些数据是正在进行的工作的最终目标,比如上一节提到的 OpenTelemetry 项目,但也有机会使用本章介绍的工具来更好的集成数据。本节重点介绍两种一般策略。
首先,Kibana 和 Grafana 都可以配置为显示来自多个来源的遥测数据。例如,可以直接在 Kibana 中集成日志和跟踪。通常首先将跟踪数据输入到 ElasticSearch,然后由 Kibana 进行查询。类似的,如果有一种方便的方式来查看已收集的指标上下文中与特定组件关联的日志消息,就会很有用。而这很容易完成,因为可以配置 Grafana 显示来自 ElasticSearch 的数据,就像显示来自 Prometheus 的数据一样简单,两者都是可以查询的数据源。这使得创建包含一组选定的日志消息的 Grafana 仪表板成为可能,类似于图 29 中所示的来自 Aether 的消息。在这个例子中,我们看到与 SD-Core 的 UPF 子组件相关联的 INFO 级消息,扩充了图 26 所示的 UPF 性能数据。
图 29. 日志消息与 SD-Core 的 UPF 组件显示在 Grafana 仪表板上。
其次,第 5 章中介绍的运行时控制界面提供了改变运行中系统的各种参数的方法,但要想做出明智的决定,获得所需数据以知道需要做哪些改变(如果有的话)是其前提条件。为此,理想做法是在一个综合仪表板上既能访问"按钮"又能访问"刻度盘"。可以通过在运行时控制 GUI 中加入 Grafana 框架来实现,在其最简单的形式下,可以显示一组与底层数据模型字段相对应的 Web 表单(更复杂的控制面板当然也是可能的)。
图 30.示例控制仪表板显示了为虚构的 Aether 站点集合定义的设备组集合。
例如,图 30 显示了一组虚构的 Aether 站点的当前设备组,单击"Edit"按钮将弹出一个 web 表单,该表单允许企业 IT 管理员修改 Device-Group 模型的相应字段(未显示),单击"Monitor"按钮将弹出类似于图 31 所示的 Grafana 生成的框架。原则上,这个框架是经过裁剪的,只显示与所选对象最相关的信息。
图 31. 选择 Device Group 关联的监控帧示例。
6.5 可观测性
知道收集什么样的遥测数据,以便在需要的时候得到准确的信息,但这样做又不会对系统性能产生负面影响,这是一个困难的问题。可观测性(Observability) 是一个相对较新的术语,被用来描述这个一般的问题空间,虽然这个术语被认为是最新的市场流行语(确实如此),但也可以被解释为所有好的系统都渴望的另一个"特性",与可扩展性(scalability)、可靠性(reliability)、可用性(availability)、安全性(security)、易用性(usability)等等并列。可观测性是一个系统的质量,可以让人们看到其内部运行的事实,从而做出明智的管理和控制决策。这已经成为肥沃的创新空间,因此我们以两个可能在不久的将来成为普遍现象的例子来结束本章。
首先是带内网络遥测(INT, Inband Network Telemetry) ,它利用可编程交换硬件的优势,使运营商能够在数据包流经网络时,提出关于数据包如何被"带内"处理的新问题。这与依赖硬接入固定功能网络设备的预定义计数器集,或仅能检查数据包的抽样子集形成对比。由于 Aether 使用可编程交换机作为其基于 SDN 的交换结构的基础,能够使用 INT 作为第四种遥测数据,并以此对流量模式和网络故障的根本原因提供定性的更深入的见解。
例如,INT 已经被用来测量和记录单个数据包在沿着端到端路径穿越一连串交换机时所经历的排队延迟,从而有可能检测到微爆(microbursts, 以毫秒甚至亚毫秒的时间尺度测量的排队延迟)。甚至有可能将这一信息与遵循不同路径的数据包流联系起来,以确定哪些流在每个交换机上共享缓冲容量。作为另一个例子,INT 已经被用来记录指导数据包如何交付的决策过程,也就是说,在端到端路径上的每个交换机都应用了哪些转发规则。这为使用 INT 来验证数据平面是否忠实执行网络运营商所期望的转发行为打开了大门。关于 INT 的更多信息,请参考我们的 SDN 配套书籍。
延伸阅读:
L. Peterson, et al. Software-Defined Networking: A Systems Approach. November 2021.
其次是第一章中提到的服务网格(Service Mesh) 的出现。像 Istio 这样的服务网格框架提供了一种通过在微服务之间注入"观察/执行点"来执行细粒度安全策略并收集云原生应用遥测数据的手段。这些注入点被称为边车(sidecar) ,通常由一个容器来实现,该容器在实现每个微服务的容器旁边运行,从服务 A 到服务 B 的所有 RPC 调用都要通过相关的 sidecar。如图 32 所示,这些 sidecar 实现运营商想要施加在应用程序上的任何策略,将遥测数据发送到全局收集器并从全局策略引擎接收安全指令。
图 32. 服务网格框架概述,通过 sidecar 拦截在服务 A 和服务 B 之间流动的消息。每个 sidecar 执行从中央控制器接收到的安全策略,并向中央控制器发送遥测数据。
从可观测性的角度来看,sidecar 可以被编程以记录运营商可能想要收集的任何信息,原则上甚至可以根据条件的需要动态更新。这为运维人员提供了一种定义系统观测方式的通用方法,而不需要依赖开发者在服务中包含指令。缺点是,sidecar 在服务间的通信上带来了不可忽视的开销。由于这个原因,替代 sidecar 的方法越来越多,特别是 Cilium,它使用 eBPF(扩展的伯克利包过滤器, extended Berkeley Packet Filters)在内核内而不是在 sidecar 中实现可观测性、安全性和网络数据平面特性。
关于 Istio 服务网格的更多信息,我们推荐 Calcote 和 Butcher 的书,同时 Cilium 项目在其网站上有大量的文档和教程。
延伸阅读:
L. Calcote and Z. Butcher Istio: Up and Running. October 2019.
关于本书
深入浅出边缘云(Edge Cloud Operations: A Systems Approach) 的源代码可在 GitHub 上根据创作共用许可协议(CC BY-NC-ND 4.0)获得。邀请社区在相同条款下提供更正、改进、更新和新材料。虽然本授权并不自动授予制作衍生作品的权利,但我们非常希望与感兴趣的各方讨论衍生作品(如翻译)。请联系 discuss@systemsapproach.org。
如果使用了该作品,应包括以下版权信息:
Title: Edge Cloud Operations: A Systems Approach
Authors: Larry Peterson, Scott Baker, Andy Bavier, Zack Williams, Bruce Davie
Source: https://github.com/SystemsApproach/ops
License: CC BY-NC-ND 4.0
阅读本书
本书是系统方法系列的一部分,其在线版本发布在https://ops.systemsapproach.org。
要跟踪进度并接收关于新版本的通知,可以在Facebook和Twitter上关注这个项目。要阅读关于互联网发展的评论,可以关注系统方法Substack。
构建本书
要构建可供网页浏览的版本,首先需要下载源码:
$ mkdir ~/systemsapproach $ cd ~/systemsapproach $ git clone https://github.com/SystemsApproach/ops.git $ cd ops
构建过程实现在 Makefile 中,并且需要安装 Python。Makefile 将创建一个虚拟环境(venv-docs
),用于安装文档生成工具集,可能还需要使用系统的包管理器安装enchant
C 库,以便拼写检查器正常工作。
请运行make html
,在_build/html
中生成 HTML。
请运行make lint
检查格式。
执行make spelling
检查拼写。如果有拼写正确但字典中没有的单词、名称或首字母缩写词,请添加到dict.txt
文件中。
请运行make
查看其他可用的输出格式。
向本书投稿
如果你使用这些材料,希望也愿意给出回馈。如果你是开放源码的新手,可以查看How to Contribute to Open Source指南,你将学到如何发布 Issue,如何发出 Pull Request 合并改进,以及其他一些主题。
如果你想投稿,并且正在寻找一些需要关注的内容,请查看wiki上的当前待办事项列表。
关于作者
Larry Peterson 是普林斯顿大学计算机科学系 Robert E. Kahn 名誉教授,并从 2003 年到 2009 年担任主席。Peterson 教授的研究主要集中在互联网大规模分布式系统的设计、实现和操作,包括广泛使用的 PlanetLab 和 MeasurementLab 平台。他目前正在为开放网络基金会(ONF)的 Aether 接入边缘云项目做出贡献,并担任首席科学家。Peterson 是美国国家工程院院士,ACM 和 IEEE 院士,2010 年 IEEE Kobayashi 计算机与通信奖得主,2013 年 ACM SIGCOMM 奖得主。Peterson 拥有普渡大学博士学位。
Scott Baker 是英特尔云软件架构师,在英特尔收购开放网络基金会(ONF)工程团队时加入英特尔。在 ONF 期间,他领导了 Aether DevOps 团队。在 ONF 之前,曾在普林斯顿大学和亚利桑那大学从事云相关的研究项目,包括 PlanetLab、GENI 和 VICCI。Baker 于 2005 年获得亚利桑那大学计算机科学博士学位。
Andy Bavier 是英特尔云软件工程师,在英特尔收购开放网络基金会(ONF)工程团队时加入英特尔。在 ONF 期间,他参与了 Aether 项目。在加入 ONF 之前,他是普林斯顿大学的研究科学家,从事 PlanetLab 项目。1990 年在威廉玛丽大学获得哲学学士学位,1995 年在亚利桑那大学获得计算机科学硕士学位,2004 年在普林斯顿大学获得计算机科学博士学位。
Zack Williams 是英特尔云软件工程师,在英特尔收购开放网络基金会(ONF)工程团队时加入英特尔。在 ONF 期间,他参与了 Aether 项目,并领导了基础设施团队。在加入 ONF 之前,他是亚利桑那大学的系统程序员。Williams 于 2001 年获得亚利桑那大学计算机科学学士学位。
Bruce Davie 是计算机科学家,因其在网络领域的贡献而闻名。他是 VMware 亚太区的前副总裁兼首席技术官,在 VMware 收购 SDN 初创公司 Nicira 期间加入 VMware。在此之前,他是 Cisco Systems 研究员,领导一个架构师团队,负责多协议标签交换(MPLS)。Davie 拥有超过 30 年的网络行业经验,并合著了 17 个 RFC。他于 2009 年成为 ACM 研究员,并于 2009 年至 2013 年担任 ACM SIGCOMM 主席。他还在麻省理工学院做了五年的访问讲师。Davie 是多本书的作者,拥有 40 多项美国专利。