ClickHouse CloudとIcebergで実現するHot/Coldログの単一UI化

出前館のオブザーバビリティ基盤の課題とClickHouse Cloudを用いた未来では、ClickHouse Cloudをオブザーバビリティ基盤の候補としてPoCを進めている背景をまとめました。今回はその一環として、Hot/ColdのログをClickHouse Cloudから単一のUIで参照できるかを検証した結果を共有します。

背景

私たちのログは、OpenTelemetry Collector などから、既存のオブザーバビリティ基盤(以下、既存基盤)とS3の二系統に配送しています。二系統にしている理由は、既存基盤に保持期間の上限があるためです。既存基盤では直近のログ(30日)を用いて可視化・検索・アラートを行い、上限を超えるデータはコスト最適化のためS3にアーカイブします。長期の調査や監査対応ではAthenaでS3上のログをクエリしています。

現行構成でも要件は満たしていますが、UIとクエリ言語が既存基盤とAthenaで二重化し、利用者の負担が大きい状態でした。 そこで本検証では、Hot層=ClickHouse Cloud(直近の高速検索)、Cold層=S3(長期保管)としつつ、両層をClickHouse CloudのUIから統一的に検索できるか、さらにHyperDXからも同様に検索できるかを確認しました。

アーキテクチャ

HotをClickHouse(SharedMergeTree)、ColdをS3(Apache Iceberg)に分離します。Icebergの細かい説明は省きますが、オープンなテーブルフォーマットなのでAthenaやSparkなどから扱え、もちろんClickHouseからも利用できます。ColdはS3上のIcebergテーブルに長期保管し、ClickHouseから参照します。UIは共通で、ClickHouse Cloudのコンソール/HyperDXを使用し、用途に応じてHot用テーブルまたはCold用テーブルを選ぶだけ、という運用です。

HotはOpenTelemetry Collector ContribのClickHouse Exporterが作成するテーブルを使用します。ColdはAWS Glueのテーブル/カラム名の制約に合わせて小文字でスキーマを定義し、そのうえでCold側にのみViewを用意して、timestamp_time -> TimestampTimeなど列名をリネームし、Hotと同じSQLでクエリできるようにします。

なお、ClickHouse CloudとS3は同一リージョンに揃えました。レイテンシを抑え、リージョン間転送の追加コストを避けるためです。

Hot(ClickHouse / SharedMergeTree)

CREATE TABLE otel.otel_logs
(
    `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
    `TimestampTime` DateTime DEFAULT toDateTime(Timestamp),
    `TraceId` String CODEC(ZSTD(1)),
    `SpanId` String CODEC(ZSTD(1)),
    `TraceFlags` UInt8,
    `SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
    `SeverityNumber` UInt8,
    `ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
    `Body` String CODEC(ZSTD(1)),
    `ResourceSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
    `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
    `ScopeSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
    `ScopeName` String CODEC(ZSTD(1)),
    `ScopeVersion` LowCardinality(String) CODEC(ZSTD(1)),
    `ScopeAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
    `LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
...
)
ENGINE = SharedMergeTree('/clickhouse/tables/{uuid}/{shard}', '{replica}')
PARTITION BY toDate(TimestampTime)
TTL TimestampTime + toIntervalDay(30)
...

Cold(S3 / Iceberg)

CREATE TABLE otel_iceberg.otel_logs (
  timestamp_time      timestamp,
  trace_id            string,
  span_id             string,
  trace_flags         int,
  severity_text       string,
  severity_number     int,
  service_name        string,
  body                string,
  resource_schema_url string,
  resource_attributes map<string,string>,
  scope_schema_url    string,
  scope_name          string,
  scope_version       string,
  scope_attributes    map<string,string>,
  log_attributes      map<string,string>
)
PARTITIONED BY (day(timestamp_time));

ClickHouse CloudからはIceberg table engineで参照用テーブルを作成します。S3への認証にはIAMロールの委譲を使えるため、アクセスキーを埋め込まずに済みます。参照時はテーブル定義やクエリでextra_credentialsrole_arnを渡す方式です。

CREATE TABLE otel.otel_logs_iceberg
(
  `timestamp_time`      DateTime64(9),
  `trace_id`            String,
  `span_id`             String,
  `trace_flags`         UInt8,
  `severity_text`       LowCardinality(String),
  `severity_number`     UInt8,
  `service_name`        LowCardinality(String),
  `body`                String,
  `resource_schema_url` LowCardinality(String),
  `resource_attributes` Map(LowCardinality(String), String),
  `scope_schema_url`    LowCardinality(String),
  `scope_name`          String,
  `scope_version`       LowCardinality(String),
  `scope_attributes`    Map(LowCardinality(String), String),
  `log_attributes`      Map(LowCardinality(String), String)
)
ENGINE = IcebergS3(
  's3://<bucket-name>/otel_iceberg/',
  'Parquet',
  extra_credentials('role_arn'='arn:aws:iam::<account-id>:role/<role-name>')
);

そのうえで、Cold専用のViewを作ります。以降はこのViewを選べば、Hotと同じ列名でクエリできます。

CREATE VIEW otel.otel_logs_cold AS
SELECT
  timestamp_time      AS TimestampTime,
  trace_id            AS TraceId,
  span_id             AS SpanId,
  trace_flags         AS TraceFlags,
  severity_text       AS SeverityText,
  severity_number     AS SeverityNumber,
  service_name        AS ServiceName,
  body                AS Body,
  resource_schema_url AS ResourceSchemaUrl,
  resource_attributes AS ResourceAttributes,
  scope_schema_url    AS ScopeSchemaUrl,
  scope_name          AS ScopeName,
  scope_version       AS ScopeVersion,
  scope_attributes    AS ScopeAttributes,
  log_attributes      AS LogAttributes
FROM otel.otel_logs_iceberg;

参照する際はパーティションプルーニングを有効化するためにuse_iceberg_partition_pruning=1を設定します。毎回ユーザーに意識させないよう、Settings Profileを作成してロール/ユーザーに付与します。

CREATE SETTINGS PROFILE iceberg_defaults
  SETTINGS use_iceberg_partition_pruning = 1;
CREATE ROLE iceberg_role
    SETTINGS PROFILE iceberg_defaults;
SET DEFAULT ROLE <existing_default_role>, iceberg_role TO <user>;

使い方

ClickHouse Cloud

テーブルを変えるだけです。直近はotel.otel_logs、長期はotel.otel_logs_cold。列名は揃えてあるので同じSQLがそのまま動きます。見る期間に応じてどちらかのテーブルを選ぶだけです。

# <table_name> を otel.otel_logs(直近) または otel.otel_logs_cold(長期)に置き換えるだけ
SELECT *
FROM <table_name>
WHERE TimestampTime >= <from_ts>
  AND TimestampTime <  <to_ts>
  AND ServiceName = 'my-service'
  AND SeverityNumber >= 17
LIMIT 10;

また、Hot/Coldを透過的に単一データソースとして扱いたい場合は、UNION ALLを使ったビューでまとめるやり方もあります。

CREATE VIEW otel.otel_logs_union AS 
SELECT TimestampTime, ServiceName, SeverityText, SeverityNumber, Body
FROM otel.otel_logs
UNION ALL 
SELECT TimestampTime, ServiceName, SeverityText, SeverityNumber, Body
FROM otel.otel_logs_cold

ただしクエリごとにIceberg側のメタデータ参照でS3リクエストが増えやすく、レイテンシ/コスト面で最善とは言い切れません。基本はテーブル切替、透過ビューは用途を限定して使うのがよさそうです。

HyperDX

HyperDXはClickHouse上の任意テーブルをSourceとして登録し、Search / Dashboard から横断的に使えます。今回は Hot=otel.otel_logs、Cold=otel.otel_logs_coldの2つをそれぞれ別Sourceとして作成し、用途に応じてSourceを切り替える運用にします。

HyperDX Source 作成画面

HyperDXのSourceでは、どの列をタイムスタンプ/サービス名/ログレベルとして扱うかを定義します。たとえば、Timestamp ColumnにはTimestampTimetimestamp_timeなど、スキーマに合わせて柔軟に設定できます。

Search画面では、左上のSourceドロップダウンから作成済みのSource(Hot=otel.otel_logs / Cold=otel.otel_logs_cold)に切り替えるだけで検索できます。直近の調査はHot、長期の調査はColdを選ぶという運用で統一できます。

HyperDX Search 画面

活用ユースケース

データをIceberg化しておけば、オブザーバビリティ以外のデータ、たとえば売上や顧客行動といった事業データとも連携できます。 ストレージはS3のままClickHouse Cloudから直接参照でき、UI/クエリ言語を統一したまま横断分析が可能です。ツール切替や記法の違いによる負担を抑えつつ、サービス品質の変動が主要KPIにどう響いたかなどを、データ移送なしに同一クエリで検証できます。

おわりに

ClickHouse Cloud + IcebergによるHot/Coldログの単一UI化は、UI/クエリ言語切替の負担を下げる有効な選択肢になり得ると分かりました。今後は本番相当データでのベンチマークと詳細なコスト分析を行い、その結果に基づき採用可否を判断します。