OpenTelemetryによるデータ収集の統合
任意の可観測性ソリューションには、ログやトレースを収集し、エクスポートする手段が必要です。この目的のために、ClickHouseはOpenTelemetry (OTel) プロジェクトを推奨します。
「OpenTelemetryは、トレース、メトリクス、ログなどのテレメトリデータを作成し、管理するために設計された可観測性フレームワークおよびツールキットです。」
ClickHouseやPrometheusとは異なり、OpenTelemetryは可観測性バックエンドではなく、テレメトリデータの生成、収集、管理、エクスポートに焦点を当てています。OpenTelemetryの初期の目標は、ユーザーが言語特有のSDKを使用してアプリケーションやシステムを容易に計装できるようにすることでしたが、現在はOpenTelemetryコレクターを通じてログの収集も含まれるようになりました。これは、テレメトリデータを受信、処理し、エクスポートするエージェントまたはプロキシです。
ClickHouse関連コンポーネント
OpenTelemetryは多くのコンポーネントから成り立っています。データおよびAPIの仕様、標準化されたプロトコル、フィールド/カラムの命名規則を提供することに加えて、OTelはClickHouseでの可観測性ソリューションの構築に不可欠な2つの機能を提供します。
- OpenTelemetryコレクターは、テレメトリデータを受信、処理、エクスポートするプロキシです。ClickHouseを利用したソリューションは、ログ収集とイベント処理の両方にこのコンポーネントを使用します。
- テレメトリデータの仕様、API、エクスポートを実装する言語SDKです。これらのSDKは、アプリケーションのコード内でトレースが正しく記録されることを保証し、構成要素スパンを生成し、メタデータを介してサービス間でコンテキストが伝播されることを確実にすることで、分散トレースを形成し、スパンが相関できるようにします。これらのSDKは、ユーザーがコードを変更する必要がなく、即時計装を得られるようにする一般的なライブラリやフレームワークを自動的に実装するエコシステムによって補完されています。
ClickHouseを利用した可観測性ソリューションは、これらのツールの両方を活用します。
ディストリビューション
OpenTelemetryコレクターには複数のディストリビューションがあります。ClickHouseソリューションに必要なfilelogレシーバーとClickHouseエクスポーターは、OpenTelemetry Collector Contrib Distroにのみ存在します。
このディストリビューションには多くのコンポーネントが含まれており、ユーザーがさまざまな構成を試すことを可能にします。ただし、運用時には、環境に必要なコンポーネントのみを含むようコレクターを制限することをお勧めします。これにはいくつかの理由があります:
- コレクターのサイズを減らすことで、コレクターのデプロイメント時間を短縮します。
- 利用可能な攻撃対象範囲を減らすことで、コレクターのセキュリティを向上させます。
カスタムコレクターは、OpenTelemetry Collector Builderを使用して作成できます。
OTelによるデータの取り込み
コレクター展開の役割
ログを収集し、ClickHouseに挿入するために、OpenTelemetryコレクターの使用をお勧めします。OpenTelemetryコレクターは、主に2つの役割で展開できます:
- エージェント - エージェントインスタンスは、サーバーやKubernetesノードなどのエッジでデータを収集するか、OpenTelemetry SDKで計装されたアプリケーションからイベントを直接受信します。この場合、エージェントインスタンスはアプリケーションと共に、またはアプリケーションと同じホスト上(サイドカーやDaemonSetなど)で実行されます。エージェントは、データを直接ClickHouseに送信するか、ゲートウェイインスタンスに送信することができます。前者の場合、これはエージェント展開パターンと呼ばれます。
- ゲートウェイ - ゲートウェイインスタンスは、通常はクラスターごと、データセンターごと、またはリージョンごとのスタンドアロンサービス(Kubernetesのデプロイメントなど)を提供します。これらは、OTLPエンドポイントを介してアプリケーション(またはエージェントとして別のコレクター)からのイベントを受信します。通常、負荷を分散させるために、アウトオブボックスの負荷分散機能を使用されるゲートウェイインスタンスのセットが展開されます。すべてのエージェントとアプリケーションがこの単一エンドポイントに信号を送信する場合、これはゲートウェイ展開パターンと呼ばれることがよくあります。
以下では、シンプルなエージェントコレクターがイベントを直接ClickHouseに送信することを前提とします。ゲートウェイの使用と、その適用時期についての詳細はゲートウェイでのスケーリングを参照してください。
ログの収集
コレクターを使用する主な利点は、サービスがデータを迅速にオフロードできることです。これにより、コレクターが再試行、バッチ処理、暗号化、さらには機密データのフィルタリングなどの追加処理を担当します。
コレクターは、レシーバ、プロセッサ、およびエクスポータという3つの主要な処理段階の用語を使用します。レシーバはデータ収集に使用され、プル方式またはプッシュ方式のいずれかです。プロセッサはメッセージの変換や強化機能を提供します。エクスポータは、受信したデータを下流のサービスに送信する役割を担っています。このサービスは理論的には別のコレクターでも可能ですが、以下の初期の議論では、すべてのデータが直接ClickHouseに送信されると仮定します。

ユーザーが受信者、プロセッサ、エクスポータの完全なセットに慣れることをお勧めします。
コレクターは、ログを収集するための主に2つのレシーバを提供します:
OTLPを介して - この場合、ログはOpenTelemetry SDKからOTLPプロトコルを介してコレクターに直接送信されます。OpenTelemetryデモはこのアプローチを採用しており、各言語のOTLPエクスポータはローカルコレクターエンドポイントを仮定します。この場合、コレクターはOTLPレシーバで構成する必要があります。上記のデモの構成を参照してください。このアプローチの利点は、ログデータが自動的にトレースIDを含むことであり、ユーザーは後で特定のログに対するトレースを識別できるようになります。

このアプローチでは、ユーザーが適切な言語SDKでコードを計装する必要があります。
- Filelogレシーバ経由のスクレイピング - このレシーバは、ディスク上のファイルを追跡し、ログメッセージを形成し、それをClickHouseに送信します。このレシーバは、複数行メッセージの検出、ログのロールオーバーの処理、再起動に対する堅牢性のためのチェックポイント、および構造の抽出などの複雑なタスクを処理します。このレシーバは、DockerおよびKubernetesコンテナのログを追跡することもでき、helmチャートとして展開可能で、これらから構造を抽出し、ポッドの詳細でそれを強化することができます。

ほとんどの展開では、上記のレシーバを組み合わせて使用します。ユーザーは、コレクターのドキュメントを読み、基本概念や構成構造およびインストール方法について理解を深めることをお勧めします。
構造化ログと非構造化ログ
ログは構造化されているか非構造化されています。
構造化ログは、JSONのようなデータ形式を使用し、HTTPコードやソースIPアドレスなどのメタデータフィールドを定義します。
非構造化ログは、通常、正規表現パターンを介して抽出可能な固有の構造を持ちながら、ログを純粋に文字列として表現します。
ユーザーには、可能な限り構造化ログとJSON形式(つまり、ndjson)でログを記録することをお勧めします。これは、後でClickHouseに送信する前にコレクタープロセッサを使用してログを処理する際、もしくは挿入時にマテリアライズドビューを使用する際に、必要なログの処理を簡素化します。構造化ログは、最終的に後の処理リソースを節約し、ClickHouseソリューションで必要なCPUを削減します。
例
例のために、構造化(JSON)ログと非構造化ログのデータセットを各約10M行で提供します。以下のリンクから入手できます:
以下の例には構造化データセットを使用します。次の例を再現するために、このファイルがダウンロードされ、抽出されていることを確認してください。
以下は、OTelコレクターがこれらのファイルをディスクから読み取り、filelogレシーバを使用して、結果のメッセージをstdoutに出力するためのシンプルな構成を示しています。私たちのログが構造化されているため、json_parser
オペレーターを使用します。access-structured.logファイルのパスを変更してください。
以下の例では、ログからタイムスタンプを抽出します。これは、全体のログ行をJSON文字列に変換し、その結果をLogAttributes
に配置するjson_parser
オペレーターの使用を必要とします。これは計算的に高価になる可能性があり、ClickHouseではより効率的に行えます - SQLでの構造抽出を参照してください。等価な非構造化の例は、[regex_parser](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/stanza/docs/operators/regex_parser.md)
を使用してこれを実現するものがこちらで見つかります。
ユーザーは、公式の指示に従って、ローカルにコレクターをインストールできます。重要なのは、指示をContribディストリビューションを使用するように変更することです(filelog
レシーバを含みます)。たとえば、otelcol_0.102.1_darwin_arm64.tar.gz
の代わりにユーザーはotelcol-contrib_0.102.1_darwin_arm64.tar.gz
をダウンロードする必要があります。リリースはこちらで見つけることができます。
インストール後、OTelコレクターは以下のコマンドで実行できます。
構造化ログを使用していると仮定すると、メッセージは以下の形式になります:
上記はOTelコレクターが生成した単一のログメッセージを表しています。これらのメッセージは、後のセクションでClickHouseに取り込まれます。
ログメッセージの完全なスキーマは、他のレシーバを使用する場合に存在する可能性のある追加カラムとともに、こちらに保たれています。ユーザーはこのスキーマに慣れることを強くお勧めします。
ここでの重要な点は、ログ行自体がBody
フィールド内に文字列として保持されますが、JSONはAttributes
フィールドに自動抽出されていることです。この同じオペレーターが、適切なTimestamp
カラムにタイムスタンプを抽出するために使用されています。OTelによるログ処理の推奨事項については、処理を参照してください。
オペレーターは、ログ処理の最も基本的な単位です。各オペレーターは、ファイルから行を読み取る、またはフィールドからJSONを解析するなど、単一の責任を果たします。その後、オペレーターはパイプラインでチェーンして、所望の結果を達成します。
上記のメッセージにはTraceID
やSpanID
フィールドがありません。ユーザーが分散トレーシングを実装している場合など、これらが存在する場合は、上記と同様の手法でJSONから抽出できます。
ローカルまたはKubernetesのログファイルを収集する必要のあるユーザーには、filelogレシーバの利用可能な構成オプションや、オフセットおよびマルチラインログ解析の取扱いについても理解することをお勧めします。
Kubernetesログの収集
Kubernetesログの収集には、OpenTelemetryのドキュメントガイドを推奨します。Kubernetes Attributes Processorは、ポッドメタデータでログとメトリクスを強化するために推奨されます。これにより、ラベルなどの動的メタデータが生成され、ResourceAttributes
カラムに保存される可能性があります。ClickHouseは現在、このカラムに対してMap(String, String)
型を使用しています。マップの取り扱いおよび最適化の詳細については、マップの使用およびマップからの抽出を参照してください。
トレースの収集
コードを計装してトレースを収集したいユーザーには、公式のOTelドキュメントに従うことをお勧めします。
ClickHouseにイベントを配信するには、適切なレシーバ経由でOTLPプロトコルを通じてトレースイベントを受信するOTelコレクターを展開する必要があります。OpenTelemetryデモは、サポートされている各言語の計装例を提供し、イベントをコレクターに送信します。以下の構成例は、イベントをstdoutに出力するための適切なコレクター構成を示しています。
例
トレースはOTLP経由で受信する必要があるため、telemetrygen
ツールを使用してトレースデータを生成します。インストール手順はこちらを参照してください。
以下の構成は、OTLPレシーバでトレースイベントを受信し、stdoutに送信します。
以下のコマンドを使用してこの構成を実行します:
telemetrygen
を使用してコレクターにトレースイベントを送信します:
これにより、以下のようなトレースメッセージがstdoutに出力されます:
上記はOTelコレクターによって生成された単一のトレースメッセージを表しています。これらのメッセージは、後のセクションでClickHouseに取り込まれます。
トレースメッセージの完全なスキーマは、こちらに保たれています。ユーザーはこのスキーマに慣れることを強くお勧めします。
処理 - フィルタリング、変換、強化
前述のログイベントのタイムスタンプを設定する例で示されたように、ユーザーは必然的にイベントメッセージをフィルタリング、変換、強化したいと考えます。これは、OpenTelemetryのいくつかの機能を使用して実現できます:
-
プロセッサ - プロセッサはレシーバによって収集されたデータを修正または変換し、エクスポータに送信します。プロセッサは、コレクターの構成の
processors
セクションで設定された順序で適用されます。これらはオプションですが、最小限のセットは通常推奨されます。ClickHouseを使用するOTelコレクターを使用する際は、プロセッサの制限をお勧めします:memory_limiter
(https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/memorylimiterprocessor/README.md)は、コレクターでメモリ不足の状況を防ぐために使用されます。推奨事項については[リソースの見積もり](#estimating-resources)を参照してください。- コンテキストに基づいて強化を行うプロセッサ。例えば、Kubernetes Attributes Processorは、k8sメタデータによってスパン、メトリクス、ログリソース属性を自動的に設定できるスパンでイベントを強化します。
- 必要に応じてトレース用のテールまたはヘッドサンプリング。
- 基本的なフィルタリング - オペレーター(以下参照)を介してこれができない場合に必要ないイベントを削除します。
- バッチ処理 - ClickHouseで作業する際にイベントがバッチとして送信されることを確認するために重要です。"ClickHouseへのエクスポート"を参照してください。
-
オペレーター - オペレーターは、レシーバで利用できる最も基本的な処理単位を提供します。基本的な解析がサポートされており、重要度やタイムスタンプなどのフィールドを設定できます。ここではJSONおよび正規表現解析がサポートされ、イベントのフィルタリングや基本的な変換が可能です。ここでイベントフィルタリングを行うことを推奨します。
ユーザーは、オペレーターや変換プロセッサを使用して過度なイベント処理を行わないことを推奨します。これは、特にJSON解析時にかなりのメモリおよびCPUオーバーヘッドを引き起こす可能性があります。特定の例外を除き、マテリアライズドビューやカラムでClickHouseで挿入時にすべての処理を行うことが可能です - 特に、k8sメタデータの追加などのコンテキスト認識の強化が必要です。詳細については、SQLでの構造抽出を参照してください。
OTelコレクターを使用して処理を行う場合、ゲートウェイインスタンスで変換を行い、エージェントインスタンスでの作業を最小限に抑えることをお勧めします。これにより、サーバー上で動作するエッジのエージェントが必要とするリソースを可能な限り最小限に抑えることができます。通常、ユーザーはフィルタリング(不必要なネットワーク使用を最小限に抑えるため)、タイムスタンプ設定(オペレーターを介して)、コンテキストを必要とする強化をエージェントで実行します。たとえば、ゲートウェイインスタンスが異なるKubernetesクラスターに居住する場合、k8sの強化はエージェント内で発生する必要があります。
例
以下の構成は、非構造化ログファイルの収集を示しています。ログ行から構造を抽出するためのオペレーター(regex_parser
)とイベントをフィルタリングし、イベントをバッチ化およびメモリ使用量を制限するためのプロセッサの使用に注意してください。
config-unstructured-logs-with-processor.yaml
ClickHouseへのエクスポート
エクスポータは、1つ以上のバックエンドまたは宛先にデータを送信します。エクスポータはプルまたはプッシュ方式にできます。ClickHouseにイベントを送信するには、プッシュベースのClickHouseエクスポータを使用する必要があります。
ClickHouseエクスポータはOpenTelemetry Collector Contribの一部であり、コアディストリビューションの一部ではありません。ユーザーは、contribディストリビューションを使用するか、独自のコレクターを構築できます。
完全な構成ファイルは、以下のように示されています。
[clickhouse-config.yaml](https://www.otelbin.io/#config=receivers%3A*N_filelog%3A*N___include%3A*N_____-_%2Fopt%2Fdata%2Flogs%2Faccess-structured.log*N___start*_at%3A_beginning*N___operators%3A*N_____-_type%3A_json*_parser*N_______timestamp%3A*N_________parse*_from%3A_attributes.time*_local*N_________layout%3A_*%22*.Y-*.m-*.d_*.H%3A*.M%3A*.S*%22*N_otlp%3A*N____protocols%3A*N______grpc%3A*N________endpoint%3A_0.0.0.0%3A4317*N*Nprocessors%3A*N_batch%3A*N___timeout%3A_5s*N___send*_batch*_size%3A_5000*N*N Nexporters%3AN_clickhouse%3AN___endpoint%3A_tcp%3A%2F%2Flocalhost%3A9000QdialtimeoutE10sAcompressElz4Aasync_insertE1*N__H_ttl%3A_72hN___traces_tablename%3A_otel_tracesN___logs_tablename%3A_otel_logsN___create_schema%3A_trueN___timeout%3A_5sN___database%3A_defaultN___sending_queue%3AN_____queue_size%3A_1000N___retry_onfailure%3AN_____enabled%3A_trueN_____initial_interval%3A_5sN_____max_interval%3A_30sN_____max_elapsedtime%3A_300sNNservice%3AN_pipelines%3AN___logs%3A*N_____receivers%3A%5Bfilelog%5D*N_____processors%3A%5Bbatch%5D*N_____exporters%3A%5Bclickhouse%5DN___traces%3AN____receivers%3A%5Botlp%5DN____processors%3A_%5Bbatch%5DN____exporters%3A_%5Bclickhouse%5D%7E&distro=otelcol-contrib%7E&distroVersion=v0.103.1%7E)
以下の主要な設定に注意してください:
- pipelines - 上記の構成は、ログとトレースのための一連のレシーバ、プロセッサ、エクスポータから成るパイプラインの使用を強調しています。
- endpoint - ClickHouseとの通信は
endpoint
パラメーターを介して構成されます。接続文字列tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1
により、通信がTCP経由で行われます。ユーザーがトラフィックスイッチの理由でHTTPを好む場合は、こちらで説明されているように、この接続文字列を変更します。ユーザー名およびパスワードをこの接続文字列内で指定する機能が含まれる完全な接続の詳細については、こちらで説明されています。
重要: 上記の接続文字列は、圧縮(lz4)および非同期挿入の両方を有効にします。両方を常に有効にすることをお勧めします。非同期挿入に関する詳細はバッチ処理を参照してください。圧縮は常に指定する必要があり、旧バージョンのエクスポータではデフォルトで無効になっています。
- ttl - ここでの値はデータが保持される期間を決定します。"データの管理"に関する詳細をご覧ください。これは、72hのような時間単位で指定する必要があります。下記の例ではデータが2019年のものであり、ClickHouseに挿入されるとすぐに削除されるためTTLを無効にしています。
- traces_table_nameおよびlogs_table_name - ログおよびトレーステーブルの名前を決定します。
- create_schema - 起動時にデフォルトスキーマでテーブルが作成されるかどうかを決定します。入門用にはtrueにデフォルト設定されています。ユーザーはこれをfalseに設定し、自分自身のスキーマを定義する必要があります。
- database - 対象データベースです。
- retry_on_failure - 失敗したバッチを再試行するかどうかを決定する設定です。
- batch - バッチプロセッサは、イベントがバッチとして送信されることを保証します。5000程度の値と5秒のタイムアウトを推奨します。どちらが早く到達してもバッチのフラッシュがトリガーされます。これらの値を下げることで、遅延の少ないパイプラインを実現し、より早くクエリー可能なデータが得られますが、ClickHouseへの接続やバッチが増えるという犠牲が伴います。非同期挿入(https://clickhouse.com/blog/asynchronous-data-inserts-in-clickhouse)を使用していない場合は、ClickHouse内の[パーツが多すぎる(too many parts)](https://clickhouse.com/blog/common-getting-started-issues-with-clickhouse#1-too-many-parts)という問題を引き起こす可能性があるため、これは推奨されません。逆に、非同期挿入を使用している場合は、クエリーのためのデータの可用性も非同期挿入の設定に依存します - ただし、コネクタからのデータはより早くフラッシュされます。詳細については、バッチ処理を参照してください。
- sending_queue - 送信キューのサイズを制御します。キュー内の各アイテムにはバッチが含まれています。このキューを超えると(たとえば、ClickHouseが到達不可能になった場合でもイベントが到着し続ける場合)、バッチがドロップされます。
ユーザーが構造化ログファイルを抽出し、ローカルインスタンスのClickHouseが実行されていると仮定すると、以下のコマンドを使用してこの構成を実行できます:
トレースデータをこのコレクターに送信するには、以下のコマンドをtelemetrygen
ツールを使用して実行します:
実行中に、シンプルなクエリーでログイベントが存在することを確認します:
Out of the box schema
デフォルトで、ClickHouseエクスポーターは、ログとトレースのためのターゲットログテーブルを作成します。これは、設定 create_schema
によって無効にすることができます。さらに、ログとトレーステーブルの名前は、上記の設定を通じてデフォルトの otel_logs
と otel_traces
から変更できます。
以下のスキーマでは、TTLが72時間に有効になっていると仮定します。
ログのデフォルトスキーマは以下の通りです(otelcol-contrib v0.102.1
):
ここでのカラムは、ここに記載されている OTel公式仕様のログに関連しています。
このスキーマに関するいくつかの重要な注意事項:
- デフォルトでは、テーブルは
PARTITION BY toDate(Timestamp)
で日付ごとにパーティション分けされています。これにより、有効期限が切れたデータを効率的に削除できます。 - TTLは
TTL toDateTime(Timestamp) + toIntervalDay(3)
により設定され、コレクター設定で設定された値に対応します。ttl_only_drop_parts=1
は、全ての行が期限切れになっている場合のみ、全体が削除されることを意味します。これは、パーツ内の行を削除するよりも効率的です。行の削除には高コストがかかりますので、常にこの設定を推奨します。詳細は データ管理とTTL をご覧ください。 - テーブルはクラシックな
MergeTree
エンジン を使用します。これはログとトレースのために推奨され、変更する必要はありません。 - テーブルは
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId)
で順序付けされています。これにより、ServiceName
、SeverityText
、Timestamp
、およびTraceId
に対するフィルターに対してクエリが最適化されます - リスト内の早いカラムは遅いカラムよりも早くフィルターされます。例えば、ServiceName
でフィルターすることはTraceId
でフィルターするよりも遥かに早くなります。ユーザーは、想定されるアクセスポイントに応じてこの順序を調整する必要があります - 主キーの選定 を参照してください。 - 上記のスキーマは、カラムに
ZSTD(1)
を適用します。これはログのために最適な圧縮を提供します。ユーザーは、より良い圧縮のためにZSTD圧縮レベル(デフォルトの1以上)を上げることができますが、これはめったに有益ではありません。この値を上げると、挿入時(圧縮中)にCPUオーバーヘッドが増加しますが、データの非圧縮(およびクエリ)は依然として同等のままであるべきです。詳細はこちらをご覧ください。追加のデルタエンコーディング がタイムスタンプに適用され、ディスク上のサイズを削減することを目指しています。 ResourceAttributes
、LogAttributes
、およびScopeAttributes
がマップとして定義されている点に注意してください。これらの違いについてユーザーは理解する必要があります。これらのマップにアクセスし、キーの最適化されたアクセス方法を見るには、マップの使用 を参照してください。- ここでの他のほとんどの型(例えば、
ServiceName
はLowCardinalityとして)も最適化されています。Body
は、私たちの例のログでJSONであるため、Stringとして格納されています。 - ブルームフィルターがマップキーと値、さらに
Body
カラムにも適用されます。これにより、これらのカラムにアクセスするクエリの時間が改善されますが、通常は必要ありません。詳細はセカンダリーデータスキッピングインデックスを参照してください。
再度、これは、ここに記載されている OTel公式仕様のトレースに対応するカラムと関連があります。ここでのスキーマは、上記のログスキーマと同様の設定を多く使用しており、スパンに特有の追加Linkカラムがあります。
ユーザーには自動スキーマ作成を無効にして、手動でテーブルを作成することをお勧めします。これにより、主キーおよび副キーの変更、クエリパフォーマンスを最適化するための追加カラムの導入が可能になります。詳細については、スキーマ設計をご覧ください。
Optimizing inserts
高挿入パフォーマンスを達成しながら強力な整合性保証を得るために、ユーザーはコレクターを介してClickHouseに可観測データを挿入する際に、シンプルなルールに従うべきです。OTelコレクターが正しく構成されている場合、以下のルールは簡単に従うことができるはずです。これにより、ClickHouseを初めて使用する際のユーザーが直面する一般的な問題を回避できます。
Batching
デフォルトでは、ClickHouseに送信された各挿入は、ClickHouseが直ちに挿入のデータと保存する必要のあるその他のメタデータを含むストレージのパートを作成させます。したがって、より多くのデータを含む少量の挿入を送信することは、少量のデータを含む多数の挿入を送信することに比べて、必要な書き込みの数を減少させます。ユーザーには、少なくとも1,000行の比較的大きなバッチでデータを挿入することをお勧めします。詳細はこちらをご覧ください。
デフォルトでは、ClickHouseへの挿入は同期的で、同一である場合は冪等性があります。マージツリーエンジンファミリーのテーブルの場合、ClickHouseはデフォルトで自動的に重複排除を行います。これは、次のような場合に挿入が許容されることを意味します:
- (1) データを受信するノードに問題がある場合、挿入クエリはタイムアウトし(またはより具体的なエラーが発生します)、確認が返されません。
- (2) ノードによってデータが書き込まれた場合、ネットワークの中断によってクエリの送信者に確認を返すことができない場合、送信者はタイムアウトまたはネットワークエラーを受け取ります。
コレクターの視点から見ると、(1)と(2)は区別が難しいことがあります。しかし、いずれの場合も、未確認の挿入はただちに再試行することができます。再試行した挿入クエリに、同じ順序で同じデータが含まれている限り、ClickHouseは元の(未確認の)挿入が成功した場合に再試行した挿入を自動的に無視します。
ユーザーには、上記の要件を満たす一貫した行のバッチが送信されることを確保するために、先に示したバッチプロセッサを使用することをお勧めします。コレクターが高スループット(秒あたりのイベント数)を持つことが期待される場合、各挿入で少なくとも5000イベントを送信できる場合、これは通常、パイプラインに必要な唯一のバッチ処理です。この場合、コレクターは、バッチプロセッサの timeout
に達する前にバッチをフラッシュし、パイプラインのエンドツーエンドのレイテンシが低く保たれるようにし、バッチが一貫したサイズであることを確保します。
Use asynchronous inserts
通常、ユーザーはコレクターのスループットが低いときに小さなバッチを送信せざるを得ず、それでもデータが最低限のエンドツーエンドのレイテンシ内でClickHouseに到達することを期待します。この場合、バッチプロセッサの timeout
が期限切れになると小さなバッチが送信されます。これが問題を引き起こす可能性があり、この場合は非同期挿入が必要です。このケースは、エージェント役割のコレクターが直接ClickHouseに送信するように設定されているときに一般的に発生します。ゲートウェイは、集約者として機能することでこの問題を軽減できます - ゲートウェイによるスケーリングを参照してください。
大きなバッチを保証できない場合、ユーザーは非同期挿入を使用してClickHouseにバッチ処理を委任できます。非同期挿入では、データはまずバッファに挿入され、その後、データベースストレージに書き込まれます。

非同期挿入が有効な状態で、ClickHouseが① 挿入クエリを受信すると、クエリのデータが② まずメモリ内バッファに直ちに書き込まれます。③ 次のバッファフラッシュが行われると、バッファのデータはソートされ、データベースストレージにパートとして書き込まれます。データはデータベースストレージにフラッシュされる前はクエリによって検索可能ではない点に注意してください;バッファフラッシュは構成可能です。
コレクター用に非同期挿入を有効にするには、接続文字列に async_insert=1
を追加します。ユーザーには配信保証を得るために wait_for_async_insert=1
(デフォルト)を使用することを推奨します - 詳細はこちらをご覧ください。
非同期挿入からのデータは、ClickHouseバッファがフラッシュされた後に挿入されます。これは、async_insert_max_data_size
を超えた場合、または最初のINSERTクエリからasync_insert_busy_timeout_ms
ミリ秒後に発生します。async_insert_stale_timeout_ms
が0より大きい値に設定されている場合、データは前回のクエリからasync_insert_stale_timeout_ms
ミリ秒後に挿入されます。ユーザーは、これらの設定を調整してパイプラインのエンドツーエンドのレイテンシを制御できます。バッファフラッシュを調整するために使用できるさらに詳しい設定は、ここに記載されています。一般的に、デフォルトは適切です。
エージェントの数が少なく、スループットが低いが厳しいエンドツーエンドのレイテンシ要件がある場合、適応型非同期挿入が役立つかもしれません。一般的に、これらはClickHouseで見る高スループットの可観測性ユースケースには適用されません。
最後に、ClickHouseへの同期挿入に関連した以前の重複排除動作は、非同期挿入を使用しているときにはデフォルトで有効になりません。必要な場合は、設定async_insert_deduplicate
を参照してください。
この機能の構成に関する詳細はこちらにあり、より深い内容はこちらで確認できます。
Deployment architectures
OTelコレクターをClickHouseで使用する際に、いくつかのデプロイメントアーキテクチャが可能です。以下にそれぞれについて、その適用可能性を説明します。
Agents only
エージェントのみのアーキテクチャでは、ユーザーはOTelコレクターをエージェントとしてエッジにデプロイします。これらはローカルアプリケーション(例:サイドカーコンテナ)からトレースを受信し、サーバーやKubernetesノードからログを収集します。このモードでは、エージェントはデータを直接ClickHouseに送信します。

このアーキテクチャは、小規模から中規模のデプロイに適しています。主な利点は、追加のハードウェアを必要とせず、ClickHouse可観測ソリューションのリソースフットプリントを最小限に抑え、アプリケーションとコレクターの間にシンプルなマッピングを維持できることです。
エージェントが数百を超えた場合は、ゲートウェイベースのアーキテクチャへの移行を検討すべきです。このアーキテクチャには、スケールが難しいいくつかの欠点があります:
- 接続のスケーリング - 各エージェントはClickHouseへの接続を確立します。ClickHouseは数百(場合によっては数千)の同時挿入接続を維持することができますが、最終的には制約の要因となり、挿入を効果的でなくします - つまり、接続を維持するためにClickHouseがより多くのリソースを消費するようになります。ゲートウェイを使用すると、接続の数を最小限にし、挿入をより効率的にします。
- エッジでの処理 - このアーキテクチャでは、エッジまたはClickHouseで変換やイベント処理を行う必要があります。これにより、制約が生じ、複雑なClickHouseマテリアライズドビューや、重要なサービスに影響を与える可能性がある重要な計算をエッジに押し込むことになります。
- 小さなバッチとレイテンシ - エージェントコレクターは非常に少数のイベントを個別に収集する場合があります。これにより、配信SLAを満たすために設定した間隔でフラッシュする必要が生じます。これにより、コレクターがClickHouseに小さなバッチを送信することになります。これは欠点ですが、非同期挿入で緩和できます - 挿入の最適化を参照してください。
Scaling with gateways
OTelコレクターは、上記の制限に対処するためにゲートウェイインスタンスとして展開することができます。これらは、通常、データセンターや地域ごとのスタンドアロンサービスを提供します。これらは、アプリケーション(またはエージェント役割の他のコレクター)からのイベントを単一のOTLPエンドポイントを介して受信します。通常、一連のゲートウェイインスタンスが展開され、負荷を分散するためにボックスから出たロードバランサーが使用されます。

このアーキテクチャの目的は、エージェントから計算集約処理をオフロードし、リソース使用量を最小限に抑えることです。これらのゲートウェイは、エージェントが行う必要のある変換タスクを実行することができます。さらに、複数のエージェントからのイベントを集約することにより、ゲートウェイはClickHouseに大きなバッチを送信できるようにし、効率的な挿入を可能にします。これらのゲートウェイコレクターは、より多くのエージェントが追加され、イベントスループットが増加するにつれて簡単にスケールできます。以下は、関連するエージェント構成とともに、例のゲートウェイ構成を示します。エージェントとゲートウェイ間の通信にはOTLPが使用されている点に注意してください。
clickhouse-gateway-config.yaml
これらの構成は、以下のコマンドで実行できます。
このアーキテクチャの主な欠点は、コレクターの管理に関連するコストとオーバーヘッドです。
ゲートウェイベースのアーキテクチャを管理するための例とそれに関連する学びの例については、このブログ記事をお勧めします。
Adding Kafka
上記のアーキテクチャがメッセージキューとしてKafkaを使用していないことにお気づきかもしれません。
Kafkaキューをメッセージバッファとして使用することは、ログアーキテクチャで見られる一般的な設計パターンであり、ELKスタックによって普及しました。これにはいくつかの利点があります;主に、強力なメッセージ配信保証を提供し、バックプレッシャーに対処するのに役立ちます。メッセージは、収集エージェントからKafkaに送信され、ディスクに書き込まれます。理論的には、クラスタ化されたKafkaインスタンスは、高スループットメッセージバッファを提供すべきです。これは、メッセージを解析および処理するよりも、ディスクにリニアに書き込む方が少ない計算オーバーヘッドがかかるためです。例えば、Elasticの場合、トークン化とインデックス作成には多くのオーバーヘッドがかかります。データをエージェントから遠ざけることにより、ソースでのログローテーションの影響でメッセージが失われるリスクも減少します。最後に、いくつかのメッセージ再実行およびクロスリージョンの複製機能が提供されており、一部のユースケースにとっては魅力的かもしれません。
しかし、ClickHouseはデータを非常に迅速に挿入でき、適度なハードウェアで毎秒数百万行の挿入が可能です。ClickHouseからのバックプレッシャーは 稀 です。しばしば、Kafkaキューを利用することは、より多くのアーキテクチャの複雑さやコストを伴います。ログが銀行取引や他の重要なデータと同じ配信保証を必要としないという原則を受け入れられるのであれば、Kafkaの複雑さは避けることをお勧めします。
さて、高い配信保証やデータの再実行の能力(複数のソースへの可能性)が必要な場合、Kafkaは有用なアーキテクチャ追加となる可能性があります。

この場合、OTelエージェントは、Kafkaエクスポーター を介してデータをKafkaに送信するように構成できます。ゲートウェイインスタンスは、Kafkaレシーバーを使用してメッセージを消費します。さらなる詳細については、ConfluentおよびOTelのドキュメントをお勧めします。
Estimating resources
OTelコレクターのリソース要件は、イベントのスループット、メッセージのサイズ、および実行される処理の量によって異なります。OpenTelemetryプロジェクトは、リソース要件を推定するためのベンチマークを維持しています。
私たちの経験では、3つのコアと12GBのRAMを持つゲートウェイインスタンスは、毎秒約60kのイベントを処理できます。これは、フィールド名の変更を行う最小限の処理パイプラインが責任を負っている場合の想定です。
イベントをゲートウェイに送信し、イベントのタイムスタンプのみを設定するエージェントインスタンスの場合、ユーザーは予想される毎秒のログに基づいてサイズを考慮することをお勧めします。以下は、ユーザーがスタートポイントとして使用できる近似値を示したものです:
ロギングレート | コレクターエージェントに必要なリソース |
---|---|
1k/秒 | 0.2CPU, 0.2GiB |
5k/秒 | 0.5 CPU, 0.5GiB |
10k/秒 | 1 CPU, 1GiB |