本番環境のアプリケーションでは、ログをリアルタイムで分析できることが非常に重要です。 ClickHouse は、優れた圧縮率 (ログで最大 170 倍) と大量のデータを高速に集計できる性能により、ログデータの保存と分析に優れています。
このガイドでは、一般的なデータパイプラインである Vector を使用して、Nginx のログファイルを tail し、ClickHouse に送信する方法を紹介します。 以下の手順は、どの種類のログファイルを tail する場合でもほぼ同様です。
前提条件:
- ClickHouse がすでに起動して稼働していること
- Vector がインストールされていること
データベースとテーブルを作成する
ログイベントを保存するためのテーブルを定義します。
- まず、
nginxdbという名前の新しいデータベースを作成します。
CREATE DATABASE IF NOT EXISTS nginxdb- ログイベント全体を1つの文字列として挿入します。これは明らかにログデータの分析には適したフォーマットではありませんが、この点については以下で materialized view を使って対応します。
CREATE TABLE IF NOT EXISTS nginxdb.access_logs (
message String
)
ENGINE = MergeTree()
ORDER BY tuple()Nginx を設定する
このステップでは、Nginx のログ出力を設定する方法を説明します。
- 次の
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;
}-
nginx.confを変更した場合は、必ず Nginx を再起動してください。 -
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 という設定ファイルで定義します。
- 次の vector.toml ファイルでは、my_access.log の末尾を追跡する file 型の ソース を定義し、さらに上で定義した access_logs テーブルを シンク として定義しています。
[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-
上記の設定を使用して Vector を起動します。ソースとシンクの定義について詳しくは、Vector のドキュメントを参照してください。
-
次のクエリを実行して、アクセスログが ClickHouse に挿入されていることを確認します。テーブルにアクセスログが表示されるはずです。
SELECT * FROM nginxdb.access_logs
ログの解析
ClickHouse にログを保存できるのは便利ですが、各イベントを単一の文字列として格納するだけでは、データ分析に活用できる情報が限られてしまいます。 次に、materialized view を使用してログイベントをパースする方法を説明します。
materialized view は、SQLのinsert triggerと同様の機能を持ちます。ソーステーブルにデータの行が挿入されると、materialized viewはそれらの行に変換処理を適用し、結果をターゲットテーブルに挿入します。 materialized viewは、access_logs 内のログイベントのパース済み表現を生成するように設定できます。 そのようなログイベントの例を以下に示します。
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 関数は、文字列を空白で分割し、各トークンを Array として返します。
動作を確認するには、次のコマンドを実行してください。
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\""]いくつかの文字列に余分な文字が含まれており、user agent (ブラウザの詳細情報) はパースする必要がありませんでしたが、 結果のArrayは必要な形に近いものになっています。
splitByWhitespace と同様に、splitByRegexp 関数は正規表現に基づいて文字列をArrayに分割します。
以下のコマンドを実行すると、2つのStringが返されます。
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\""]最終的な CREATE MATERIALIZED VIEW コマンドを確認する前に、データのクリーンアップに使用するいくつかの関数を見ておきましょう。
例えば、RequestMethod の値は "GET となっており、不要なダブルクォートが含まれています。
trimBoth (alias 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
シンプルなインストールと簡単な設定だけで導入できる Vector を使えば、Nginx サーバーのログを ClickHouse のテーブルに送信できます。materialized view を使うことで、それらのログをカラムにパースし、より簡単に分析できるようになります。