メインコンテンツへスキップ
メインコンテンツへスキップ

Vector と ClickHouse の統合

Partner Integration

本番環境のアプリケーションでは、ログをリアルタイムに分析できることが極めて重要です。 ClickHouse は、優れた圧縮率(ログでは最大 170x)と、大量データを高速に集計できる性能により、ログデータの保存と分析に特に優れています。

このガイドでは、広く利用されているデータパイプラインである Vector を使用して Nginx のログファイルをテールし、ClickHouse に送信する方法を説明します。 以下の手順は、任意の種類のログファイルをテールする場合にもほぼ同様に適用できます。

前提条件:

  • ClickHouse がすでに稼働していること
  • Vector がインストールされていること

データベースとテーブルを作成する

ログイベントを保存するテーブルを定義します:

  1. まず、nginxdb という名前の新しいデータベースを作成します。
CREATE DATABASE IF NOT EXISTS nginxdb
  1. ログイベント全体を 1 つの文字列として挿入します。当然ながら、これはログデータに対して分析を行うのに適した形式ではありませんが、以下で materialized views を使ってこの点を解決していきます。
CREATE TABLE IF NOT EXISTS  nginxdb.access_logs (
  message String
)
ENGINE = MergeTree()
ORDER BY tuple()
注記

ORDER BYtuple()(空のタプル)に設定されています。これは、まだプライマリキーが不要なためです。

Nginx を設定する

このステップでは、Nginx のログ記録を設定する方法を説明します。

  1. 次の access_log プロパティは、ログを combined フォーマットで /var/log/nginx/my_access.log に出力します。 この値は、nginx.conf ファイルの http セクションに記述します。
http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  access_log  /var/log/nginx/my_access.log combined;
  sendfile        on;
  keepalive_timeout  65;
  include /etc/nginx/conf.d/*.conf;
}
  1. nginx.conf を変更した場合は、必ず Nginx を再起動してください。

  2. Web サーバー上のページにアクセスして、アクセスログにいくつかのログイベントを出力します。 combined 形式のログは次のようになります。

192.168.208.1 - - [12/Oct/2021:03:31:44 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
192.168.208.1 - - [12/Oct/2021:03:31:44 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
192.168.208.1 - - [12/Oct/2021:03:31:49 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"

Vector を設定する

Vector は、ログ、メトリクス、トレース(ソース と呼ばれます)を収集、変換、ルーティングし、ClickHouse との標準互換性を含む、多数の異なるベンダー(シンク と呼ばれます)に送信します。 ソースとシンクは、vector.toml という名前の設定ファイルで定義されます。

  1. 次の vector.toml ファイルでは、my_access.log の末尾を読み取り続ける file タイプの source を定義し、さらに先ほど定義した access_logs テーブルを sink として定義しています。
[sources.nginx_logs]
type = "file"
include = [ "/var/log/nginx/my_access.log" ]
read_from = "end"

[sinks.clickhouse]
type = "clickhouse"
inputs = ["nginx_logs"]
endpoint = "http://clickhouse-server:8123"
database = "nginxdb"
table = "access_logs"
skip_unknown_fields = true
  1. 上記の設定を使用して Vector を起動します。ソースおよびシンクの定義方法の詳細については、Vector のドキュメントを参照してください。

  2. 次のクエリを実行して、アクセスログが ClickHouse に挿入されていることを確認します。テーブル内にアクセスログが表示されるはずです。

SELECT * FROM nginxdb.access_logs
ClickHouse のログをテーブル形式で表示する

ログをパースする

ログを ClickHouse に保存できるのは有用ですが、各イベントを 1 つの文字列として保存するだけでは、十分なデータ分析は行えません。 次に、マテリアライズドビュー を使用してログイベントをどのようにパースするかを見ていきます。

materialized view は、SQL における INSERT トリガーと同様に機能します。ソーステーブルにデータ行が挿入されると、materialized view はそれらの行を変換し、その結果をターゲットテーブルに挿入します。 The materialized view can be configured to configure a parsed representation of the log events in access_logs. An example of one such log event is shown below:

192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"

ClickHouse には、上記の文字列を解析するためのさまざまな関数があります。splitByWhitespace 関数は、文字列を空白文字で分割し、各トークンを配列で返します。 動作を確認するには、次のコマンドを実行します。

SELECT splitByWhitespace('192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')
["192.168.208.1","-","-","[12/Oct/2021:15:32:43","+0000]","\"GET","/","HTTP/1.1\"","304","0","\"-\"","\"Mozilla/5.0","(Macintosh;","Intel","Mac","OS","X","10_15_7)","AppleWebKit/537.36","(KHTML,","like","Gecko)","Chrome/93.0.4577.63","Safari/537.36\""]

いくつかの文字列には余分な文字が含まれており、ユーザーエージェント(ブラウザ情報)は解析する必要はありませんでしたが、 結果として得られた配列は、必要なものにかなり近い形になっています。

splitByWhitespace と同様に、splitByRegexp 関数は、正規表現に基づいて文字列を配列に分割します。 次のコマンドを実行します。2 つの文字列が返されます。

SELECT splitByRegexp('\S \d+ "([^"]*)"', '192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')

2 番目に返される文字列が、ログから正しくパースされたユーザーエージェントであることに注目してください。

["192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] \"GET / HTTP/1.1\" 30"," \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36\""]

Before looking at the final CREATE MATERIALIZED VIEW command, let's view a couple more functions used to clean up the data. For example, the value of RequestMethod is "GET containing an unwanted double-quote. この二重引用符を削除するには、trimBoth(エイリアス trim 関数を使用できます。

SELECT trim(LEADING '"' FROM '"GET')

時刻文字列は先頭に角括弧が付いており、ClickHouse が日付として解析できる形式にもなっていません。 しかし、区切り文字をコロン(:)からカンマ(,)に変更すると、問題なく解析できるようになります:

SELECT parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM '[12/Oct/2021:15:32:43'), ':', ' '))

これで、materialized view を定義する準備が整いました。 以下の定義には POPULATE が含まれており、これは access_logs 内の既存の行が直ちに処理され、挿入されることを意味します。 次の SQL 文を実行します。

CREATE MATERIALIZED VIEW nginxdb.access_logs_view
(
  RemoteAddr String,
  Client String,
  RemoteUser String,
  TimeLocal DateTime,
  RequestMethod String,
  Request String,
  HttpVersion String,
  Status Int32,
  BytesSent Int64,
  UserAgent String
)
ENGINE = MergeTree()
ORDER BY RemoteAddr
POPULATE AS
WITH
  splitByWhitespace(message) as split,
  splitByRegexp('\S \d+ "([^"]*)"', message) as referer
SELECT
  split[1] AS RemoteAddr,
  split[2] AS Client,
  split[3] AS RemoteUser,
  parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM split[4]), ':', ' ')) AS TimeLocal,
  trim(LEADING '"' FROM split[6]) AS RequestMethod,
  split[7] AS Request,
  trim(TRAILING '"' FROM split[8]) AS HttpVersion,
  split[9] AS Status,
  split[10] AS BytesSent,
  trim(BOTH '"' from referer[2]) AS UserAgent
FROM
  (SELECT message FROM nginxdb.access_logs)

正常に動作したことを確認します。 アクセスログがカラムに適切にパースされていることが確認できるはずです:

SELECT * FROM nginxdb.access_logs_view
パース済みの ClickHouse ログを表形式で表示する
注記

上記のレッスンでは、データを 2 つのテーブルに保存しましたが、最初の nginxdb.access_logs テーブルを Null テーブルエンジンを使用するように変更することもできます。 パース済みデータは引き続き nginxdb.access_logs_view テーブルに格納されますが、生データはテーブルに保存されません。

シンプルなインストールと短時間の設定だけで利用できる Vector を使うと、Nginx サーバーからのログを ClickHouse のテーブルに送信できます。マテリアライズドビューを使用すれば、それらのログを列にパースして、より簡単に分析できるようにできます。