嘗鮮初體驗:使用 Loggie 和 VictoriaLogs 快速構建新一代的日志系統
如果你熟悉Prometheus,想必你肯定也知道VictoriaMetrics,這款越來越流行的監控項目,可作為Prometheus的增強或者平替。VictoriaMetrics一個重要的亮點就是解決Prometheus在大規模Metrics指標數據量級下的存儲問題。
同屬于可觀測性,當我們把眼光聚焦到日志領域,其實很久以來日志的一個痛點是也是存儲。
當前日志存儲的痛點
時下比較常見的一些開源日志存儲項目有:Elasticsearch、Clickhouse、Loki等。當然,Elasticsearch和Clickhouse并非天生針對日志存儲而設計,我們只是可以拿來存儲日志數據而已。
比如Elasticsearch的核心是一個搜索引擎。針對日志存儲的場景,可以全文檢索是一大優勢,但同時存在以下一些不足:
- 寫入性能相對慢
- 資源占用較高
- 針對日志存儲的壓縮差
總體來說,Elasticsearch是一款歷史悠久、被廣泛使用的日志存儲數據庫,畢竟當年ELK的概念深入人心。但是,在當前降本增效的大背景下,很多企業還是會對Elasticsearch占用的機器資源比較敏感,如果只用于存儲大量的運維類日志,性價比還是偏低。
所以前兩年Grafana家的Loki橫空出世,還是掀起了一點水花的,畢竟日志領域早就苦Elasticsearch久矣。
簡單介紹一下Loki的優點:
- 天生就是為了存儲日志設計
- 資源占用還不錯
- 引入了日志流Log Stream的概念
大半年前,我們公司內部有部門開始嘗試使用Loki存儲一些系統日志。但總會遇到一些小問題,并不是很讓人放心。除此之外,Loki的不足之處還有:
- 沒有實際意義上的全文檢索,所以關鍵字查詢等可能會比較慢
- 不支持獨立設置檢索的label,可能導致性能等一系列問題
當然,Loki還是一個相對年輕的項目,我們可以理解這些穩定性、性能、設計上的問題可能是發展早期的陣痛。
但是,貌似很多人已經等不及了。
姍姍來遲:VictoriaLogs的優勢
最近VictoriaMetrics發布了預覽版的VictoriaLogs,類似Loki專門用于存儲日志。鑒于VictoriaMetrics的良好名聲,還是讓大家對這條攪局的「鯰魚」充滿了一定的期待。
VictoriaMetrics為什么要入局搞VictoriaLogs呢?
其實從2020年的這個Issues開始:https://github.com/VictoriaMetrics/VictoriaMetrics/issues/816
VictoriaMetrics就有了研發VictoriaLogs的想法。從該issues的討論中我們可以看出,大家對Loki還是有點微辭的,比如說存儲依賴S3(本地存儲不支持分布式),比如說性能。
這里節選一下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到底有哪些優勢,又能解決日志存儲領域的哪些問題呢?
這里我簡略總結幾點,感興趣的同學可以在[官方文檔]: https://docs.victoriametrics.com/VictoriaLogs/ 中尋找更多答案。
- 兼容Elasticsearch bulk接口
- 支持橫向和縱向擴容
- 資源占用低
- 支持多租戶
- 繼承(抄)了Loki的log stream概念,但有一些優化
- 支持全文檢索,提供了簡單強大的LogsQL查詢語法
先說VictoriaMetrics家的一大特色:兼容性。VictoriaLogs直接支持了Elasticsearch bulk API,由于市面上幾乎所有的日志采集Agent都支持發送至Elasticsearch,所以可以基本做到無縫對接和遷移,無需讓這些Agent都去研發增加新的輸出源。(這里確實要吐槽一下Loki,連個公開的客戶端Client SDK包都沒有提供,這讓人怎么對接呢)
但是支持橫向和縱向擴容,這點由于現在VictoriaLogs預覽版只提供了單節點的,暫時還無法確認。
另外在 資源占用 方面,我們可以直接看 VictoriaLogs提供的[benchmark]: https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/logs-benchmark 結果。對比Elasticsearch,從下圖可以看出:
- 平均內存
- Elasticsearch:4.4 GiB
- VictoriaLogs:144 MiB
- 平均磁盤占用:
- Elasticsearch:53.9 GB
- VictoriaLogs:4.20 GB
內存和磁盤占用確實要低太多,基本上是差了一個數量級,如果存儲量大的話,能省下不少臺服務器的錢,無疑是現在降本增效患者的一大福音。
VictoriaLogs同樣引入了 log stream 的概念,結合多租戶的能力,似乎可以做到日志存儲場景下的性能、資源占用權衡下的最優解,這也是VictoriaLogs區別于Elasticsearch等非專門為日志存儲設計數據庫的核心因素。
所以在使用VictoriaLogs之前,請務必先好好了解一下log stream。
log stream是什么呢?
簡單來說,表示應用(服務)的一個日志實例。至于這個日志實例的具體粒度,可以由我們自行設計和掌控,但是不建議整體數量特別大。
舉例說明,一個日志實例可以為:
- 主機部署下一個Linux進程產生的日志
- Kubernetes上一個應用運行的Pod的Container容器產生的日志,或者更細粒度,容器里的一個日志文件也可以表示一個log stream
log stream設計的關鍵是可以被唯一標識,這樣可以確定日志在分布式系統中產生的位置,比如在哪個節點的哪個容器的哪個日志文件中。
一個log stream由多個label來標識,所以其實這里和Prometheus metrics的label類似,我們可以拿Prometheus中的一些概念類比:
- job label:表示多個副本上面的應用,比如deployment名稱
- instance label:表示哪個進程和端口號產生的metrics
在VictoriaLogs中也可以自己設計一些類似的label,加在日志采集的元信息中,這樣還能用于后續的日志和指標的關聯和檢索。當然在實際的應用中,我們還可以增加諸如:環境、數據中心、namespace等的label。
如果你之前了解Loki,肯定會想說,Loki不也是這樣設計label的嗎?
對,但是等你深入用過Loki后,可能會遇到這樣的坑:當發送的日志labels里攜帶了一些頻繁修改的字段,比如說一條日志,將其中的偏移量offset字段作為一個label,大概如下所示:
{
"message": "xxx",
"timestamp": "",
"logconfig": "foo",
"podname": "bar",
"offset": 20,
...
}
Loki會將所有labels的值作為一個唯一的log stream標識,比如上面的內容就會將{logconfig: "foo", "podname":"bar", "offset": 20}作為一個log stream。由于在同一個文件中,offset會隨著采集的每行日志都在增加,因此會導致log stream個數無限增長,對Loki造成巨大的壓力。
為了規避這類問題,VictoriaLogs設計上就將stream label和普通label做了區分,比如以上這種場景,我們只需要將logconfig和podname作為stream label即可,offset則作為普通的label。
了解了stream label后,我們就可以更好的理解以下VictoriaLogs中的數據格式:
- _msg :日志內容字段。
- _time :時間字段。
- _stream label:在同一個log stream中,label不變。
- 普通labels:在同一個log stream中可以變化,比如level, traceId等等。
真實世界:使用Loggie采集日志至VictoriaLogs中
下面我們開始真實的體驗一下如何使用Loggie和VictoriaLogs快速構建一套日志系統。
1、部署VictoriaLogs
可執行以下命令:
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。
這里我們設置部署的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后,創建一個測試用的Deployment genfiles用于產生日志。
wget https://raw.githubusercontent.com/loggie-io/catalog/main/common/genfiles/deployment.yml
kubectl apply -f deployment.yml
然后創建一個相匹配的日志采集任務,告訴Loggie要去采集這個Deployment容器中的日志文件:
wget https://raw.githubusercontent.com/loggie-io/catalog/main/scenarios/victoriaLogs/genfiles_logconfig.yml
kubectl apply -f genfiles_logconfig.yml
這里我們重點關注下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:表示使用哪個日志字段當作msg內容字段,Loggie默認的日志內容字段為body,如果你已經在用Loggie并且修改成了其他字段,請對應修改。
- _time_field:表示使用哪個字段作為時間字段。
- _stream_fields:表示使用哪些字段作為log stream的唯一標識label。
在本示例中,我們在sink端發送的日志格式大概如下所示:
{
"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設置為了cluster,logconfig,namespace,podname,containername。
現在我們模擬產生一點日志:
# 進入到genfiles容器中
kubectl exec -it $(kubectl get po -l app=genfiles -o jsnotallow="{.items[0].metadata.name}") bash
# 產生一點日志
./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"
關于loggie生成日志的genfiles子命令,更多的使用方式可以參考[這里]: https://github.com/loggie-io/catalog/tree/main/common/genfiles。
正常情況下,Loggie會很快采集這些日志,然后發送至VictoriaLogs。
當然我們也可以進入Loggie terminal控制臺確認一下采集進度:
kubectl -nloggie -it exec $(kubectl -nloggie get po -l app=loggie -o jsnotallow="{.items[0].metadata.name}") -- ./loggie inspect
如上圖所示,文件采集進度為100%,表示已經采集完畢,并且發送日志到了VictoriaLogs中。
Loggie terminal具體的操作方式,也可以參考我們的[日志采集快速排障指南]: https://loggie-io.github.io/docs/main/user-guide/troubleshot/log-collection/。
接下來,我們就可以使用VictoriaLogs內置的UI查看采集的日志了。
LogsQL和日志查詢
由于本地和Kubernetes集群內部網絡不通,簡單起見,我們直接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當前提供了一個簡單的UI頁面,可用于查詢日志。為了演示如何查詢剛才采集的日志,我們先快速了解一下查詢語法。
考慮到最大化查詢的性能,建議一般寫LogsQL規范性套路為:
1、確定log stream
_stream這里的過濾字段,就是剛才在sink上parameters._stream_fields配置的,比如查詢哪個日志采集任務,哪個Pod等。示例中我們根據log stream中的logconfig label查詢了剛才創建的日志采集任務下的所有日志,LogsQL如下:
_stream:{logcnotallow=genfiles}
如果logconfig匹配了很多的Pod,這里還可以加上對應的podname等字段來進一步過濾。比如:_stream:{logcnotallow=genfiles, podname="genfiles-66f5c86fdb-mrfzc"}。
2、加上時間區間
接著我們還可以增加時間區間,進一步縮減返回的日志條數。
LogsQL: _stream:{logcnotallow=genfiles} _time:[now-1h,now]
請注意,這里通過空格來間隔,另外_stream和_time為內置的字段,無先后順序區分。
3、進一步的過濾
上面我們讓Victoria先快速確定一個log stream的日志范圍,然后在此基礎上,進行關鍵字匹配,字段過濾等其他復雜的日志檢索。
比如:
- 關鍵字檢索:_stream:{logcnotallow=genfiles} _time:[now-1h,now] <關鍵字>。
- 根據普通的label字段來過濾:_stream:{logcnotallow=genfiles} _time:[now-1h,now] nodename:kind-control-plane。
- 還可以加上邏輯條件:AND, OR, NOT。
LogsQL的功能還是比較多的,更詳細的LogsQL使用姿勢,可以參考[官方文檔]: https://docs.victoriametrics.com/VictoriaLogs/LogsQL.html。
總結
雖然 VictoriaLogs 只發布了一個預覽版,但是從當前的設計和體驗來看,還是要優于 Loki 的,畢竟站在了 Loki 的肩膀上,有后發優勢。
但是軟件設計沒有銀彈,真實的世界里,是否選擇使用某個項目,最重要的還是適不適合,而不在與項目本身功能是否強大。
可以預料到的是,在很長的一段時間內,Elasticsearch這些老牌們還是會占據大部分的市場,因為在企業中,很多都有已經長期穩定運行的Elasticsearch或者Clickhouse,同時還有相應的運維人員和配套支撐。
那什么情況適合VictoriaLogs呢?
如果你們正在從頭開始搭建自己的日志系統,愿意接受新事物的潛在風險,對VictoriaLogs的未來充滿信心,那么還是值得一試的。
正是由于VictoriaLogs這些新星們進場,讓日志領域更加內卷的同時,也讓廣大的人民群眾受益。畢竟我們多了一個不錯的選擇,國內的「開源定制化開發」市場又有更多的活可以干了。