ClickHouse - ClickVisual 構建日誌平臺
越來越多的互聯網公司開始嘗試 ClickHouse 存儲日誌,比如映客、快手、攜程、唯品會、石墨文檔,但是 ClickHouse 存儲日誌缺少對應的可視化方案,石墨文檔開源了 ClickVisual 用於解決這個問題。筆者初步嘗試了一下 ClickVisual,一點小小的實踐經驗,與各位分享。
簡介
ClickVisual 官方宣揚的核心功能是:輕量級日誌查詢、分析、報警可視化平臺。報警這塊有更好的方案,我這裏主要嘗試一下接入日誌、存儲、查詢日誌的整個流程。ClickVisual 的相關資料地址:
文檔:https://clickvisual.net/
代碼:https://github.com/clickvisual/clickvisual
架構
ClickVisual 只是一個 web 端,查詢日誌並展示,並不參與日誌流的處理,日誌流主要是通過 LogAgent、Kafka、ClickHouse 來協同處理,ClickVisual 主要是對 ClickHouse 的表結構做一些調整,來控制 ClickHouse 對日誌的處理過程。整個數據流如下:
ClickVisual 不關心採集,用什麼 agent 都行,只是對進入 Kafka 中的日誌格式有要求,要求日誌中包括時間字段和日誌原文字段。ClickVisual 官網有 fluentbit、ilogtail、loggie 的相關文檔,fluentbit 的文檔最爲詳細,看來石墨的朋友內部主要是使用 fluentbit 做採集器。後面我會使用 categraf 做數據採集,categraf 中的日誌採集邏輯是 fork 自 datadog-agent,比較穩定可靠。不過 categraf 沒有日誌清洗能力,如果想對日誌格式做清洗,需要引入 logstash 或者 vector。我這裏重點想嘗試 ClickVisual,所以採集側就簡單搞,使用 categraf 採集 json 格式的日誌,然後直接進入 Kafka。
ilogtail: https://ilogtail.gitbook.io/ilogtail-docs/about/readme
loggie: https://loggie-io.github.io/docs/
categraf: https://github.com/flashcatcloud/categraf
通常,不同的 log stream 進入不同的 Kafka topic,每個 Kafka topic 對應 ClickVisual 裏的一個日誌庫,日誌庫通常包含兩個 ClickHouse Table + 一個物化視圖,一個 Table 是 Kafka 引擎類型的 Table,用於消費 Kafka 中的原始日誌,然後通過物化視圖流式處理原始日誌,做一些數據 ETL 之後寫入日誌結果 Table。比如日誌原文可能是 json 格式,通過物化視圖把 json 日誌原文裏的某個字段提取出來,作爲日誌結果 Table 的一個一等公民字段,可以提升查詢篩選性能。
安裝
日誌的處理流比指標要複雜,涉及的組件比較多,這裏我會安裝 Kafka 用於日誌傳輸,Kafka 依賴 Zookeeper,Kafka 的可視化使用 Kowl,日誌採集使用 Categraf,日誌存儲使用 ClickHouse,日誌可視化使用 ClickVisual,ClickVisual 依賴 MySQL 和 Redis,所以,總共需要安裝 8 個組件,我會盡可能使用二進制安裝,方便摸清箇中原理。
Kafka
Kafka 最新的版本是 3.6.0,直接下載最新版本安裝,下載的包裏包含 Zookeeper,所以 Zookeeper 不需要單獨下載包。當然,Kafka、Zookeeper 都是依賴 JDK,JDK 請列位自行安裝和配置。
- Kafka 下載地址:https://downloads.apache.org/kafka/3.6.0/kafka_2.13-3.6.0.tgz
下載之後解壓縮,修改一下 config/zookeeper.properties
,調整 dataDir 配置,不要放 /tmp
目錄。然後啓動 Zookeeper:
./bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
然後修改 Kafka 的配置:config/server.properties
,修改 log.dirs,也是更換一下目錄,不要使用 /tmp
。然後啓動 Kafka:
nohup bin/kafka-server-start.sh config/server.properties &> kafka.stdout &
請自行檢查 Zookeeper 和 Kafka 是否啓動成功,可以使用 jps
命令查看進程,也可以使用 netstat -tunlp
查看端口。
[root@VM-0-33-debian:~/tarball/kafka_2.13-3.6.0# jps
1148293 Jps
606735 QuorumPeerMain
608066 Kafka
Kafka 數據查看使用 Kowl,不過 Kowl 沒有找到二進制,官網建議使用容器,但是我的 Kafka 和 Zookeeper 都沒有用容器,所以 Kowl 使用容器安裝,但是要使用 host network,命令如下:
docker run --network=host -d -e KAFKA_BROKERS=localhost:9092 quay.io/cloudhut/kowl:master
kowl 如果啓動成功,會監聽在 8080 端口,頁面長這樣:
ClickHouse
ClickHouse 的安裝比較簡單,官方提供安裝腳本,直接下載執行即可,curl 命令結束之後會拿到一個 clickhouse 二進制,然後執行 ./clickhouse install
就可以安裝了,安裝的時候會提示設置密碼,我這裏測試,設置爲 1234。
curl https://clickhouse.com/ | sh
./clickhouse install
clickhouse start
ClickVisual
ClickVisual 的安裝依賴 MySQL 和 Redis,這倆太常見了大家自行搞定。ClickVisual 我也直接體驗最新版本,v1.0.0-rc9,下載之後解壓縮,看一下 help 信息:
mkdir clickvisual && cd clickvisual
wget https://github.com/clickvisual/clickvisual/releases/download/v1.0.0-rc9/clickvisual-v1.0.0-rc9-linux-amd64.tar.gz
tar zxvf clickvisual-v1.0.0-rc9-linux-amd64.tar.gz
[root@VM-0-33-debian:~/tarball/clickvisual# ./clickvisual --help
Usage:
clickvisual [command]
Available Commands:
agent 啓動 clickvisual agent 服務端
command 啓動 clickvisual 命令行
completion Generate the autocompletion script for the specified shell
help Help about any command
server 啓動 clickvisual server 服務端
Flags:
-c, --config string 指定配置文件,默認 config/default.toml (default "config/default.toml")
-h, --help help for clickvisual
Use "clickvisual [command] --help" for more information about a command.
從命令中可以看出,啓動 ClickVisual 應該是使用 server 參數,通過 -c
傳入配置文件,默認配置文件是 config/default.toml
,我們要調整這個配置文件中的 MySQL 和 Redis 的認證信息,我的環境配置如下:
[redis]
debug = true
addr = "127.0.0.1:6379"
writeTimeout = "3s"
password = ""
[mysql]
debug = true
# database DSN
dsn = "root:1234@tcp(127.0.0.1:3307)/clickvisual?charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&loc=Local&readTimeout=1s&timeout=1s&writeTimeout=3s"
# log level
level = "debug"
# maximum number of connections in the idle connection pool for database
maxIdleConns = 5
# maximum number of open connections for database
maxOpenConns = 10
# maximum amount of time a connection
connMaxLifetime = "300s"
OK,啓動 ClickVisual:
nohup ./clickvisual server &>stdout.log &
ClickVisual 啓動之後監聽在 19011 端口,可以檢查這個端口是否存活:
ss -tlnp|grep 19011
ClickVisual 解壓縮之後,裏邊有個 sql 腳本,位於 scripts/migration/database.sql,需要把這個 sql 導入 MySQL:
mysql -uroot -p < scripts/migration/database.sql
之後就可以瀏覽器訪問 19011 了,ClickVisual 會提示你進行表結構初始化,初始賬號密碼是 clickvisual/clickvisual
。
Categraf
最後一個要安裝的組件是日誌採集器,我這裏使用 categraf,下載地址如下:
下載:https://flashcat.cloud/download/categraf/
代碼:https://github.com/flashcatcloud/categraf
這裏選擇 v0.3.38
版本,下載解壓縮,重點需要 categraf 二進制以及 conf 目錄下的 logs.toml,其他所有 input.
打頭的配置都是指標採集插件,全部刪除,另外也刪除 conf 目錄下的 traces.yaml,搞的乾淨點。然後修改兩個配置文件。
1、修改 config.toml,關閉 heartbeat:
[heartbeat]
enable = false
Categraf 和 Nightingale 配合工作,主要處理指標場景,我們現在不測試指標,只是測試日誌採集,所以不需要 Nightingale,關閉 Heartbeat。
2、修改 logs.toml,要給出要採集的日誌路徑以及要發往的 Kafka 地址。
[logs]
api_key = "x"
enable = true
send_to = "127.0.0.1:9092"
send_type = "kafka"
topic = "categraf"
use_compress = false
send_with_tls = false
batch_wait = 5
run_path = "/opt/categraf/run"
open_files_limit = 100
scan_period = 10
frame_size = 9000
collect_container_all = false
[[logs.items]]
type = "file"
path = "/root/works/catpaw/stdout.log"
source = "app"
service = "catpaw"
topic = "catpaw"
accuracy = "s"
其中 send_to 字段是配置了 Kafka 的地址,send_type 配置爲 kafka,collect_container_all 設置爲 false 避免一些非 K8s 環境下的報錯日誌,[[logs.items]]
是雙中括號,在 toml 裏表示數組,即可以配置多個 [[logs.items]]
段,這裏我採集了 catpaw 的 stdout.log,source、service 都是標籤,topic 是日誌發往 Kafka 的 Topic。
stdout.log 的日誌內容,給大家看一行例子:
{"level":"error","ts":"2023-11-01T16:57:15+08:00","caller":"http/http.go:236","msg":"failed to send http request","error":"Get \"http://a.cn\": dial tcp: lookup a.cn on 183.60.83.19:53: no such host","plugin":"http","target":"http://a.cn"}
這是一個 json 格式的日誌,不需要額外的數據清洗了,直接採集即可。推薦大家寫的程序都打印 json 格式的日誌,對於日誌採集非常方便。
啓動 categraf:
nohup ./categraf &> categraf.log &
通過 ps 查看 categraf 進程是否啓動成功,查看 categraf.log 是否有異常日誌,如果一切正常,咱們就可以去 Kowl 查看日誌了。
查看 Kafka 中的日誌
其中 message 字段是日誌原文,timestamp 是採集日誌時的時間戳,有這倆字段,ClickVisual 就可以處理了。其他字段是 categraf 自動添加的,比如 source、service、topic,即便沒有這些額外的字段,也不影響 ClickVisual 的使用。
在 ClickVisual 配置日誌庫
終於到了最後一步了,到 ClickVisual 配置日誌庫。首先去系統管理裏新增 ClickHouse 實例:
我之前創建過,現在點擊編輯給大家看一下內容:
進入日誌菜單,可以看到剛纔添加的 ClickHouse 實例,右鍵添加數據庫(一個 ClickHouse 實例裏可以創建多個數據庫,跟 MySQL 一樣,我這裏直接取名 db01,你隨意 ):
之後在 db01 上右鍵,新增日誌庫。數據表通常填成 topic 名字就行,其實就是 log stream 的名字。source 字段很關鍵,ClickVisual 會根據 source 來提取日誌,Kowl 的截圖中大家看到了,我的日誌裏有好幾個字段:message、status、timestamp、agent_hostname、fcservice、fcsource 等,但是我在 source 裏故意少填幾個字段,填入如下內容:
{
"message": "x",
"timestamp": 1698829486,
"status": "y"
}
source 裏填 json 結構,不用填真實內容,只要填一個假數據結構,ClickVisual 能推斷出各個字段的類型就行,我呢,就填了上面三個字段。點擊轉換,選擇時間字段和日誌詳情字段:
確定之後,ClickVisual 自動填充了相關字段,然後,我們補齊剩下的 Kafka 信息即可:
確定之後,稍等幾秒鐘,就可以看到數據了,我的截圖如下:
其實,剛開始日誌字段下面是空的,右側日誌詳情裏的 level 字段也沒有背景色。基礎字段裏有 status,顯然,因爲配置日誌庫的時候,source 樣例只給了 message、timestamp、status 三個字段,所以,ClickHouse 只拿到一個基本字段 status,如果當時要是把 fcservice、fcsource 也作爲 source 樣例寫上,基礎字段裏大概率就會有了。
查看 ClickHouse 中的庫表
clickhouse client
進入 ClickHouse 客戶端,可以看到相關庫表:
localhost.localdomain :) use db01
USE db01
Query id: a96ccd16-990c-4c8a-9d07-7bba0d0c4425
Ok.
0 rows in set. Elapsed: 0.001 sec.
localhost.localdomain :) show tables;
SHOW TABLES
Query id: 3b0849f7-cd05-481e-b786-5c8c1870caaf
┌─name────────────────────┐
│ catpaw │
│ catpaw_stream │
│ catpaw_view │
│ qinxiaohuisyslog │
│ qinxiaohuisyslog_stream │
│ qinxiaohuisyslog_view │
└─────────────────────────┘
6 rows in set. Elapsed: 0.001 sec.
catpaw 相關的三個表就是我剛纔一通操作產生的,qinxiaohuisyslog 相關的三個表不用關注,那是之前測試的時候生成的。看一下 stream 表的表結構:
localhost.localdomain :) show create table catpaw_stream\G
SHOW CREATE TABLE catpaw_stream
Query id: 1c75d947-d122-48cf-ad0e-e4f4049795b5
Row 1:
──────
statement: CREATE TABLE db01.catpaw_stream
(
`status` String,
`timestamp` Float64,
`message` String CODEC(ZSTD(1))
)
ENGINE = Kafka
SETTINGS kafka_broker_list = '127.0.0.1:9092', kafka_topic_list = 'catpaw', kafka_group_name = 'db01_catpaw', kafka_format = 'JSONEachRow', kafka_num_consumers = 1, kafka_skip_broken_messages = 0
1 row in set. Elapsed: 0.001 sec.
這是一個引擎類型爲 Kafka 的 Table,再看一下 catpaw_view:
localhost.localdomain :) show create table catpaw_view\G
SHOW CREATE TABLE catpaw_view
Query id: 99c22d6a-a617-43fc-9963-3575778b0623
Row 1:
──────
statement: CREATE MATERIALIZED VIEW db01.catpaw_view TO db01.catpaw
(
`status` String,
`_time_second_` DateTime,
`_time_nanosecond_` DateTime64(9),
`_raw_log_` String
) AS
SELECT
status,
toDateTime(toInt64(timestamp)) AS _time_second_,
fromUnixTimestamp64Nano(toInt64(timestamp * 1000000000)) AS _time_nanosecond_,
message AS _raw_log_
FROM db01.catpaw_stream
WHERE 1 = 1
1 row in set. Elapsed: 0.001 sec.
這是一個 ClickHouse 物化視圖,查詢 stream 表的數據,塞入日誌結果表 catpaw,我們看一下日誌結果表 catpaw 的表結構:
localhost.localdomain :) show create table catpaw\G
SHOW CREATE TABLE catpaw
Query id: d06e11b9-4b5a-4594-97af-877435b93238
Row 1:
──────
statement: CREATE TABLE db01.catpaw
(
`status` String,
`_time_second_` DateTime,
`_time_nanosecond_` DateTime64(9),
`_raw_log_` String CODEC(ZSTD(1))
INDEX idx_raw_log _raw_log_ TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(_time_second_)
ORDER BY _time_second_
TTL toDateTime(_time_second_) + toIntervalDay(1)
SETTINGS index_granularity = 8192
1 row in set. Elapsed: 0.001 sec.
如果根據 status 字段來篩選,速度是比較快的,但是如果想根據 _raw_log_
裏的信息來篩選,比如根據 level 字段來篩選,level 是日誌原文 json 裏的一個字段,不是一等公民字段,速度就慢了,ClickVisual 官方建議,這種情況,應該把日誌原文裏的過濾字段單獨出來作爲一個字段,點擊日誌字段右側的小齒輪:
可以把日誌原文那個 json 裏的 level 字段單獨提取出來,配置如下
如上操作之後,重新查看 catpaw 和 catpaw_view 的表結構:
localhost.localdomain :) show create table catpaw\G
SHOW CREATE TABLE catpaw
Query id: d06e11b9-4b5a-4594-97af-877435b93238
Row 1:
──────
statement: CREATE TABLE db01.catpaw
(
`status` String,
`_time_second_` DateTime,
`_time_nanosecond_` DateTime64(9),
`_raw_log_` String CODEC(ZSTD(1)),
`level` Nullable(String),
INDEX idx_raw_log _raw_log_ TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(_time_second_)
ORDER BY _time_second_
TTL toDateTime(_time_second_) + toIntervalDay(1)
SETTINGS index_granularity = 8192
1 row in set. Elapsed: 0.001 sec.
localhost.localdomain :) show create table catpaw_view\G
SHOW CREATE TABLE catpaw_view
Query id: ebfebeb7-d8f5-4427-b3ba-33c0d07d24b1
Row 1:
──────
statement: CREATE MATERIALIZED VIEW db01.catpaw_view TO db01.catpaw
(
`status` String,
`_time_second_` DateTime,
`_time_nanosecond_` DateTime64(9),
`_raw_log_` String,
`level` Nullable(String)
) AS
SELECT
status,
toDateTime(toInt64(timestamp)) AS _time_second_,
fromUnixTimestamp64Nano(toInt64(timestamp * 1000000000)) AS _time_nanosecond_,
message AS _raw_log_,
toNullable(toString(replaceAll(JSONExtractRaw(message, 'level'), '"', ''))) AS level
FROM db01.catpaw_stream
WHERE 1 = 1
1 row in set. Elapsed: 0.001 sec.
霧化視圖 catpaw_view 裏,增加了對 level 字段的提取,日誌結果表 catpaw 裏也新增了一個 level 字段。看來 ClickVisual 是執行了一些 alter table 的語句。之後就可以這麼查了(不用像之前使用 like 語句):
總結
ClickVisual 的整體思路設計挺巧妙的,不過業界使用 ClickHouse 存儲日誌,大都是使用的雙 array 存儲動態字段。你們公司是如何做的呢?有在生產環境使用 ClickVisual 麼?感覺如何?歡迎大家留言交流。
作者:秦曉輝,Open-Falcon、Nightingale、Categraf 等開源項目創始研發人員,極客時間專欄《運維監控系統實戰筆記》作者,目前在創業,提供可觀測性產品,微信 picobyte,歡迎加好友交流,加好友請備註公司。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/xst2Qyr4zATFr3OlmIKQ6g