嘗鮮初體驗:使用 Loggie 和 VictoriaLogs 快速構(gòu)建新一代的日志系統(tǒng)
如果你熟悉Prometheus,想必你肯定也知道VictoriaMetrics,這款越來越流行的監(jiān)控項目,可作為Prometheus的增強或者平替。VictoriaMetrics一個重要的亮點就是解決Prometheus在大規(guī)模Metrics指標(biāo)數(shù)據(jù)量級下的存儲問題。
同屬于可觀測性,當(dāng)我們把眼光聚焦到日志領(lǐng)域,其實很久以來日志的一個痛點是也是存儲。
當(dāng)前日志存儲的痛點
時下比較常見的一些開源日志存儲項目有:Elasticsearch、Clickhouse、Loki等。當(dāng)然,Elasticsearch和Clickhouse并非天生針對日志存儲而設(shè)計,我們只是可以拿來存儲日志數(shù)據(jù)而已。
比如Elasticsearch的核心是一個搜索引擎。針對日志存儲的場景,可以全文檢索是一大優(yōu)勢,但同時存在以下一些不足:
- 寫入性能相對慢
- 資源占用較高
- 針對日志存儲的壓縮差
總體來說,Elasticsearch是一款歷史悠久、被廣泛使用的日志存儲數(shù)據(jù)庫,畢竟當(dāng)年ELK的概念深入人心。但是,在當(dāng)前降本增效的大背景下,很多企業(yè)還是會對Elasticsearch占用的機器資源比較敏感,如果只用于存儲大量的運維類日志,性價比還是偏低。
所以前兩年Grafana家的Loki橫空出世,還是掀起了一點水花的,畢竟日志領(lǐng)域早就苦Elasticsearch久矣。
簡單介紹一下Loki的優(yōu)點:
- 天生就是為了存儲日志設(shè)計
- 資源占用還不錯
- 引入了日志流Log Stream的概念
大半年前,我們公司內(nèi)部有部門開始嘗試使用Loki存儲一些系統(tǒng)日志。但總會遇到一些小問題,并不是很讓人放心。除此之外,Loki的不足之處還有:
- 沒有實際意義上的全文檢索,所以關(guān)鍵字查詢等可能會比較慢
- 不支持獨立設(shè)置檢索的label,可能導(dǎo)致性能等一系列問題
當(dāng)然,Loki還是一個相對年輕的項目,我們可以理解這些穩(wěn)定性、性能、設(shè)計上的問題可能是發(fā)展早期的陣痛。
但是,貌似很多人已經(jīng)等不及了。
姍姍來遲:VictoriaLogs的優(yōu)勢
最近VictoriaMetrics發(fā)布了預(yù)覽版的VictoriaLogs,類似Loki專門用于存儲日志。鑒于VictoriaMetrics的良好名聲,還是讓大家對這條攪局的「鯰魚」充滿了一定的期待。
VictoriaMetrics為什么要入局搞VictoriaLogs呢?
其實從2020年的這個Issues開始:https://github.com/VictoriaMetrics/VictoriaMetrics/issues/816
VictoriaMetrics就有了研發(fā)VictoriaLogs的想法。從該issues的討論中我們可以看出,大家對Loki還是有點微辭的,比如說存儲依賴S3(本地存儲不支持分布式),比如說性能。
這里節(jié)選一下issues里的吐槽:
almost 2 years passed and Loki is still unusable for scenarios with real logging data. Trying to query anything hitting more than 50k logs is exploding servers :)
不用翻譯了,隔著屏幕我們都能感受到這個用戶的強烈不滿。
時隔兩年多,VictoriaLogs終于正式來到了我們面前,那VictoriaLogs到底有哪些優(yōu)勢,又能解決日志存儲領(lǐng)域的哪些問題呢?
這里我簡略總結(jié)幾點,感興趣的同學(xué)可以在[官方文檔]: https://docs.victoriametrics.com/VictoriaLogs/ 中尋找更多答案。
- 兼容Elasticsearch bulk接口
- 支持橫向和縱向擴容
- 資源占用低
- 支持多租戶
- 繼承(抄)了Loki的log stream概念,但有一些優(yōu)化
- 支持全文檢索,提供了簡單強大的LogsQL查詢語法
先說VictoriaMetrics家的一大特色:兼容性。VictoriaLogs直接支持了Elasticsearch bulk API,由于市面上幾乎所有的日志采集Agent都支持發(fā)送至Elasticsearch,所以可以基本做到無縫對接和遷移,無需讓這些Agent都去研發(fā)增加新的輸出源。(這里確實要吐槽一下Loki,連個公開的客戶端Client SDK包都沒有提供,這讓人怎么對接呢)
但是支持橫向和縱向擴容,這點由于現(xiàn)在VictoriaLogs預(yù)覽版只提供了單節(jié)點的,暫時還無法確認。
另外在 資源占用 方面,我們可以直接看 VictoriaLogs提供的[benchmark]: https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/logs-benchmark 結(jié)果。對比Elasticsearch,從下圖可以看出:
- 平均內(nèi)存
- Elasticsearch:4.4 GiB
- VictoriaLogs:144 MiB
- 平均磁盤占用:
- Elasticsearch:53.9 GB
- VictoriaLogs:4.20 GB
內(nèi)存和磁盤占用確實要低太多,基本上是差了一個數(shù)量級,如果存儲量大的話,能省下不少臺服務(wù)器的錢,無疑是現(xiàn)在降本增效患者的一大福音。
VictoriaLogs同樣引入了 log stream 的概念,結(jié)合多租戶的能力,似乎可以做到日志存儲場景下的性能、資源占用權(quán)衡下的最優(yōu)解,這也是VictoriaLogs區(qū)別于Elasticsearch等非專門為日志存儲設(shè)計數(shù)據(jù)庫的核心因素。
所以在使用VictoriaLogs之前,請務(wù)必先好好了解一下log stream。
log stream是什么呢?
簡單來說,表示應(yīng)用(服務(wù))的一個日志實例。至于這個日志實例的具體粒度,可以由我們自行設(shè)計和掌控,但是不建議整體數(shù)量特別大。
舉例說明,一個日志實例可以為:
- 主機部署下一個Linux進程產(chǎn)生的日志
- Kubernetes上一個應(yīng)用運行的Pod的Container容器產(chǎn)生的日志,或者更細粒度,容器里的一個日志文件也可以表示一個log stream
log stream設(shè)計的關(guān)鍵是可以被唯一標(biāo)識,這樣可以確定日志在分布式系統(tǒng)中產(chǎn)生的位置,比如在哪個節(jié)點的哪個容器的哪個日志文件中。
一個log stream由多個label來標(biāo)識,所以其實這里和Prometheus metrics的label類似,我們可以拿Prometheus中的一些概念類比:
- job label:表示多個副本上面的應(yīng)用,比如deployment名稱
- instance label:表示哪個進程和端口號產(chǎn)生的metrics
在VictoriaLogs中也可以自己設(shè)計一些類似的label,加在日志采集的元信息中,這樣還能用于后續(xù)的日志和指標(biāo)的關(guān)聯(lián)和檢索。當(dāng)然在實際的應(yīng)用中,我們還可以增加諸如:環(huán)境、數(shù)據(jù)中心、namespace等的label。
如果你之前了解Loki,肯定會想說,Loki不也是這樣設(shè)計label的嗎?
對,但是等你深入用過Loki后,可能會遇到這樣的坑:當(dāng)發(fā)送的日志labels里攜帶了一些頻繁修改的字段,比如說一條日志,將其中的偏移量offset字段作為一個label,大概如下所示:
{
"message": "xxx",
"timestamp": "",
"logconfig": "foo",
"podname": "bar",
"offset": 20,
...
}
Loki會將所有l(wèi)abels的值作為一個唯一的log stream標(biāo)識,比如上面的內(nèi)容就會將{logconfig: "foo", "podname":"bar", "offset": 20}作為一個log stream。由于在同一個文件中,offset會隨著采集的每行日志都在增加,因此會導(dǎo)致log stream個數(shù)無限增長,對Loki造成巨大的壓力。
為了規(guī)避這類問題,VictoriaLogs設(shè)計上就將stream label和普通label做了區(qū)分,比如以上這種場景,我們只需要將logconfig和podname作為stream label即可,offset則作為普通的label。
了解了stream label后,我們就可以更好的理解以下VictoriaLogs中的數(shù)據(jù)格式:
- _msg :日志內(nèi)容字段。
- _time :時間字段。
- _stream label:在同一個log stream中,label不變。
- 普通labels:在同一個log stream中可以變化,比如level, traceId等等。
真實世界:使用Loggie采集日志至VictoriaLogs中
下面我們開始真實的體驗一下如何使用Loggie和VictoriaLogs快速構(gòu)建一套日志系統(tǒng)。
1、部署VictoriaLogs
可執(zhí)行以下命令:
helm repo add vm https://victoriametrics.github.io/helm-charts/
helm repo update
helm install vlsingle vm/victoria-logs-single -nvictoria-logs --create-namespace
更多的詳情可參考[helm chart部署]: https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-logs-single/README.md。
這里我們設(shè)置部署的namespace為victoria-logs,如果修改了該namespace名稱,下面的一些配置也請同步修改。
2、部署Loggie
如果你還不知道Loggie,請進[這里]: https://github.com/loggie-io/loggie。
為了方便起見,我們在Loggie catalog中提供了一個適配VictoriaLogs的部署配置。
VERSION=v1.4.0
helm pull https://github.com/loggie-io/installation/releases/download/$VERSION/loggie-$VERSION.tgz && tar xvzf loggie-$VERSION.tgz
# 從catalog中下載適配victoriaLogs所用的部署配置values文件
wget https://raw.githubusercontent.com/loggie-io/catalog/main/scenarios/victoriaLogs/values.yml
# 指定該values文件部署Loggie
helm install loggie ./loggie -nloggie --create-namespace -f values.yml
3、生成和采集日志
部署完了VictoriaLogs和Loggie后,創(chuàng)建一個測試用的Deployment genfiles用于產(chǎn)生日志。
wget https://raw.githubusercontent.com/loggie-io/catalog/main/common/genfiles/deployment.yml
kubectl apply -f deployment.yml
然后創(chuàng)建一個相匹配的日志采集任務(wù),告訴Loggie要去采集這個Deployment容器中的日志文件:
wget https://raw.githubusercontent.com/loggie-io/catalog/main/scenarios/victoriaLogs/genfiles_logconfig.yml
kubectl apply -f genfiles_logconfig.yml
這里我們重點關(guān)注下genfiles_logconfig.yml中的sink部分配置:
sink: |
type: elasticsearch
hosts: [ "vlsingle-victoria-logs-single-server.victoria-logs.svc:9428/insert/elasticsearch/" ]
parameters:
_msg_field: "body"
_time_field: "@timestamp"
_stream_fields: "logconfig,namespace,podname,containername"
- 直接使用的Elasticsearch類型的sink,因為VictoriaLogs兼容bulk接口
- 這里將hosts改成了VictoriaLogs的url,請注意后面加了固定的path: /insert/elasticsearch/
- 新增了parameters字段,兼容一下VictoriaLogs所需日志的格式,同時配置了stream labels字段
- _msg_field:表示使用哪個日志字段當(dāng)作msg內(nèi)容字段,Loggie默認的日志內(nèi)容字段為body,如果你已經(jīng)在用Loggie并且修改成了其他字段,請對應(yīng)修改。
- _time_field:表示使用哪個字段作為時間字段。
- _stream_fields:表示使用哪些字段作為log stream的唯一標(biāo)識label。
在本示例中,我們在sink端發(fā)送的日志格式大概如下所示:
{
"body": "2023-07-04 02:58:18.014 INF cmd/subcmd/genfiles/genfiles.go:57 > 1000 TqrccSCPzRUYRP PJ MlvgdAluEpIoRIRyzjZoNk",
"containername": "genfiles",
"namespace": "default",
"podname": "genfiles-66f5c86fdb-tjpzr",
"@timestamp": "2023-07-04T02:58:21.905Z",
"offset": 1092798,
"cluster": "test",
"logconfig": "genfiles",
"nodename": "kind-control-plane",
"filename": "/var/lib/kubelet/pods/c7b2da94-b152-414e-a7d8-1951e9d4f09a/volumes/kubernetes.io~empty-dir/logs/loggie.log"
}
可以看到,這里將log stream的粒度定為了Pod容器級別,所以_stream_fields設(shè)置為了cluster,logconfig,namespace,podname,containername。
現(xiàn)在我們模擬產(chǎn)生一點日志:
# 進入到genfiles容器中
kubectl exec -it $(kubectl get po -l app=genfiles -o jsnotallow="{.items[0].metadata.name}") bash
# 產(chǎn)生一點日志
./loggie genfiles -totalCount=1000 -lineBytes=1024 -qps=0 \
-log.maxBackups=1 -log.maxSize=1000 -log.directory=/tmp/log -log.noColor=true \
-log.enableStdout=false -log.enableFile=true -log.timeFormat="2006-01-02 15:04:05.000"
關(guān)于loggie生成日志的genfiles子命令,更多的使用方式可以參考[這里]: https://github.com/loggie-io/catalog/tree/main/common/genfiles。
正常情況下,Loggie會很快采集這些日志,然后發(fā)送至VictoriaLogs。
當(dāng)然我們也可以進入Loggie terminal控制臺確認一下采集進度:
kubectl -nloggie -it exec $(kubectl -nloggie get po -l app=loggie -o jsnotallow="{.items[0].metadata.name}") -- ./loggie inspect
如上圖所示,文件采集進度為100%,表示已經(jīng)采集完畢,并且發(fā)送日志到了VictoriaLogs中。
Loggie terminal具體的操作方式,也可以參考我們的[日志采集快速排障指南]: https://loggie-io.github.io/docs/main/user-guide/troubleshot/log-collection/。
接下來,我們就可以使用VictoriaLogs內(nèi)置的UI查看采集的日志了。
LogsQL和日志查詢
由于本地和Kubernetes集群內(nèi)部網(wǎng)絡(luò)不通,簡單起見,我們直接port-forward一下:
export POD_NAME=$(kubectl get pods --namespace victoria-logs -l "app=server" -o jsnotallow="{.items[0].metadata.name}")
kubectl --namespace victoria-logs port-forward $POD_NAME 9428
然后本地訪問以下頁面:[http://localhost:9428/select/vmui/](http://localhost:9428/select/vmui/)。
VictoriaLogs當(dāng)前提供了一個簡單的UI頁面,可用于查詢?nèi)罩尽榱搜菔救绾尾樵儎偛挪杉娜罩?,我們先快速了解一下查詢語法。
考慮到最大化查詢的性能,建議一般寫LogsQL規(guī)范性套路為:
1、確定log stream
_stream這里的過濾字段,就是剛才在sink上parameters._stream_fields配置的,比如查詢哪個日志采集任務(wù),哪個Pod等。示例中我們根據(jù)log stream中的logconfig label查詢了剛才創(chuàng)建的日志采集任務(wù)下的所有日志,LogsQL如下:
_stream:{logcnotallow=genfiles}
如果logconfig匹配了很多的Pod,這里還可以加上對應(yīng)的podname等字段來進一步過濾。比如:_stream:{logcnotallow=genfiles, podname="genfiles-66f5c86fdb-mrfzc"}。
2、加上時間區(qū)間
接著我們還可以增加時間區(qū)間,進一步縮減返回的日志條數(shù)。
LogsQL: _stream:{logcnotallow=genfiles} _time:[now-1h,now]
請注意,這里通過空格來間隔,另外_stream和_time為內(nèi)置的字段,無先后順序區(qū)分。
3、進一步的過濾
上面我們讓Victoria先快速確定一個log stream的日志范圍,然后在此基礎(chǔ)上,進行關(guān)鍵字匹配,字段過濾等其他復(fù)雜的日志檢索。
比如:
- 關(guān)鍵字檢索:_stream:{logcnotallow=genfiles} _time:[now-1h,now] <關(guān)鍵字>。
- 根據(jù)普通的label字段來過濾:_stream:{logcnotallow=genfiles} _time:[now-1h,now] nodename:kind-control-plane。
- 還可以加上邏輯條件:AND, OR, NOT。
LogsQL的功能還是比較多的,更詳細的LogsQL使用姿勢,可以參考[官方文檔]: https://docs.victoriametrics.com/VictoriaLogs/LogsQL.html。
總結(jié)
雖然 VictoriaLogs 只發(fā)布了一個預(yù)覽版,但是從當(dāng)前的設(shè)計和體驗來看,還是要優(yōu)于 Loki 的,畢竟站在了 Loki 的肩膀上,有后發(fā)優(yōu)勢。
但是軟件設(shè)計沒有銀彈,真實的世界里,是否選擇使用某個項目,最重要的還是適不適合,而不在與項目本身功能是否強大。
可以預(yù)料到的是,在很長的一段時間內(nèi),Elasticsearch這些老牌們還是會占據(jù)大部分的市場,因為在企業(yè)中,很多都有已經(jīng)長期穩(wěn)定運行的Elasticsearch或者Clickhouse,同時還有相應(yīng)的運維人員和配套支撐。
那什么情況適合VictoriaLogs呢?
如果你們正在從頭開始搭建自己的日志系統(tǒng),愿意接受新事物的潛在風(fēng)險,對VictoriaLogs的未來充滿信心,那么還是值得一試的。
正是由于VictoriaLogs這些新星們進場,讓日志領(lǐng)域更加內(nèi)卷的同時,也讓廣大的人民群眾受益。畢竟我們多了一個不錯的選擇,國內(nèi)的「開源定制化開發(fā)」市場又有更多的活可以干了。