来源|KubeWharf 社区
项目地址:github.com/kubewharf/k…

Kelemetry是字节跳动开发的用于Kubernetes控制平面的追寻体系,它从大局视角串联起多个 Kubernetes 组件的行为,追寻单个 Kubernetes 目标的完好生命周期以及不同目标之间的相互影响。经过可视化 K8s 体系内的事情链路,它使得 Kubernetes 体系更简单观测、更简单了解、更简单 Debug。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

布景

在传统的分布式追寻中,“追寻”一般对应于用户恳求期间的内部调用。特别是,当用户恳求到达时,追寻会从根跨度开端,然后每个内部RPC调用会发动一个新的子跨度。由于父跨度的持续时刻一般是其子跨度的超集,追寻能够直观地以树形或火焰图的形式调查,其间层次结构表明组件之间的依靠联系。

与传统的RPC体系相反,Kubernetes API是异步和声明式的。为了履行操作,组件会更新apiserver上目标的标准(希望状况),然后其他组件会不断尝试自我纠正以到达希望的状况。例如,当咱们将ReplicaSet从3个副本扩展到5个副本时,咱们会将spec.replicas字段更新为5,rs controller会调查到此更改,并不断创立新的pod目标,直到总数到达5个。当kubelet调查到其办理的节点创立了一个pod时,它会在其节点上生成与pod中的标准匹配的容器。

在此进程中,咱们从未直接调用过rs controller,rs controller也从未直接调用过kubelet。这意味着咱们无法调查到组件之间的直接因果联系。如果在进程中删去了原始的3个pod中的一个,副本集控制器将与两个新的pod一同创立一个不同的pod,咱们无法将此创立与ReplicaSet的扩展或pod的删去相关起来。因而,由于“追寻”或“跨度”的定义模糊不清,传统的基于跨度的分布式追寻模型在Kubernetes中几乎不适用。

曩昔,各个组件一直在完结自己的内部追寻,一般每个“reconcile”对应一个追寻(例如,kubelet追寻只追寻处理单个pod创立/更新的同步操作)。但是,没有单一的追寻能够解说整个流程,这导致了可调查性的孤立岛,由于只有调查多个reconcile才干了解许多面向用户的行为;例如,扩展ReplicaSet的进程只能经过调查副本集控制器处理ReplicaSet更新或pod安排妥当更新的多个reconcile来揣度。

为解决可调查性数据孤岛的问题,Kelemetry以组件无关、非侵入性的方法,搜集并衔接来自不同组件的信号,并以追寻的形式展示相关数据。

设计

将目标作为跨度

为了衔接不同组件的可调查性数据,Kelemetry选用了一种不同的方法,遭到kspan项目的启示,与将单个操作作为根跨度的尝试不同,这里为目标自身创立一个跨度,而每个在目标上产生的事情都是一个子跨度。此外,各个目标经过它们的拥有联系衔接在一同,使得子目标的跨度成为父目标的子跨度。因而,咱们得到了两个维度:树形层次结构表明目标层次结构和事情规模,而时刻线表明事情次序,一般与因果联系共同。

例如,当咱们创立一个单pod布置时,deployment controller、rs controller和kubelet之间的交互能够运用审计日志和事情的数据在单个追寻中显现:

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统
追寻一般用于追寻持续几秒钟的短暂恳求,所以追寻存储完结可能不支撑具有长生命周期或包括太多跨度的追寻;包括过多跨度的追寻可能导致某些存储后端的功能问题。因而,咱们经过将每个事情分到其所属的半小时时刻段中,将每个追寻的持续时刻约束为30分钟。例如,产生在12:56的事情将被分组到12:30-13:00的目标跨度中。

咱们运用分布式KV存储来存储(集群、资源类型、命名空间、称号、字段、半小时时刻戳)到相应目标创立的追寻/跨度ID的映射,以保证每个目标只创立一个追寻。

审计日志搜集

Kelemetry的主要数据源之一是apiserver的审计日志。审计日志供给了关于每个控制器操作的丰厚信息,包括发起操作的客户端、触及的目标、从接纳恳求到完结的精确持续时刻等。在Kubernetes架构中,每个目标的更改会触发其相关的控制器进行协调,并导致后续目标的更改,因而调查与目标更改相关的审计日志有助于了解一系列事情中控制器之间的交互。

Kubernetes apiserver的审计日志以两种不同的方法露出:日志文件和webhook。一些云供给商完结了自己的审计日志搜集方法(例如,Amazon EKS、GKE),而在社区中配置审计日志搜集的与厂商无关的方法发展甚微。为了简化自助供给的集群的布置进程,Kelemetry供给了一个审计webhook,用于接纳原生的审计信息,也露出了插件API以完结从特定厂商的消息队列中消费审计日志。

Event 搜集

当Kubernetes控制器处理目标时,它们会宣布与目标相关的“event”。当用户运行kubectl describe指令时,这些event会显现出来,一般供给了控制器处理进程的更友好的描述。例如,当调度器无法调度一个pod时,它会宣布一个FailToSchedulePod事情,其间包括具体的消息:

0/4022 nodes are available to run pod xxxxx: 1072 Insufficient memory, 1819 Insufficient cpu, 1930 node(s) didn’t match node selector, 71 node(s) had taint {xxxxx}, that the pod didn’t tolerate.

由于event针对用于kubectl describe指令优化,它们并不保留每个原始事情,而是存储了最后一次记录事情的时刻戳和次数。另一方面,Kelemetry运用Kubernetes中的目标列表调查API检索事情,而该API仅公开event目标的最新版别。为了避免重复事情,Kelemetry运用了几种启示式方法来“猜想”是否应将event陈述为一个跨度:

  • 耐久化处理的最后一个event的时刻戳,并在重启后忽略该时刻戳之前的事情。尽管事情的接纳次序不一定有保证(由于客户端时钟偏差、控制器 — apiserver — etcd往复的不共同推迟等原因),但这种推迟相对较小,能够消除由于控制器重启导致的大多数重复。
  • 验证event的resourceVersion是否产生了变化,避免由于重列导致的重复event。

将目标状况与审计日志相关

在研究审计日志进行故障排除时,咱们最想知道的是“此恳求改变了什么”,而不是“谁发起了此恳求”,尤其是当各个组件的语义不清楚时。Kelemetry运行一个控制器来监督目标的创立、更新和删去事情,并在接纳到审计事情时将其与审计跨度相关起来。当Kubernetes目标被更新时,它的resourceVersion字段会更新为一个新的唯一值。这个值能够用来相关更新对应的审计日志。Kelemetry把目标每个resourceVersion的diff和快照缓存在分布式KV存储中,以便稍后从审计消费者中链接,从而使每个审计日志跨度包括控制器更改的字段。

追寻resourceVersion还有助于辨认控制器之间的409冲突。当客户端传递UPDATE恳求的resourceVersion过旧,且其他恳求是将resourceVersion更改时,就会产生冲突恳求。Kelemetry能够将具有相同旧资源版别的多个审计日志组合在一同,以显现与其后续冲突相关的审计恳求作为相关的子跨度。

为了保证无缝可用性,该控制器运用多主选举机制,答应控制器的多个副本一同监督同一集群,以保证在控制器重新发动时不会丢失任何事情。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

前端追寻转化

在传统的追寻中,跨度总是在同一个进程(一般是同一个函数)中开端和完毕。 因而,OTLP 等追寻协议不支撑在跨度完结后对其进行修正。 不幸的是,Kelemetry 不是这种状况,由于目标不是运行中的函数,并且没有专门用于发动或中止其跨度的进程。 相反,Kelemetry 在创立后当即确认目标跨度,并将其他数据写入子跨度, 是以每个审计日志和事情都是一个子跨度而不是目标跨度上的日志。

但是,由于审计日志的完毕时刻/持续时刻一般没有什么价值,因而追寻视图十分丑陋且空间效率低下:

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

为了进步用户体验,Kelemetry 阻拦在 Jaeger 查询前端和存储后端之间,将存储后端结果回来给查询前端之前,对存储后端结果履行自定义转化流水线。

Kelemetry 现在支撑 4 种转化流水线:

  • tree:服务名/操作名等字段名简化后的原始trace树
  • timeline:修剪一切嵌套的伪跨度,将一切事情跨度放在根跨度下,有效地供给审计日志
  • tracing:非目标跨度被展平为相关目标的跨度日志

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

  • 分组:在追寻管道输出之上,为每个数据源(审计/事情)创立一个新的伪跨度。 当多个组件将它们的跨度发送到 Kelemetry 时,组件一切者能够专心于自己组件的日志并轻松地交叉检查其他组件的日志。

用户能够在追寻查找时经过设置“service name”来选择转化流水线。 中间存储插件为每个追寻查找结果生成一个新的“CacheID”,并将其与实践 TraceID 和转化管道一同存储到缓存 KV 中。 当用户检查时,他们传递CacheID,CacheID 由中间存储插件转化为实践TraceID,并履行与 CacheID 相关的转化管道。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

打破时长约束

如上所述,追寻不能无限增长,由于它可能会导致某些存储后端出现问题。 相反,咱们每 30 分钟开端一个新的追寻。 这会导致用户体验混乱,由于在 12:28 开端翻滚的布置追寻会在 12:30 忽然终止,用户必须在 12:30 手动跳转到下一个追寻才干持续检查追寻 . 为了避免这种认知开支,Kelemetry 存储插件在查找追寻时辨认具有相同目标标签的跨度,并将它们与相同的缓存 ID 以及用户指定的查找时刻规模一同存储。 在烘托 span 时,一切相关的轨道都兼并在一同,具有相同目标标签的目标 span 被删去重复,它们的子目标被兼并。 轨道查找时刻规模成为轨道的剪切规模,将目标组的完好故事显现为单个轨道。

多集群支撑

能够布置 Kelemetry 来监督来自多个集群的事情。 在字节跳动,Kelemetry 每天创立 80 亿个跨度(不包括伪跨度)(运用多 raft 缓存后端而不是 etcd)。 目标能够链接到来自不同集群的父目标,以启用对跨集群组件的追寻。

未来增强

选用自定义追寻源

为了真实衔接K8S生态体系中的一切观测点,审计和事情并不满足全面。Kelemetry将从现有组件搜集追寻,并将其集成到Kelemetry追寻体系中,以供给对整个体系的统一和专业化视图。

批量剖析

经过Kelemetry的聚合追寻,回答诸如“从布置升级到首次拉取镜像的发展需要多长时刻”等问题变得愈加简单,但咱们仍然缺少在大规模上聚合这些目标以供给全体功能洞察的才能。经过每隔半小时剖析Kelemetry的追寻输出,咱们能够辨认一系列跨度中的形式,并将其相关为不同的场景。

运用事例

1. replicaset controller 異常

用户陈述,一个 deployment 不断创立新的 Pod。咱们能够经过deployment称号快速查找其 Kelemetry 追寻,剖析replicaset与其创立的 Pod 之间的联系。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

从追寻可见,几个关键点:

  • Replicaset-controller 宣布 SuccessfulCreate 事情,表明 Pod 创立恳求成功回来,并在replicaset reconcile中得到了replicaset controller的确认。
  • 没有replicaset状况更新事情,这意味着replicaset controller中的 Pod reconcile未能更新replicaset状况或未调查到这些 Pod。

此外,检查其间一个 Pod 的追寻:

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

  • Replicaset controller 在 Pod 创立后再也没有与该 Pod 进行交互,乃至没有失败的更新恳求。

因而,咱们能够得出结论,replicaset controller中的 Pod 缓存很可能与 apiserver 上的实践 Pod 存储不共同,咱们应该考虑 pod informer 的功能或共同性问题。如果没有 Kelemetry,定位此问题将触及检查多个 apiserver 实例的各个 Pod 的审计日志。

2. 起浮的 minReadySeconds

用户发现deployment的翻滚更新十分缓慢,从14:00到18:00花费了几个小时。如不运用Kelemetry,经过运用 kubectl 查找目标,发现 minReadySeconds 字段设置为 10,所以长时刻的翻滚更新时刻是不符合预期的。kube-controller-manager 的日志显现,在一个小时后 Pod 才变为 Ready 状况。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

进一步检查 kube-controller-manager 的日志后发现,在某个时刻 minReadySeconds 的值为 3600。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

运用 Kelemetry 进行调试,咱们能够直接经过deployment称号查找追寻,并发现federation组件增加了 minReadySeconds 的值。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

后来,deployment controller将该值康复为 10。

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

因而,咱们能够得出结论,问题是由用户在翻滚更新进程中暂时注入的较大 minReadySeconds 值引起的。经过检视目标 diff ,能够轻松辨认由非预期中间状况引起的问题。

尝试Kelemetry

Kelemetry已在GitHub上开源。依照快速入门指南试试Kelemetry如何与您的组件进行交互,或者如果您不想设置一个集群,能够检查从GitHub CI流水线烘托的在线预览。