微服务架构得到了广泛的部署与应用, 提升了软件系统开发的效率, 降低了系统更新与维护的成本, 提高了系统的可扩展性. 但微服务变更频繁、异构融合等特点使得微服务故障频发、其故障传播快且影响大, 同时微服务间复杂的调用依赖关系或逻辑依赖关系又使得其故障难以被及时、准确地定位与诊断, 对微服务架构系统的智能运维提出了挑战. 服务依赖发现技术从系统运行时数据中识别并推断服务之间的调用依赖关系或逻辑依赖关系, 构建服务依赖关系图, 有助于在系统运行时及时、精准地发现与定位故障并诊断根因, 也有利于如资源调度、变更管理等智能运维需求. 首先就微服务系统中服务依赖发现问题进行分析, 其次, 从基于监控数据、系统日志数据、追踪数据等 3 类运行时数据的角度总结分析了服务依赖发现技术的技术现状; 然后, 以基于服务依赖关系图的故障根因定位、资源调度与变更管理等为例, 讨论了服务依赖发现技术应用于智能运维的相关研究. 最后, 对服务依赖发现技术如何准确地发现调用依赖关系和逻辑依赖关系, 如何利用服务依赖关系图进行变更治理进行了探讨并对未来的研究方向进行了展望.
目录
1 问题描述
2 服务依赖发现
2.1 基于监控数据的服务依赖发现
2.1.1 基于网络通信包数据的服务依赖发现
2.1.2 基于资源使用数据的服务依赖发现
2.1.3 基于统计指标的服务依赖发现
2.2 基于系统日志的服务依赖发现
2.2.1 依据统一标识的服务依赖发现
2.2.2 基于共现概率的服务依赖发现
2.2.3 基于日志频率的服务依赖发现
2.3 基于追踪数据的服务依赖发现
1 问题描述
● 服务. 在微服务架构软件系统中, 服务即指微服务. 但在已有的服务依赖发现相关研究工作中, 并没有一个通用且标准的关于服务的定义, 所以在不同的研究工作中, 服务依赖发现中的“服务”的具体含义可能有所不同, 但基本可以划分为 3 类: 由 IP 和 Port 代表的服务, 组件或者应用, 虚拟机. 在文献中, 服务或被定义为<IP,Port> 这样的二元组, 或被定义为<IP, Port, Protocol> 的三元组. 在文献中, 服务即组件/应用, 组件是分布式软件系统中可被独立部署的最小单元. 而将虚拟机作为服务依赖发现的研究对象时, 通常是基于假设: 每个虚拟机中仅部署一个服务, 因此虚拟机之间的依赖关系也就代表服务之间的依赖关系. 在一对服务依赖关系中, 服务按照是依赖的一方还是被依赖的一方可以划分为依赖服务 (depending service) 和被依赖服务 (depended service).
● 依赖. 服务依赖发现中的依赖关系有两种, 调用依赖关系 (local-remote dependency) 和逻辑依赖关系 (remote-remote dependency). 调用依赖关系指一个服务 为完成对该服务的请求的响应, 对其他服务如 的调用关系,是微服务系统中最常见的依赖关系. 如图 2 所示在一个典型的开源微服务系统中所发现的部分服务依赖关系中, CheckoutService 为完成结账服务, 会分别调用CartService、PaymentService 和 ShipmentService 完成下单、支付和邮寄功能, 那么CheckoutService 依赖于 CartService、PaymentService 以及 ShipmentService, 依赖类型为调用依赖. 逻辑依赖关系是指一个服务 完成对该服务的请求响应是以另一个服务 完成对指定请响应为前提的逻辑先后关系. 如图 2 所示的服务依赖关系中, ShipmentService 为完成邮寄服务, CheckoutService 首先需要调用PaymentService 完成支付, 那么 ShipmentService 依赖 PaymentService, 依赖类型为逻辑依赖. 依赖关系是可以传递的, 即 , 根据依赖关系是否是由其他依赖关系的传递而衍生, 又可将依赖关系分为直接依赖 (direct dependency) 关系与间接依赖 (indirect dependency) 关系, 所有间接依赖关系都可以通过直接依赖关系传递获得, 因此为了保持服务依赖图的统一与简洁, 服务依赖图中依赖关系视为直接依赖关系. 除此之外,服务依赖发现方法通常会基于不同算法赋予依赖关系一个数值来衡量依赖关系的强弱或依赖关系存在的置信度.
2 服务依赖发现
从多源运行时数据角度对服务依赖发现方法进行综述分析. 系统运行时数据可以分为 3 类: 监控数据、系统日志数据与追踪数据. 监控数据是由监控工具在系统运行时获取的用以表征系统运行状况的数据, 包括网络通信包 (packet) 数据、资源使用数据如 CPU/内存等的使用量、业务统计指标如请求响应时间与吞吐量等. 系统日志数据是由开发人员在开发时添加的日志打印语句在系统运行时产生的用以记录程序运行状态及相关变量信息的半结构化文本数据. 追踪数据是由分布式追踪技术产生的用以刻画请求在分布式软件系统中端到端的处理过程的数据. 展示了服务依赖发现的基本流程. 首先, 多数服务依赖发现方法依赖于运行时数据的分布变化相关性, 为加速和加剧分布变化, 需要利用故障或干扰注入工具对微服务系统进行故障和干扰注入. 然后, 收集微服务系统产生的监控、系统日志和追踪数据并利用这些数据发现微服务实例和微服务依赖关系. 最后, 根据服务依赖关系构建服务依赖关系图. 相关研究工作分别基于 3 类不同运行时数据, 提出了不同自动化构建服务依赖关系图的方法.
2.1 基于监控数据的服务依赖发现
2.1.1 基于网络通信包数据的服务依赖发现
基于网络通信包数据的服务依赖发现方法利用存在依赖关系的两个微服务的网络通信消息中存在特定交互模式与时空上相关性的特点, 通过监听与解析网络传输层网络包数据, 使用统计方法从中推断服务之间的依赖关系.
基于网络通信包数据的服务依赖发现首先利用网络包监控工具获取每个节点上所有 TCP packets 与 UDPpackets, 从每个 packet 中提取一个五元组<SrcIP, SrcPort, DestIP, DestPort, Protocol>, 其中 SrcIP, SrcPort, DestIP,DestPort, Protocol 分别表示一个 packet 的源端 IP, 源端端口, 目标 IP, 目标端口与传输层协议; 然后根据五元组将在一定时间窗口内所有拦截到的 packets 划分为不同流 (flow)/通道 (channel)/会话 (session), 同一个流中 SrcIP,SrcPort, DestIP, DestPort 是相同的 (或者源端 IP 和源端端口与目标 IP 和目标端口交换), 进而得到表征每一个流的七元组<SrcIP, SrcPort, DestIP, DestPort, Protocol, startTime, endTime>, TCP 流的 startTime 是建立 TCP 连接 3 次握手时第 1 个 packet 的时间戳, endTime 是关闭 TCP 连接 4 次握手时最后一个 packet 的时间戳, UDP 流的startTime 是最早出现该五元组 pakcet 的时间戳, endTime 是在大于指定的时间间隔内不再出现该五元组 packet 的最后一个 packet 的时间戳; 构建系统中每个节点的所有流之后, 不同文献采用不同方法计算两个由<IP1, Port1> 和<IP2, Port2> 代表的两个服务是否存在依赖关系以及依赖关系成立的概率.
2.1.2 基于资源使用数据的服务依赖发现
基于资源使用数据的服务依赖发现技术利用存在依赖关系的两个服务之间资源使用在时间序列存在相似性的特点, 通过不同算法计算不同服务在一维或多维的资源使用时间序列数据上的相似度, 推断任意两个服务之间的相似度即服务依赖的强弱.
2.1.3 基于统计指标的服务依赖发现
基于统计指标的服务依赖发现方法利用存在依赖关系的两个服务执行时间差 (delay) 与响应时间 (responsetime) 存在一定规律的特点, 通过分析两个服务间的执行与响应时间关系, 进而推断两个服务之间的依赖关系.其通过拦截每个服务在一定时间窗口内的所有网络包, 使其延迟传递一定的时间并监控其他所有服务的响应时间, 根据服务的响应时间是否受影响, 以及响应时间受影响的程度, 来判断每个服务与被拦截网络包的服务依赖关系及强弱. 其反应在响应时间上的相关性特征不同, 通过学习利用被依赖服务响应时间来预测依赖服务响应时间的模型, 可以判断服务之间是否存在依赖关系, 以及存在的调用依赖关系的类型. 将服务之间的调用关系分为 4 类: 单调依赖 (single dependency)、组合依赖 (composite dependency)、并行依赖 (concurrent dependency)和分流依赖 (distrbuted dpendency), 分别表示两个服务之间的直接调用关系、一个服务依赖多个服务的串行调用,一个服务依赖多个服务的并行调用以及一个服务在负载均衡场景下对多个服务的调用. 针对 4 类调用关系, 作者分别分析了被依赖服务响应时间与依赖服务响应时间的关系, 给出了预测模型. 通过利用历史数据训练预测模型,可以预测某个服务的响应时间, 通过对比预测的响应时间符合哪类调用关系, 可以判断服务之间的调用依赖关系的类型.
2.2 基于系统日志的服务依赖发现
基于系统日志数据的微服务依赖关系发现利用不同日志数据内容或特征, 发现或推断不同微服务的调用路径、逻辑依赖或关联关系. 根据所依赖的日志内容或特征, 相关研究工作可以分为 3 种: 依据统一标识的服务依赖发现, 基于共现概率的服务依赖发现和基于日志频率的服务依赖发现. 依据统一标识的服务依赖发现假设日志文本中存在对不同微服务的标识信息 (例如 IP 等) 或请求的标识信息 (例如 Request ID, Block ID 等), 通过解析日志文本, 提取标识信息然后通过表示标识关联不同微服务. 基于共现概率的服务依赖发现假设如果两个微服务输出的一些日志存在频繁共现关系, 则两个微服务之间存在服务依赖. 基于日志频率的服务依赖发现统计连续时间窗口内不同微服务输出的日志频率, 将日志频率作为一个核心指标, 通过挖掘不同微服务的该指标之间的分布关系, 挖掘其中因果和关联关系, 最终获取微服务服务依赖.
2.2.1 依据统一标识的服务依赖发现
依据统一标识的服务依赖发现是基于系统日志数据的微服务依赖发现的主流方法. 本方法假设日志文本中包含能够表征请求的标识信息, 如果两个微服务输出日志的标识信息相同且具有先后序列关系, 则说明两个微服务在请求执行过程中存在调用关系, 即存在依赖关系. 文献 [36] 使用日志中 resource ID 和 request ID 关联不同微服务的日志, 构建请求执行路径. HDFS 日志文本中提取 block ID 和 IP 信息, IP 信息用以发现并标识各个微服务, block ID 用于构建请求执行路径,并通过关联执行路径中的连续日志, 发现微服务依赖. 在很多情况下, 日志文本中不存在一个特殊标识能够标识一个请求执行路径. 为解决这个问题, 文献 [37,38] 假设日志文本中包含多种 ID 信息, 通过多种 ID 信息串联请求执行路径, 最终发现微服务间的依赖关系. 文献 [39] 的主要贡献在于从系统源代码中找到最关键的 ID, 并最终使用这些 ID 对微服务进行依赖关系发现. 具体而言, 首先通过静态代码分析方法, 挖掘出绝对精确的日志之间的转移关系和日志中的关键标识. 然后, 这些关键标识被用于连接跨越不同组件却属于同一个请求的日志, 进而形成了一个跨服务的完整的以日志为节点的请求执行路径.
2.2.2 基于共现概率的服务依赖发现
基于共现概率的服务依赖发现的核心思想是依据单条日志之间的共现概率, 判断输出日志的服务间的依赖关系. 本方法假设如果不同微服务输出的两条日志之间存在着频繁先后共现关系, 则说明两个微服务可能存在逻辑上的因果或关联关系, 并依据此发现服务间依赖关系.
2.2.3 基于日志频率的服务依赖发现
基于日志频率的服务依赖发现的核心思想是将日志转换成为数值型的指标, 通过分析指标的分布差异或变化趋势, 发现微服务间的依赖关系. 本方法假设伴随着负载变化, 不同微服务输出的日志数量或频率也随之变化, 如果两个微服务输出的日志数量或频率之间存在相关性, 则说明两个微服务有可能共同协作处理相同请求, 因此两者之间存在一些因果或关联关系, 并依据此发现服务依赖关系.
2.3 基于追踪数据的服务依赖发现
基于追踪数据的服务依赖发现技术以分布式追踪技术作为支撑, 通过分布式追踪技术生成一次服务请求在分布式软件系统中的请求执行路径, 请求执行路径中的事件之间存在因果关系, 当事件的粒度为方法/服务时, 事件之间的因果关系即方法/服务之间的调用关系, 每一个请求执行路径中都包含了部分的服务依赖 (事件之间的因果关系) 信息, 而将多个请求执行路径中的服务依赖信息进行合并便能直接且准确地获取分布式软件系统完整的服务之间的调用依赖信息. 当请求执行路径中事件为细粒度的系统调用、方法调用时, 从请求执行路径中构建服务依赖关系图需要首先对请求执行路径进行抽象, 将细粒度的事件聚合为服务, 然后根据服务之间的因果关系判断服务之间调用依赖关系.
虽然请求执行路径中仅直接体现了服务调用依赖关系, 但服务之间的逻辑依赖关系同样可以从请求执行路径中较为直接地获取. 例如在图 4 所示的请求执行路径中, 事件之间的因果关系即服务之间的调用依赖关系, 服务之间的调用顺序可以根据各个服务调用的时间戳决定, 为从请求执行路径中发现 ShipmentService 对 PaymentService的逻辑依赖关系, 首先需要判断在所有此类请求执行路径中, PaymentService 是否先于 ShipmentSerice 被调用; 进而判断调用 PaymentService 的失效是否会导致 ShipmentService 的调用同样失效, 如果 PaymentService 的失效同样会导致 ShipmentService 的失效 (或者失效的概率超过一定阈值), 那么则可以判断 ShipmentService 与 PaymentService之间存在逻辑依赖关系.