0
124
0
0
首页/专栏/ 技术分享/ 查看内容

官宣了:ClickHouse 与 OpenTelemetry 的集成

 admin   发表于  2024-11-25 17:26
专栏 技术分享


Meetup活动

ClickHouse 北京第二届 Meetup 讲师招募中,欢迎讲师在文末扫码报名!


今年早些时候,ClickHouse 团队决定正式支持并参与 ClickHouse 的 OpenTelemetry 导出器开发,目前该导出器的日志和追踪功能已经进入 Beta 阶段(这是目前 OTel 导出器生态系统中的最高级别)。我们希望借此机会,介绍 OpenTelemetry 与 ClickHouse 的集成。


什么是 OpenTelemetry?

OpenTelemetry(简称 OTel)是云原生计算基金会 (CNCF) 提供的开源框架,用于标准化遥测数据的收集、处理和导出。OTel 基于三大可观测性支柱(追踪、指标和日志)构建,为开发和运维团队提供了一种统一、厂商无关的方式来了解系统健康状况并诊断分布式系统中的问题。

OTel 支持多种语言的自动埋点库,只需少量代码更改即可实现数据自动收集。OTel Collector 则负责管理这些数据流,作为网关将遥测数据导出到不同的后端平台。OTel 通过提供一致的可观测性标准,帮助团队高效收集遥测数据,并深入洞察复杂系统。


为什么 OpenTelemetry 很重要?

在可观测性领域中,各种工具五花八门,OTel 提供了一个统一、灵活和开放的解决方案。随着软件系统的复杂度提升,跨微服务和云架构跟踪系统内部和系统间活动变得越来越困难。OTel 通过稳定的数据收集和分析解决了这一难题。

OTel 的厂商无关特性尤其重要,使团队避免被某一监控工具绑定。使用 OTel,组织可以轻松切换或组合不同的可观测性后端,灵活应对需求变化、降低成本。标准化的遥测数据格式简化了跨系统的集成,即便是在多云或混合环境中也能有效运作。


OpenTelemetry 与 ClickHouse 的结合

在之前的博客中,我们介绍了如何利用 ClickHouse 处理海量的可观测性数据,使其成为专有系统的开源替代方案。基于 SQL 的可观测性解决方案非常适合熟悉 SQL 的团队,既能控制成本,又具备可扩展性。随着 OTel 等开源工具的发展,对于拥有大量数据需求的组织来说,这种方法变得愈加实际可行。

SQL 可观测性架构的核心组件之一是 OpenTelemetry Collector。OTel Collector 从 SDK 或其他来源收集遥测数据并将其发送到指定的后端。它相当于一个集中枢纽,负责接收、处理和导出遥测数据。OTel Collector 可以作为单一应用的本地采集器(代理)运行,也可以作为多个应用的集中采集器(网关)运行。

Collector 包含多种支持不同数据格式的导出器。导出器将数据发送到选定的后端或可观测性平台,例如 ClickHouse。开发者可以根据需求配置多个导出器,将遥测数据路由到不同目的地。

在 ClickHouse,我们使用 OpenTelemetry 来满足自身需求,也看到许多用户成功采用这一方案,因此决定正式支持 OTel 导出器,并参与该组件的开发。


一个通用的模式

我们首先决定解决的就是表模式问题。没有“通用适用”的方案,这在设计 ClickHouse 导出器时尤其真实。像其他大型数据库一样,我们需要对插入的数据及其查询方式有清晰的理解。ClickHouse 的最佳性能依赖于根据具体用例来优化表模式。

对于 OpenTelemetry 数据,这一点更为重要。即使是 OpenTelemetry 的设计者在编写 SDK 时也遇到这一难题。如何将不同语言和工具适配到一个统一的遥测管道中?每个团队都有自己特定的日志和追踪查询习惯,因此在设计表模式时需考虑这些因素。数据保留多长时间?是按服务名称过滤,还是按其他标识符分区?是否需要按 Kubernetes Pod 名称过滤?无法满足所有需求,我们也不会牺牲性能和可用性来做到这一点。

适用于大多数需求”是我们希望达到的效果,ClickHouse 导出器的默认模式为日志、追踪和指标提供支持。默认模式在大多数常见的遥测用例中性能良好,但如果您要构建大规模日志解决方案,我们建议先了解数据在 ClickHouse 中的存储和访问方式,选择合适的主键。我们内部日志解决方案中使用的主键已经支持超过 43 PB 的 OTel 数据(截至 2024 年 10 月)。

LogHouse 的统计信息,ClickHouse Cloud 基于 OTel 的日志平台

导出器默认会自动创建所需表,但这并不推荐用于生产环境。如果您希望替换表模式而不修改导出器代码,可以直接创建自定义表。配置文件只需定义数据的目标表名。要求列名与导出器插入的内容一致,且类型与数据兼容。

{  "Timestamp": "2024-06-15 21:48:06.207795400",  "TraceId": "10c0fcd202c978d6400aaa24f3810514",  "SpanId": "60e8560ae018fc6e",  "TraceFlags": 1,  "SeverityText": "Information",  "SeverityNumber": 9,  "ServiceName": "cartservice",  "Body": "GetCartAsync called with userId={userId}",  "ResourceAttributes": {    "container.id": "4ef56d8f15da5f46f3828283af8507ee8dc782e0bd971ae38892a2133a3f3318",    "docker.cli.cobra.command_path": "docker%20compose",    "host.arch": "",    "host.name": "4ef56d8f15da",    "telemetry.sdk.language": "dotnet",    "telemetry.sdk.name": "opentelemetry",    "telemetry.sdk.version": "1.8.0"  },  "ScopeName": "cartservice.cartstore.RedisCartStore",  "ScopeAttributes": {},  "LogAttributes": {    "userId": "71155994-7b72-428a-9d51-43962a82ae43"  }}

示例:OpenTelemetry 生成的日志事件

如果默认的表模式不符合您的需求,您可以使用 ClickHouse 物化视图。默认模式提供了一个不错的起点,也可作为导出器暴露数据的参考。如果您在设计自己的表模式,可以选择添加或省略特定的列,甚至修改它们的类型。在我们的内部日志系统中,我们借此机会提取了 Kubernetes 相关的列,例如 pod 名称,并将其添加到表的主键中,以优化特定查询模式的性能。

在生产环境中,建议禁用默认的表创建功能。如果有多个导出器进程运行,可能会竞争创建表(甚至创建不同版本!)。在本指南中,我们列出了使用 ClickHouse 作为可观测性存储的最佳实践。

以下是我们为 LogHouse(ClickHouse Cloud 日志解决方案)使用的自定义模式:

CREATE TABLE otel.server_text_log_0(  `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),  `EventDate` Date,  `EventTime` DateTime,  `TraceId` String CODEC(ZSTD(1)),  `SpanId` String CODEC(ZSTD(1)),  `TraceFlags` UInt32 CODEC(ZSTD(1)),  `SeverityText` LowCardinality(String) CODEC(ZSTD(1)),  `SeverityNumber` Int32 CODEC(ZSTD(1)),  `ServiceName` LowCardinality(String) CODEC(ZSTD(1)),  `Body` String CODEC(ZSTD(1)),  `Namespace` LowCardinality(String),  `Cell` LowCardinality(String),  `CloudProvider` LowCardinality(String),  `Region` LowCardinality(String),  `ContainerName` LowCardinality(String),  `PodName` LowCardinality(String),  `query_id` String CODEC(ZSTD(1)),  `logger_name` LowCardinality(String),  `source_file` LowCardinality(String),  `source_line` LowCardinality(String),  `level` LowCardinality(String),  `thread_name` LowCardinality(String),  `thread_id` LowCardinality(String),  `ResourceSchemaUrl` String CODEC(ZSTD(1)),  `ScopeSchemaUrl` String CODEC(ZSTD(1)),  `ScopeName` String CODEC(ZSTD(1)),  `ScopeVersion` String CODEC(ZSTD(1)),  `ScopeAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),  `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),  `LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),  INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1,  INDEX idx_thread_id thread_id TYPE bloom_filter(0.001) GRANULARITY 1,  INDEX idx_thread_name thread_name TYPE bloom_filter(0.001) GRANULARITY 1,  INDEX idx_Namespace Namespace TYPE bloom_filter(0.001) GRANULARITY 1,  INDEX idx_source_file source_file TYPE bloom_filter(0.001) GRANULARITY 1,  INDEX idx_scope_attr_key mapKeys(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,  INDEX idx_scope_attr_value mapValues(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,  INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,  INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,  INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,  INDEX idx_log_attr_value mapValues(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,  INDEX idx_body Body TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 1)ENGINE = SharedMergeTreePARTITION BY EventDateORDER BY (PodName, Timestamp)TTL EventTime + toIntervalDay(180)SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1;

LogHouse 的 OTel 模式——ClickHouse Cloud 日志解决方案

关于 LogHouse 模式的一些说明:

  • 我们使用(PodName,Timestamp)作为排序键,这样可以针对用户按这些列过滤的查询模式进行优化。用户可以根据自己的需求修改排序键。

  • 对大部分字符串列,我们采用 LowCardinality(String) 类型,但对于基数较高的列则例外。通过字典编码字符串值,显著提升了压缩率和读取性能。我们的经验法则是对基数低于 1 万的字符串列应用此编码。

  • 所有列的默认压缩方式是 ZSTD,压缩级别为 1,尤其适合存储在 S3 上的数据。尽管 ZSTD 的压缩速度比 LZ4 稍慢,但更高的压缩比和快速解压(约 20% 波动)使其在 S3 存储中表现更优。

  • 在 OTel 模式中,我们对所有 map 键和值使用了布隆过滤器。这种过滤器为 map 的键和值提供了二级索引,通过布隆过滤器结构,可以在空间效率高的情况下测试集合成员资格(代价是可能有少量误判)。理论上,这使我们可以快速判断磁盘上的数据块是否包含特定的 map 键或值。某些 map 键和值通常会与 pod 名称和时间戳的排序键相关,这种情况下过滤器效果较好;但如果键和值在每个 pod 中都出现,则查询加速效果不明显(在此配置中,数据块即为粒度单位,因为 GRANULARITY=1)。进一步解释排序键与列/表达式相关性的原因,请参考相关文档【https://clickhouse.com/docs/en/optimize/skipping-indexes#skip-best-practices】。类似的规则也适用于其他列,如 Namespace。通常,布隆过滤器的使用较为广泛,还需进一步优化,这是待办事项。此外,假阳性率 0.01 也未进行调优。


下一步行动

ClickHouse 导出器仍有许多优化空间。我们的目标是确保导出器持续跟随 ClickHouse 服务器的最新更新。随着新优化的发现和性能基准测试的推进,我们可以不断改进日志、追踪和指标的默认模式。

最近 ClickHouse 支持了全新的 JSON 数据类型,这是一项影响广泛的功能,将大大简化日志和追踪中属性的存储和查询。除了新功能外,OTel+ClickHouse 的用户在代码仓库中频繁反馈意见,过去一年中,这些反馈促成了众多功能改进和问题修复。


附录:如何为 OpenTelemetry 做贡献

开源项目的魅力在于社区的协作:通过为 OpenTelemetry 做贡献,您可以直接推动可观测性的未来发展。无论是优化代码、改进文档,还是提供宝贵反馈,每一份贡献都帮助项目覆盖更多开发者。在本节,我们分享一些参与建议。

参与 OpenTelemetry 项目与其他开源项目相似,您无需成为组织成员即可贡献。无论是对某个问题提供看法,还是提交拉取请求,项目都欢迎所有贡献。

作为组件的维护者,用户反馈是最简单也是最有价值的贡献。了解用户遇到的 bug 或发现某个功能改进点能够帮助多个用户,价值巨大。虽然我们在内部使用 ClickHouse 导出器,但我们的用法不同于其他用户,我们从社区反馈中获益良多。

同时,我们也有一些用户熟悉 OpenTelemetry 和 ClickHouse,他们可以为导出器做出有创造性的贡献。比如最近的一个改进,用户将 map 属性在插入 ClickHouse 之前进行了排序。在以前的版本中,日志和追踪属性按接收顺序插入,这不总是最优,因为属性内容相同但顺序不同时压缩效果可能欠佳。通过按键对 map 属性排序,我们更好地利用了 ClickHouse 的压缩性能。这一建议最初出现在外部反馈中,一位社区用户发现了它,并提交了相关实现的拉取请求。

如果您经常参与 OpenTelemetry 项目,申请成为组织成员可能是不错的选择。社区仓库中有完整的申请流程指南,整体思路是展现您已经是社区活跃成员。成员申请通过在 GitHub 上提交一个包含您贡献列表(问题、拉取请求等)的 issue。如果现有成员认可,您的成员资格将得到批准,并可以承担更多角色。尽管成为成员不是贡献的必要条件,但它能向其他成员和访客表明您在积极参与 OTel 生态系统。


Meetup 活动讲师招募

我们正为北京活动招募讲师,如果你有独特的技术见解、实践经验或 ClickHouse 使用故事,非常欢迎你加入我们,成为这次活动的讲师,与大家分享你的经验。

点击此处或扫描下方二维码,立刻报名成为讲师!


注册ClickHouse中国社区大使,领取认证考试券

ClickHouse社区大使计划正式启动,首批过审贡献者享原厂认证考试券!


试用阿里云 ClickHouse企业版


轻松节省30%云资源成本?阿里云数据库ClickHouse架构全新升级,推出和原厂独家合作的ClickHouse企业版,在存储和计算成本上带来双重优势,现诚邀您参与100元指定规格测一个月的活动,了解详情:https://t.aliyun.com/Kz5Z0q9G


征稿启示

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


路过

雷人

握手

鲜花

鸡蛋

版权声明:本文为 clickhouse 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
返回顶部