扩展可观测性平台至 100 PB:通过拥抱宽事件并替换 OTel 实现突破

图片

本文字数:16133;估计阅读时间:41 分钟

作者:Rory Crispin

本文在公众号【ClickHouseInc】首发

图片

TLDR

可观测性扩展能力显著提升:我们的内部系统从 19 PiB 的未压缩日志增长至 100 PB,日志行数从约 40 万亿提升至 500 万亿。

效率实现飞跃:面对 20 倍的事件量激增,我们仅用不到过去 10% 的 CPU 就成功应对。

OTel 遇到瓶颈:OpenTelemetry 对事件的解析和编组在扩展时表现不佳,成为性能瓶颈 —— 我们通过自研管道解决了这一问题。

引入 HyperDX:基于 ClickHouse 的原生可观测性界面,支持类 Lucene 语法,实现便捷的数据探索、关联与根因分析。

介绍

大约一年前,我们分享了 LogHouse 的构建历程 —— 这是我们内部开发的日志平台【https://clickhouse.com/blog/building-a-logging-platform-with-clickhouse-and-saving-millions-over-datadog】,专为监控 ClickHouse Cloud 而设计。当时系统管理的数据量为 19 PiB,我们已经觉得相当庞大。LogHouse 不仅帮助我们解决了可观测性难题,还替代了日益高昂的 Datadog 账单,为公司节省了数百万美元。这篇文章引发了广泛关注,也让我们意识到,很多团队同样受困于传统可观测性厂商,这进一步说明在大规模场景中,数据管理的效率至关重要。

一年后,LogHouse 的规模远超我们的想象,目前系统已存储超过 100 PB 的未压缩日志数据,数据行数接近 500 万亿。为支持这一增长,我们对架构进行了多次重构,引入了全新工具,也积累了不少经验教训 —— 其中最重要的一点是:OpenTelemetry(OTel)并非在所有场景下都适用(尽管我们仍然高度评价它),在某些情况下,自定义日志处理管道不可或缺。

以我们的经验来看,这一架构调整带来了极大的收益:在我们最关键的数据源上,仅使用不到过去十分之一的 CPU 资源,就能承载比原来多 20 倍的日志事件 —— 这对成本控制和处理效率而言,是一次重大的飞跃。

我们的技术栈也在持续演进,尤其是在 ClickHouse 收购 HyperDX 之后。这项收购不仅为我们带来了原生的 ClickHouse UI,还促成了 ClickStack 的诞生 —— 这是一个围绕 ClickHouse 打造的完整可观测性解决方案,有着明确的设计理念。得益于 HyperDX,我们已逐步从基于 Grafana 的自定义 UI 迁移,转向更为集成化的探索、分析和问题排查体验。

越来越多的团队正在将 ClickHouse 用于可观测性场景,并惊喜地发现可以以极低的成本存储和查询海量数据。我们希望这篇分享,能像我们之前的文章一样,带来有价值的启发。如果你对我们这段扩展之路、OTel 的适用边界,以及如何将日志管道扩展到 100PB 感兴趣……欢迎继续阅读。

从通用走向专用:可观测性在规模化中的演进

过去一年,我们的可观测性策略经历了深刻的变革。虽然我们仍然使用 OpenTelemetry 来采集通用日志,但随着系统的不断扩展,它逐渐暴露出性能上的瓶颈。OTel 依然是我们工具体系中重要的一环,但对于最关键的工作负载,它已无法提供我们所需的高性能与高精度。因此,我们开始构建专为核心系统量身定制的工具,并重新评估通用方案的适用边界。在这一过程中,我们拓展了数据采集的范围,也对面向工程师的洞察展示方式进行了全面重构。

突破数据规模的新边界

在上一篇介绍 LogHouse 的文章中,我们曾为系统能管理 19 PiB 的未压缩数据、37 万亿行日志感到自豪。而如今,这些数字早已被远远抛在身后。当前的 LogHouse 已经处理超过 100 PB 的未压缩数据,日志行数接近 500 万亿。下面是一些关键数据的拆解视图:

图片

这些数字的变化背后,蕴含着我们架构策略的重大转变。在最初的版本中,我们的全部可观测性数据都通过 OpenTelemetry 管道采集,所有日志统一通过这一通用系统处理。然而,随着系统规模和数据复杂度不断提升,通用方案已难以满足我们对高性能和可扩展性的需求。虽然总数据量增加了 5 倍以上,但数据来源结构的变化则反映出我们有意识地进行了一次战略调整 —— 现在,我们的大部分日志数据都来自 “SysEx”,这是我们为 ClickHouse 内部日志打造的高吞吐、高保真专用导出器。这一改变标志着我们在构建可观测性管道方面进入了新的阶段。

我们希望接下来的内容,能够帮助你更直观地理解 LogHouse 所处的规模和复杂度。

图片

OpenTelemetry 在超大规模下的效率瓶颈

在最初阶段,我们通过 OpenTelemetry(OTel)来收集所有日志。作为业内通用的标准方案,OTel 帮助我们快速搭建了一个基础框架,使得 Kubernetes 集群中的每个 pod 都能将日志发送到 ClickHouse。但随着系统规模不断扩大,我们意识到必须为 ClickHouse 核心的遥测数据构建更为专业的采集工具,背后主要有两个原因。

首先,虽然 OTel 能够通过 stdout 捕获 ClickHouse 的文本日志,但这仅仅是 ClickHouse 所暴露出的遥测数据的冰山一角。任何熟悉 ClickHouse 的人都知道,真正有价值的数据其实深藏在其 system 表中 —— 那里包含了丰富的结构化信息,包括日志、性能指标和运行状态,远比标准输出更全面。这些系统表记录了从查询执行细节、磁盘 I/O 到后台任务状态等全部内容,并且不同于易失的日志信息,它们可以在集群中长期保留。这类数据无论是用于实时排查问题,还是回溯历史分析,都是不可替代的。因此,我们希望将这部分关键数据完整纳入 LogHouse。

其次,随着规模不断扩大,我们逐渐发现,OTel 管道在处理这类高保真日志时效率低下,难以满足我们的性能需求。

图片

整个数据传输流程如下:

  1. 客户的 ClickHouse 实例将日志以 JSON 格式写入 stdout;

  2. kubelet 将这些日志保存到 /var/log/…;

  3. OTel 的日志采集器从磁盘读取这些日志,解析并将其编组成内存结构;

  4. 然后,日志被转换为 OTel 专用格式(仍为内存结构);

  5. 最后,通过 ClickHouse Go 客户端将其转换并写入另一个 ClickHouse 实例(即 LogHouse)。

补充说明:上述流程是简化版本,实际情况比这更为复杂。我们的 OTel 管道会首先在边缘节点以 JSON 格式收集日志,随后转换为 OTel 格式,并通过 OTLP 协议传送到一组网关实例。这些网关(本身也是 OTel collector)在最终写入 ClickHouse 之前,还会对日志数据进行额外处理并转换为 ClickHouse 的原生格式。整个过程中,每一个环节都不可避免地引入了性能开销、处理延迟以及架构上的复杂性。

在大规模环境下,OTel 管道暴露出两大问题:效率低下与数据丢失。
随着数据量的持续增长,我们发现 OTel 管道开始成为性能瓶颈。首先,它在处理过程中涉及多次数据转换,导致大量计算资源被无谓消耗。ClickHouse 的原生数据类型需先被展平成 JSON,再转成 OTel 格式,最后重新导入并被 ClickHouse 重新解析。这一流程不仅极大地浪费了 CPU 资源,还影响了数据的准确性和完整性。

更严重的是,collector 本身也面临资源瓶颈。由于它们作为 agent 部署在每个 Kubernetes 节点上,必须遵守严格的 CPU 和内存限制。一旦遇到流量高峰,部分 collector 无法承载 ClickHouse 产生的海量日志,直接开始丢弃数据。很多日志在到达 LogHouse 之前,就已经在边缘被丢弃。

面对这一局面,我们必须做出选择:要么大幅扩展 OTel agent 和 gateway 的资源规模,要么彻底重构整个数据采集模型。我们最终选择了后者。

注:为更直观地了解资源开销,我们测算发现,如果希望通过 OTel 管道无丢失地处理每秒 2000 万行日志,系统将需要约 8000 个 CPU 核心用于 agent 和 collector。这种体量的资源开销仅仅用于日志采集,显然通用方案在我们当前的规模下已经难以为继。

打造 SysEx:为 ClickHouse 内部传输量身定制的日志导出系统

为了解决这些问题,我们设计并开发了 System Tables Exporter,简称 SysEx。它是一个专门用于高效在两个 ClickHouse 实例之间传输数据的工具。通过 SysEx,我们可以直接将客户 pod 中的 system 表数据导入 LogHouse,保留原生的数据类型,彻底省去所有中间格式转换。

这一方案还有一个意想不到的好处:我们工程师在排查线上问题时所使用的查询语句,几乎可以原样复用到 LogHouse 中,用于分析全局历史数据。这得益于我们保持了两端表结构的完全一致,仅在 LogHouse 中额外添加了少量用于增强分析能力的字段(如 Pod 名称、ClickHouse 版本等)。

图片

值得强调的是,SysEx 实现的是字节级别的数据精确复制。它能完整保留所有信息,避免不必要的 CPU 负担,同时规避多次数据编组所带来的各种问题。

SysEx 的架构设计既简单又高效。我们运行了一组 SysEx 抓取器,它们连接到客户的 ClickHouse 实例。通过哈希环的方式,将每个客户 pod 分配给特定的抓取器副本,实现负载均衡。每个抓取器从目标 pod 的 system 表中执行 SELECT 查询,并将结果数据直接流式传输到 LogHouse,无需进行反序列化。整个过程中,抓取器仅负责在源端与目标端之间协调并传输字节数据。

抓取 system 表需要特别小心,以防由于缓冲区未及时刷新导致数据丢失。幸运的是,ClickHouse 的 system 表大多具有时间序列特性。SysEx 正是基于这一点,采用滑动时间窗口进行查询,窗口通常比当前时间延迟五分钟左右。这段延迟可以确保内部缓冲数据已经刷新完毕,从而在抓取时能够获取到完整的数据。实践表明,这一策略稳定可靠,能够满足我们对数据实时性与完整性的内部 SLA 要求。

SysEx 使用 Go 语言编写,与我们在 ClickHouse Cloud 的大多数基础设施组件保持一致。对于熟悉 Go 的 ClickHouse 客户端的开发者来说,可能会产生一个疑问:我们如何避免客户端默认的数据编解码操作?因为客户端默认会将数据转换为 Go 的原生类型,这会破坏我们所需的字节级数据传输。为此,我们向 Go 客户端提交了增强功能,允许完全绕过内部的编组过程,使 SysEx 能以 ClickHouse 原生格式直接将数据从源集群传输至 LogHouse —— 无需解码、无需再编码,也不涉及中间数据结构的创建。

本质上,这种传输方式就像执行一个简单的 bash 命令一样高效。

curl -s -u 'default:<password>' "https://sql-clickhouse.clickhouse.com:8443/?query=SELECT+*+FROM+system.query_log+FORMAT+Native" | curl -s -X POST --data-binary @- 'http://localhost:8123/?query=INSERT+INTO+query_log+FORMAT+Native'

如果你对 Go 实现感兴趣,可以在这里查看完整代码示例。【https://pastila.nl/?00740eb5/722157a76fc25f54212d7097b805253b#OSfprbH3wM1dGgtyMAnmuw==】

更重要的是,SysEx 不再像 OTel 那样依赖大量缓冲机制。得益于其基于拉取的架构模型,抓取器可以以稳定、可控的速率对数据进行查询,因此即便 LogHouse 短暂不可用,或源端遥测数据出现波动,也不会造成日志丢失。SysEx 会自动回补历史时间窗口中的数据,从而保障数据的可靠性,无需引入系统复杂性或高资源消耗的重试缓冲。

图片

动态表结构(Schema)处理机制

在 SysEx 的架构中,有一个重要挑战:它默认源端和目标端的表结构一致。然而,熟悉 ClickHouse 的用户都知道,系统表的 schema 经常发生变动。工程师们持续添加新的字段和指标,以支持新功能、优化排障体验,这导致表结构始终处于动态变化之中。

为了解决这一问题,我们实现了动态 schema 生成机制。当 SysEx 抓取某个 system 表时,会先分析其结构,并通过哈希判断 LogHouse 中是否已有匹配的目标表。如果有,就将数据直接写入该表;如果没有,则为该 system 表创建一个新版本的表结构,例如 text_log_6。

在查询阶段,我们利用 ClickHouse 的 Merge 表引擎,将所有不同版本的表结构统一呈现为一个逻辑视图。这样一来,查询系统表时就无需关心其结构是否一致。引擎会自动处理 schema 差异,只返回所有版本中兼容的字段,或者将查询限制在包含特定字段的表上。借助这一机制,我们不仅能够兼容未来 schema 的演进,还保持了查询语法的简洁性,完全无需手动维护或更新表结构。

系统状态快照

在持续扩展我们的可观测性能力过程中,我们将注意力集中在系统状态类的内存表(如 system.processes)上。不同于之前采集的时间序列数据,这类表提供的是系统在某个特定时刻的即时快照。为了记录这类关键信息,我们构建了周期性快照机制,将内存表的状态定期捕获并写入 LogHouse。

这种机制不仅帮助我们记录任意时间点上的集群状态,还能让我们追溯重要的历史变化,例如表结构或集群配置的变更。通过这些快照数据,我们能够在整个集群,乃至 ClickHouse Cloud 全局范围内,进行更深入的诊断分析。我们可以将其与服务配置或查询特征(如 used_functions)进行关联分析,更精确地识别系统中的异常,从而更快速地定位问题根因。同时,通过将查询和表结构进行关联分析,我们进一步增强了提前发现性能瓶颈或稳定性问题的能力,帮助客户实现更高效的问题预防与解决。

跨集群的统一查询能力

借助 SysEx,我们解锁了众多强大能力,其中最具代表性的一项,是可以将客户在 Advanced Dashboard(高级仪表板)中用于监控单个 ClickHouse 实例的查询,直接扩展到整个客户集群的所有实例上统一运行。

这使得我们的发布分析流程更加高效:在每次版本发布前后,我们都可以运行一系列经过验证的诊断查询,立即捕捉整个系统行为的变化。这些查询覆盖了查询性能、资源利用率和错误率等多个维度,并可在实时范围内完成,帮助我们快速识别性能回退或验证优化效果,真正实现面向全集群的发布质量评估。

同时,我们的支持工具也得到了显著增强。现在,支持工程师在排查客户问题时,可以使用与客户相同的深度诊断查询,但结合了我们集中式遥测平台提供的更多上下文信息,如网络日志、Kubernetes 事件、数据平面与控制平面日志等,所有分析都可在同一个界面中完成,无需切换工具或上下文。

图片

数据提升 20 倍,CPU 降低 90%:SysEx 重构带来的显著成果

SysEx 在效率方面带来了突破性的提升。以下是来自 LogHouse 的一组真实对比数据:

  • 传统 OTel 收集器:需要超过 800 个 CPU 核,才能支撑每秒 200 万条日志的处理。

  • SysEx 抓取器:仅需 70 个 CPU 核,即可稳定传输每秒 3700 万条日志。

借助这一专用方案,我们在最核心的数据通道上实现了 20 倍的数据处理能力提升,同时将资源消耗控制在不到原来的 10%。更关键的是,这也意味着我们已彻底解决边缘日志丢失的问题。若仍采用旧的 OTel 管道架构,要达到同样的可靠性,我们需要部署超过 8000 个 CPU 核。而现在,SysEx 几乎以原来资源开销的一个零头,便实现了更高的吞吐、更强的稳定性和数据完整性。

何时该使用 OpenTelemetry?

读到这里你可能会问:OpenTelemetry 是否还有使用场景?它还值得保留吗?我们的答案是肯定的。虽然我们在架构上已经进行了优化,以应对每秒处理 2000 万条日志这样的极端规模挑战,但 OpenTelemetry 仍然是我们整个体系中的重要一环。它提供统一、标准化的日志格式,且不依赖任何厂商,能够让新用户快速上手 —— 这也是它成为 ClickStack 默认方案的原因。相比深度绑定 ClickHouse 内部逻辑的 SysEx,OpenTelemetry 具有生产者与消费者解耦的优势,对那些希望灵活切换可观测性平台的用户尤为重要。

它也非常适用于 SysEx 无法运行的场景。SysEx 依赖主动查询 system 表,这意味着服务必须处于健康、可响应状态。如果服务宕机或不断重启,SysEx 无法抓取任何数据。但 OpenTelemetry 是被动收集模式,即使服务处于故障状态,也能从 stdout 或 stderr 中捕获日志。这使我们在事故发生时仍能获取关键日志数据,并进行问题回溯分析。基于这一点,我们仍然在所有 ClickHouse 服务中运行 OTel。不同的是,数据采集策略发生了调整 —— 过去我们采集包括 trace 级在内的所有日志,现在仅采集 info 级及以上日志,从而大幅减少了数据量,也降低了资源消耗,使 OTel 收集器和网关可以更高效运行。目前的这套轻量级 OTel 流水线仍支持每秒 200 万条日志的处理能力。

HyperDX:为 ClickHouse 打造的极致可观测体验

日志采集只是第一步,让数据真正“用起来”才是关键。在 LogHouse 的初始阶段,我们曾在 Grafana 上构建了一套高度定制的可观测性界面。这套系统一度表现良好,但随着数据来源不断增多,尤其是引入了 SysEx 和宽列日志之后,我们逐渐意识到:需要一个更强、更“懂 ClickHouse”的前端工具。

我们并不是唯一遇到这个问题的团队。很多使用 ClickHouse 构建可观测性平台的开发者也面临同样挑战 —— 写入 ClickHouse 很容易,但要打造一套释放其全部价值的 UI,往往需要大量的前端开发投入。对没有专职前端团队的小公司而言,这几乎是无法实现的目标。

而 HyperDX 改变了这一现状。它是 ClickHouse 官方支持的原生 UI,具备强大的日志与链路分析能力,能够在大规模数据场景下高效完成探索、关联与根因分析。它的查询流程针对 ClickHouse 深度优化,响应速度快、延迟低。在我们收购 HyperDX 之前的评估中,它就已经很好地解决了我们自己和业界共同面临的痛点。Lucene 查询语法极大简化了数据探索,绝大多数场景下已足够使用。而在更复杂的事件分析中,HyperDX 同样支持 SQL 查询,这是我们团队依然不可或缺的分析方式(详见“用于复杂分析的 SQL”)。

HyperDX 之所以与 LogHouse 高度契合,还有一个关键点:在 2.0 版本中,它引入了 schema 无关模式。这意味着日志表无需遵循固定结构。对像 LogHouse 这样从多个不同来源接收数据的系统来说,这种灵活性至关重要:

  • 它能无缝支持来自 OTel 管道的标准化但不断变化的日志结构。

  • 更重要的是,它可以直接兼容由 SysEx 和其他自定义导出器生成的宽列表。无需预定义 schema,也不需要复杂的 grok 规则。HyperDX 会在后台自动解析表结构,并适配使用,真正实现“开箱即用”。


通过 SysEx,我们的工程团队可以灵活地向 LogHouse 添加来自不同来源的新数据表结构,而无需担心影响用户界面或进行额外配置。HyperDX 提供了强大的可视化界面与会话回放功能,加上 LogHouse 海量的数据支持,我们为工程师打造出统一、高效、可扩展的可观测性体验。

图片

图片

Grafana 依然不可替代,尽管我们广泛采用了 HyperDX,但 Grafana 仍然在我们体系中发挥着重要作用。我们内部基于 Grafana 的应用在查询路由与范围控制方面尤为强大 —— 用户需指定具体的命名空间(即客户服务名),系统即可将查询自动路由到 LogHouse 中正确的 ClickHouse 实例。这样避免了跨无关服务的资源浪费,提升了整体效率。

这种机制对于我们来说尤其重要,因为我们的 LogHouse 集群分布在多个区域。正如我们早期文章所提到的,在分布式环境下提升查询效率和稳定性是系统设计的核心。目前我们也在尝试将这套路由逻辑直接集成到 ClickHouse 引擎中,让 HyperDX 同样具备这类性能优化功能,敬请期待。

同时,Grafana 依旧是我们保留许多传统仪表板和 Prometheus 告警的核心平台。这些内容,诸如 kube_state_metrics,已成为监控集群健康的行业默认方案。虽然它们不适合深入分析,但作为高层级的告警信号依然十分有效。目前我们也没有迁移这些内容的计划。

因此,HyperDX 与 Grafana 在我们的可观测性平台中互为补充、协同并存。

拥抱高基数的可观测性范式

写入全量,查询聚合

SysEx 不只是一次架构优化,它也带来了我们文化上的一次变革 —— 我们开始彻底摆脱旧有的三支柱监控模型,转而采用以宽事件和高基数数据为核心的方式来建设可观测性系统。

这种理念有时被称为“Observability 2.0”,但在我们看来,它就是 LogHouse + ClickStack 的自然演进。

这种架构下,我们不再需要为适配指标库而提前聚合或丢弃维度信息。每一条记录都保留完整的上下文信息 —— 包括查询 ID、Pod 名称、软件版本、网络信息等。我们构建的是一个可以容纳所有数据的中心仓库,查询时再动态处理和聚合,从而保留了数据的真实性与灵活性。

我们尤为推崇的一种模式是:用包含时间序列属性的宽事件日志来替代传统的指标写入。例如,下面这条日志来自 SysEx,记录了一个源端 ClickHouse 实例向 LogHouse 集群推送数据的情况:

{
  "level": "info",
  "ts": 1728437333.011701,
  "caller": "scrape/scrape.go:334",
  "msg": "pushed",
  "podName": "c-plum-qa-31-server-zkmrfei-0",
  "podIP": "10.247.29.9",
  "spokenName": "plum-qa-31",
  "azTopoName": "us-east1-c",
  "srcTable": "part_log",
  "windowSize": 120,
  "insertDuration": 0.00525254,
  "insertLag": 300.011693981,
  "startGTE": 1728436913,
  "stopLT": 1728437033
}

你可能会问:这套体系与 Prometheus 这样的传统指标存储有何本质区别?

核心区别在于:我们不做预聚合,而是逐条存储每一条原始数据。像 insertDuration 这样的字段不会在采集时进行处理,而是原样保留所有采样值并集中存储。

而 Prometheus 等系统通常只会为每个时间序列保存一个 gauge 值,或者更常见地通过直方图对多个样本进行预聚合,从而优化查询性能。但这种设计也带来了明显的局限。例如,如果要在 Prometheus 中保留所有标签组合的时间序列,将会面临基数爆炸的问题。考虑我们数万个唯一 pod 名称的生产环境,如果要在查询时保留标签的灵活性,每个标签组合都必须维护一条独立时间序列。使用直方图虽然有助于节省资源,但代价是牺牲了精度,这会让很多问题无法被回答,比如:

“insertDuration 发生波动的那个峰值,到底是哪一条具体的 insert 操作造成的?在哪个实例、哪张表、哪个时间点?”

而我们的方法完全避免了这些权衡。我们将每个日志事件都记录成一行宽表,包含全部的指标值和上下文字段。所有聚合和摘要都推迟到查询时执行,既保留了分析灵活性,又不影响数据保真度。

这种数据模型并非首次出现。像 Elasticsearch 等系统早已鼓励使用宽事件日志与结构化文档的方式进行采集。而真正的突破在于 —— ClickHouse 将这一理念在大规模生产环境中变得切实可行。得益于其列式架构,我们可以在不牺牲查询性能或承担高昂存储成本的前提下,长期高效地保存海量、高基数的事件数据。

使用数据科学工具进行可观测性探索

这种新模型的最大优势,是它为我们打开了数据探索的广阔空间 —— 我们可以针对一条事件从多个角度得出不同结论,灵活可视化其特征,并能随时从可视化界面跳转回原始日志进行追踪。

例如,我们可以聚焦于某个服务,按时间顺序逐行查看它的 insert 操作 —— 这是最直接的数据原貌。

图片

我们也可以轻松绘制该实例所有表的 insert 延迟图……

图片

还可以进一步扩展到区域级视角,对所有服务器的 insert 延迟进行汇总,筛选出延迟高于预期的主机……

图片

更重要的是 —— 可观测性,本质上就是一个数据问题。因此,我们可以充分借助数据科学领域的所有工具,对可观测性数据进行灵活探索。任何支持 ClickHouse 查询的可视化工具或客户端库都能派上用场,比如在 Jupyter Notebook 中用 Plotly 直接分析日志数据;这也正是我们当前正在探索并积极实践的方向。

import plotly.express as px
import pandas as pd
import clickhouse_connect

client = clickhouse_connect.get_client(
…
)
query = """
SELECT
    toInt64(toFloat64(LA['ts'])) AS ts,
    toInt64(LA['startGTE']) AS start,
    toInt64(LA['stopLT']) AS stop
FROM otel.generic_logs_0
WHERE (PodName LIKE 'loghouse-scraper-%'
    AND Timestamp >= '2025-06-10 16:14:00'
    AND Timestamp <= '2025-06-10 18:35:00'
    AND EventDate = '2025-06-10'
    AND Body = 'pushed'
    AND LA['srcTable'] = 'text_log'
    AND LA['podName'] = 'c-plum-qa-31-server-zzvuyka-0'
)
ORDER BY EventTime DESC
"""

df = client.query_df(query)

# Convert the 'start' and 'stop' columns from Unix timestamps to datetime objects
df['start'] = pd.to_datetime(df['start'], unit='s')
df['stop'] = pd.to_datetime(df['stop'], unit='s')

fig = px.timeline(df, x_start="start", x_end="stop", y="ts")

fig.update_traces(width=40)
fig.update_layout(bargap=0.1)

fig.show()

图片

上图展示了抓取时间与系统时间的对应关系,使我们可以检查是否存在重复采集的事件。借助 Plotly,我们可以精确设定矩形的起止时间,清晰地标出数据重叠的时间窗口,从而确认在该时间段内确实存在重复抓取。

图片

在这张图中,我们可视化了 LogHouse 抓取器采集到的部分表的 insert 操作耗时变化情况。

我个人比较偏好 Plotly,但我们也理解不同开发者会有不同的工具偏好。得益于 ClickHouse 强大的生态集成能力,我们的 SRE 团队可以根据自身工作流灵活选择最适合的可视化平台 —— 无论是 Hex、Bokeh、Evidence,还是其他任何支持 SQL 查询的工具。

以上,我们展示了对同一个日志事件的五种不同可视化方式,充分体现了我们在查询阶段所拥有的灵活性 —— 可以按需选择不同图表库进行展示,并且随时能够回溯至逐行的原始日志数据。

当日志搜索不够用:用 SQL 解锁更复杂的可观测性分析

HyperDX 提供了功能强大的事件搜索界面,采用 Lucene 语法,非常适合快速过滤和检索。但对于一些更复杂的可观测性问题,我们需要一套更强表达力的查询语言 —— 所幸,LogHouse 背后的引擎是 ClickHouse,我们可以随时使用完整的 SQL。

SQL 让我们可以执行跨数据源的关联、基于时间的窗口分析、复杂字段转换等操作 —— 这些能力在传统日志搜索工具中往往难以实现。比如下面这个例子:我们通过 SQL 中的 ASOF JOIN 语法,关联 Kubernetes 的 Killing 与 Created 事件,从而计算同一个容器的终止与重建之间的时间间隔。

WITH
    KE AS
    (
        SELECT *
        FROM loghouse.kube_events
        WHERE (FirstTimestamp >= '2025-03-10 01:00:00') AND (FirstTimestamp <= '2025-03-11 01:00:00') AND (Reason IN ['Killing']) AND (FieldPath LIKE 'spec.containers{c-%-server}')
    ),
    CE AS
    (
        SELECT *
        FROM loghouse.kube_events
        WHERE (FirstTimestamp >= '2025-03-10 01:00:00') AND (FirstTimestamp <= '2025-03-11 01:00:00') AND (Reason IN ['Created']) AND (FieldPath LIKE 'spec.containers{c-%-server}')
    )
SELECT
    Name,
    KE.FirstTimestamp AS killTime,
    CE.FirstTimestamp AS createTime,
    createTime - killTime AS delta,
    formatReadableTimeDelta(createTime - killTime) AS readableDelta
FROM KE
ASOF LEFT JOIN CE ON (CE.Name = KE.Name) AND (CE.FirstTimestamp >= KE.FirstTimestamp)
HAVING createTime > '1970-01-01 00:00:00'
ORDER BY delta DESC
LIMIT 5
┌─Name─────────────────────────────┬────────────killTime─┬──────────createTime─┬─delta─┬─readableDelta─────────────────────┐
│ c-emerald-tu-48-server-p0jw87g-0 │ 2025-03-10 19:01:39 │ 2025-03-10 20:15:59 │  4460 │ 1 hour, 14 minutes and 20 seconds │
│ c-azure-wb-13-server-648r93g-0   │ 2025-03-10 11:30:23 │ 2025-03-10 12:28:50 │  3507 │ 58 minutes and 27 seconds         │
│ c-azure-wb-13-server-3mjrr1g-0   │ 2025-03-10 11:30:23 │ 2025-03-10 12:28:47 │  3504 │ 58 minutes and 24 seconds         │
│ c-azure-wb-13-server-v31soea-0   │ 2025-03-10 11:30:23 │ 2025-03-10 12:28:46 │  3503 │ 58 minutes and 23 seconds         │
└──────────────────────────────────┴─────────────────────┴─────────────────────┴───────┴───────────────────────────────────┘

4 rows in set. Elapsed: 0.099 sec. Processed 17.78 million rows, 581.49 MB (180.05 million rows/s., 5.89 GB/s.)
Peak memory usage: 272.88 MiB.

当然,我们也可以将这类逻辑预先写入系统,作为一个专门的指标来监控。但 ClickHouse 的优势就在于我们不必预定义任何东西 —— 只需完整保存所有宽事件数据,在查询时再动态生成所需信息即可。因此,当有人问我们“pod 被请求终止后,p95 的重启时间是多少”时,我们无需说“我来加个新指标,下个版本再告诉你结果”,而是可以立即从已有事件中查询出答案。这样的灵活性,大大提升了我们的分析效率与响应能力。

拓展可观测性数据版图:引入更多宽事件数据源

当我们深刻意识到,将结构化、精细的遥测数据引入高性能分析引擎能释放巨大价值后,LogHouse 迅速成为工程与支持团队的首选平台。他们希望所有关键数据都能统一纳入 LogHouse。今年,我们也在文化上完成了一次转变,全面拥抱高基数、基于宽事件的可观测性方式。

以下是我们近期接入的一些重要新数据源,它们都采用了宽事件结构:

  • kubenetmon:我们开源的 Kubernetes 网络监控工具,能够深入分析集群内的网络通信。kubenetmon 利用 Linux 的 conntrack 捕获 L3/L4 层连接信息,统计字节数与包数,带来以下三项能力:流量取证(记录连接的时间序列与每分钟带宽),连接归属(将连接与具体 pod 和 workload 对应),成本计量(追踪如跨区域出口等高成本流量)。系统每分钟可处理数百万条连接记录,可用于识别跨区域下载、分析跨可用区流量模式,并将网络使用情况与成本实时关联。项目地址:https://github.com/ClickHouse/kubenetmon

  • Kubernetes Event Exporter(事件导出器):我们 fork 了这个主流导出器,并为其添加了 ClickHouse 原生支持,便于在大规模场景中分析 Kubernetes API 事件【https://github.com/ClickHouse/kubernetes-event-exporter】。这对于理解 Kubernetes 系统随时间变化的行为尤为有用。我们正在进一步扩展,不仅采集事件,还将整个 Kubernetes 对象模型接入 LogHouse,并为每次变更记录快照。未来我们将可以还原任意时刻集群的完整状态,追踪所有变更路径 —— 不只是知道“Pod X 在 15 点被终止”,而是能看清当时整个系统状态、依赖关系和后续影响。

  • 控制面数据:我们已将控制面团队的所有运维数据接入 LogHouse,此前他们尚未纳入这一平台。

  • 真实用户监控(RUM):该项目仍在建设中。我们从终端用户浏览器采集前端性能数据,并通过公开网关将其接入 OTel 流水线。

  • Istio 访问日志:我们从 Istio 服务网格中采集 HTTP 层日志,记录请求路径、响应延迟和路由决策。再结合 ClickHouse 的 system.query_log 和 kubenetmon 的网络流量信息,形成了强大的三层数据关联能力 —— 当网络流量出现异常时,支持团队可以快速还原事件全貌:哪条 SQL 查询被触发、是哪个 HTTP 请求引发的、底层数据包如何流动。通过这种全链路可见性,排障过程不再依赖猜测,而是变成了可重复、精确的根因分析。一旦发现出口流量异常,我们可以立即判断是由跨区域查询、备份操作,还是意外副本同步引起,大大提升了故障响应效率。

下一阶段的构想与前进路线

过去这一年,LogHouse 取得了飞跃式的发展。我们从一套通用方案中走出,转而构建专用、极致高效的工具体系,让整个可观测性平台在可扩展性和性价比上迈上了新台阶。集成 HyperDX 是其中的重要组成部分,它在 PB 级数据之上,为工程师带来了强大且灵活的用户体验。站在这一坚实基础上,我们非常期待接下来一年的新挑战与新成果。

向“零影响抓取”迈进

尽管 SysEx 已经非常高效且资源友好,但客户偶尔还是会在其日志或监控中注意到我们的抓取查询。虽然这些查询有严格的资源限制,实际影响微乎其微,但在敏感环境中,它们可能仍会引发误解或担忧。

为此,我们正在推进一个全新的目标 —— “零影响抓取”。它是 SysEx 的下一步演进,目标是彻底取消对在线 ClickHouse 实例的查询,将日志采集完全从运行系统中解耦。一种可能的做法,是直接读取 ClickHouse 写入 S3 的服务日志文件,由一组 SysEx worker 直接挂载这些文件表进行处理,从而避免与在线服务交互。这种架构既保留了原有方案的全部优点(原生格式、高保真、低转换成本),又进一步消除了操作层面的影响。

OpenTelemetry 仍然在我们的体系中发挥重要作用,尤其是在服务尚未完全启动、结构化日志尚不可用的 early-stage 阶段。像 crash loop 这种场景中,OTel 提供了关键日志支持。但如果我们未来的“零影响抓取”方案验证成功,也许将能进一步减少对 OTel 的依赖,为集群整个生命周期提供更一致、更低侵入的日志采集路径。

该方案目前仍在开发中,一旦在生产环境验证稳定,我们将分享更多成果。

逐步迁移至 JSON

ClickHouse 从 25.3 版本起正式 GA 支持 JSON 类型,这为我们引入更灵活的半结构化数据打下了基础。它可以根据出现的新字段动态创建列,还能处理字段类型不一致与结构膨胀等问题。

不过我们仍在评估 JSON 在大规模可观测性访问场景中的表现。比如,在整个 JSON 文档中搜索某个字符串,实际可能涉及扫描成千上万个列。对此虽然可以采用旁路方案(如同时存储原始字符串版本),但我们仍在总结最优实践。

与此同时,我们也意识到 Map 类型的边界 —— 它过去为我们很好地服务,尤其适用于字段少且结构稳定的日志。我们发现,大多数日志只需单层 JSON 即可满足需求,少数复杂情况也可借助 HyperDX 自动将 map 访问转换为 JSONExtract 表达式。我们确实计划未来更广泛采用 JSON,但这仍处于持续探索阶段,请期待我们后续的更新。

总结

LogHouse 在过去一年里,已经从一个内部日志平台演进为 ClickHouse Cloud 核心的可观测性基石,支撑着性能分析、调试排障等关键任务。从最初为降低成本而生,到如今成为驱动技术与文化变革的中枢平台,我们完成了一次深刻的转型 —— 全面拥抱高保真、宽事件、强上下文的数据模型。在这一过程中,我们将像 SysEx 这样的专用工具与 OpenTelemetry 等通用方案结合使用,再叠加 HyperDX 提供的灵活交互界面,构建出了一套既能应对增长,又能激发创新的新一代可观测性系统。

这段旅程远未结束,而我们在迈向 100PB、500 万亿行的实践中,越来越坚定地意识到:可观测性,本质上就是一个规模化的数据问题 —— 而 LogHouse 的目标,就是用数据仓库的能力去解决它。

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值