跳转到主内容
跳转到主内容

使用 OpenTelemetry 追踪 ClickHouse

OpenTelemetry 是一个用于从分布式应用程序中收集链路追踪和指标的开放标准。ClickHouse 内置了对 OpenTelemetry 的部分支持。

向 ClickHouse 提供 trace 上下文

ClickHouse 接收符合 W3C 规范的 trace 上下文 HTTP 头部。它还可以通过一种原生协议接收 trace 上下文,该协议用于 ClickHouse 服务器之间或客户端与服务器之间的通信。对于手动测试,可以使用 --opentelemetry-traceparent--opentelemetry-tracestate 参数,将符合 Trace Context 规范的 trace 上下文头部传递给 clickhouse-client

如果未提供父级 trace 上下文,或者提供的 trace 上下文不符合上述 W3C 标准,ClickHouse 可以启动一个新的 trace,其触发概率由 opentelemetry_start_trace_probability 设置控制。

传播 trace 上下文

在以下情况下,trace 上下文会被传播到下游服务:

  • 对远程 ClickHouse 服务器的查询,例如使用 Distributed 表引擎时。

  • url 表函数。trace 上下文信息会通过 HTTP 请求头发送。

跟踪 ClickHouse Keeper 请求

ClickHouse 支持对 ClickHouse Keeper 请求(兼容 ZooKeeper 的协调服务)启用 OpenTelemetry 跟踪。借助此功能,可以对 Keeper 操作从客户端请求提交到服务端处理的完整生命周期获得细粒度可见性。

启用 Keeper 追踪

要为 Keeper 请求启用追踪,请在 ZooKeeper/Keeper 客户端配置中进行如下配置:

<clickhouse>
    <zookeeper>
        <node>
            <host>keeper1</host>
            <port>9181</port>
        </node>
        <!-- Enable OpenTelemetry tracing context propagation -->
        <pass_opentelemetry_tracing_context>true</pass_opentelemetry_tracing_context>
    </zookeeper>
</clickhouse>

Keeper Span 类型

启用 tracing 后,ClickHouse 会为 Keeper 的客户端和服务端操作创建 span:

客户端 span:

  • zookeeper.create — 创建新节点
  • zookeeper.get — 获取节点数据
  • zookeeper.set — 设置节点数据
  • zookeeper.remove — 删除节点
  • zookeeper.list — 列出子节点
  • zookeeper.exists — 检查节点是否存在
  • zookeeper.multi — 以原子方式执行多个操作
  • zookeeper.client.requests_queue — 请求在发送前排队所花费的时间

服务端 span(Keeper):

  • keeper.receive_request — 接收并解析来自客户端的请求
  • keeper.dispatcher.requests_queue — Dispatcher 中的请求排队
  • keeper.write.pre_commit — 在 Raft commit 前预处理写请求
  • keeper.write.commit — 在 Raft commit 后处理写请求
  • keeper.read.wait_for_write — 读请求等待其依赖的写请求完成
  • keeper.read.process — 处理读请求
  • keeper.dispatcher.responses_queue — Dispatcher 中的响应排队
  • keeper.send_response — 向客户端发送响应

采样与性能

为控制链路追踪开销,Keeper 实现了动态采样。采样率会根据请求大小在 1/10,000 到 1/10 之间自动调整。所有请求(无论是否被采样)其持续时间都会记录到直方图指标中,用于性能监控。

对 ClickHouse 本身进行追踪

ClickHouse 会为每个查询以及部分查询执行阶段(例如查询规划或分布式查询)创建 trace spans

为了发挥作用,追踪信息必须导出到支持 OpenTelemetry 的监控系统中,例如 JaegerPrometheus。ClickHouse 避免依赖特定监控系统,而是仅通过系统表提供追踪数据。符合 OpenTelemetry 标准要求的 trace span 信息(参见标准定义)存储在 system.opentelemetry_span_log 表中。

必须在服务器配置中启用该表,参见默认配置文件 config.xml 中的 opentelemetry_span_log 元素。该功能默认已启用。

标签或属性以两个并行数组的形式保存,分别包含键和值。使用 ARRAY JOIN 来处理它们。

Log-query-settings

log_query_settings 设置允许在查询执行期间将对查询设置所做的更改写入日志。启用后,对查询设置的任何修改都会记录到 OpenTelemetry span 日志中。此功能在生产环境中尤为有用,可用于跟踪可能影响查询性能的配置更改。

与监控系统的集成

目前,还没有可将 ClickHouse 的跟踪数据导出到监控系统的现成工具。

在测试时,可以通过在 system.opentelemetry_span_log 表之上创建一个使用 URL 引擎的物化视图来配置导出,该视图会将接收到的日志数据推送到某个 trace 收集器的 HTTP 端点。例如,要将最精简的 span 数据以 Zipkin v2 JSON 格式推送到运行在 http://localhost:9411 的 Zipkin 实例,可以这样做:

CREATE MATERIALIZED VIEW default.zipkin_spans
ENGINE = URL('http://127.0.0.1:9411/api/v2/spans', 'JSONEachRow')
SETTINGS output_format_json_named_tuples_as_objects = 1,
    output_format_json_array_of_rows = 1 AS
SELECT
    lower(hex(trace_id)) AS traceId,
    CASE WHEN parent_span_id = 0 THEN '' ELSE lower(hex(parent_span_id)) END AS parentId,
    lower(hex(span_id)) AS id,
    operation_name AS name,
    start_time_us AS timestamp,
    finish_time_us - start_time_us AS duration,
    cast(tuple('clickhouse'), 'Tuple(serviceName text)') AS localEndpoint,
    cast(tuple(
        attribute.values[indexOf(attribute.names, 'db.statement')]),
        'Tuple("db.statement" text)') AS tags
FROM system.opentelemetry_span_log

如果发生任何错误,发生错误的那部分日志数据将被静默丢弃。若数据未到达,请检查服务器日志中的错误消息。