輕量化日志Loki全攻略,再也不會(huì)整懵了
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)??

文章來(lái)源:https://c1n.cn/0wHvF
前言
簡(jiǎn)介
架構(gòu)說(shuō)明
部署
使用
前言
簡(jiǎn)介
Loki 是 Grafana Labs 團(tuán)隊(duì)最新的開(kāi)源項(xiàng)目,是一個(gè)水平可擴(kuò)展,高可用性,多租戶的日志聚合系統(tǒng)。
它的設(shè)計(jì)非常經(jīng)濟(jì)高效且易于操作,因?yàn)樗粫?huì)為日志內(nèi)容編制索引,而是為每個(gè)日志流編制一組標(biāo)簽,專門(mén)為 Prometheus 和 Kubernetes 用戶做了相關(guān)優(yōu)化。
該項(xiàng)目受 Prometheus 啟發(fā),官方的介紹就是:Like Prometheus,But For Logs。類似于 Prometheus 的日志系統(tǒng)。
https://github.com/grafana/loki/
不對(duì)日志進(jìn)行全文索引。通過(guò)存儲(chǔ)壓縮非結(jié)構(gòu)化日志和僅索引元數(shù)據(jù),Loki 操作起來(lái)會(huì)更簡(jiǎn)單,更省成本。
通過(guò)使用與 Prometheus 相同的標(biāo)簽記錄流對(duì)日志進(jìn)行索引和分組,這使得日志的擴(kuò)展和操作效率更高,能對(duì)接 alertmanager。
特別適合儲(chǔ)存 Kubernetes Pod 日志;諸如 Pod 標(biāo)簽之類的元數(shù)據(jù)會(huì)被自動(dòng)刪除和編入索引。
受 Grafana 原生支持,避免 kibana 和 grafana 來(lái)回切換。
架構(gòu)說(shuō)明

| 組件說(shuō)明
說(shuō)明如下:
Promtail 作為采集器,類比 filebeat
Loki 相當(dāng)于服務(wù)端,類比 es
querier 查詢器
inester 日志存儲(chǔ)器
query-frontend 前置查詢器
distributor 寫(xiě)入分發(fā)器
| read path
查詢器接受 HTTP/1 數(shù)據(jù)請(qǐng)求
查詢器將查詢傳遞給所有 ingesters 請(qǐng)求內(nèi)存中的數(shù)據(jù)
接收器接受讀取的請(qǐng)求,并返回與查詢匹配的數(shù)據(jù)(如果有)
如果沒(méi)有接受者返回?cái)?shù)據(jù),則查詢器會(huì)從后備存儲(chǔ)中延遲加載數(shù)據(jù)并對(duì)其執(zhí)行查詢
查詢器將迭代所有接收到的數(shù)據(jù)并進(jìn)行重復(fù)數(shù)據(jù)刪除,從而通過(guò) HTTP/1 連接返回最終數(shù)據(jù)集
| write path

分發(fā)服務(wù)器收到一個(gè) HTTP/1 請(qǐng)求,以存儲(chǔ)流數(shù)據(jù)
每個(gè)流都使用散列環(huán)散列
分發(fā)程序?qū)⒚總€(gè)流發(fā)送到適當(dāng)?shù)?inester 和其副本(基于配置的復(fù)制因子)
每個(gè)實(shí)例將為流的數(shù)據(jù)創(chuàng)建一個(gè)塊或?qū)⑵渥芳拥浆F(xiàn)有塊中,, 每個(gè)租戶和每個(gè)標(biāo)簽集的塊都是唯一的
分發(fā)服務(wù)器通過(guò) HTTP/1 鏈接以成功代碼作為響應(yīng)
部署
| 本地化模式安裝
下載 Promtail 和 Loki:
wget??https://github.com/grafana/loki/releases/download/v2.2.1/loki-linux-amd64.zip
wget?https://github.com/grafana/loki/releases/download/v2.2.1/promtail-linux-amd64.zip
安裝 Promtail:
$?mkdir?/opt/app/{promtail,loki}?-pv
#?promtail配置文件
$?cat?<?/opt/app/promtail/promtail.yaml
server:
??http_listen_port:?9080
??grpc_listen_port:?0
positions:
??filename:?/var/log/positions.yaml?#?This?location?needs?to?be?writeable?by?promtail.
client:
??url:?http://localhost:3100/loki/api/v1/push
scrape_configs:
?-?job_name:?system
???pipeline_stages:
???static_configs:
???-?targets:
??????-?localhost
?????labels:
??????job:?varlogs
??????host:?yourhost
??????__path__:?/var/log/*.log
EOF
#?解壓安裝包
unzip?promtail-linux-amd64.zip
mv?promtail-linux-amd64?/opt/app/promtail/promtail
#?service文件
$?cat?</etc/systemd/system/promtail.service
[Unit]
Description=promtail?server
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/opt/app/promtail/promtail?-config.file=/opt/app/promtail/promtail.yaml
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=promtail
[Install]
WantedBy=default.target
EOF
systemctl?daemon-reload
systemctl?restart?promtail
systemctl?status?promtail
安裝 Loki:
$?mkdir?/opt/app/{promtail,loki}?-pv
#?promtail配置文件
$?cat?<?/opt/app/loki/loki.yaml
auth_enabled:?false
server:
??http_listen_port:?3100
??grpc_listen_port:?9096
ingester:
??wal:
????enabled:?true
????dir:?/opt/app/loki/wal
??lifecycler:
????address:?127.0.0.1
????ring:
??????kvstore:
????????store:?inmemory
??????replication_factor:?1
????final_sleep:?0s
??chunk_idle_period:?1h???????#?Any?chunk?not?receiving?new?logs?in?this?time?will?be?flushed
??max_chunk_age:?1h???????????#?All?chunks?will?be?flushed?when?they?hit?this?age,?default?is?1h
??chunk_target_size:?1048576??#?Loki?will?attempt?to?build?chunks?up?to?1.5MB,?flushing?first?if?chunk_idle_period?or?max_chunk_age?is?reached?first
??chunk_retain_period:?30s????#?Must?be?greater?than?index?read?cache?TTL?if?using?an?index?cache?(Default?index?read?cache?TTL?is?5m)
??max_transfer_retries:?0?????#?Chunk?transfers?disabled
schema_config:
??configs:
????-?from:?2020-10-24
??????store:?boltdb-shipper
??????object_store:?filesystem
??????schema:?v11
??????index:
????????prefix:?index_
????????period:?24h
storage_config:
??boltdb_shipper:
????active_index_directory:?/opt/app/loki/boltdb-shipper-active
????cache_location:?/opt/app/loki/boltdb-shipper-cache
????cache_ttl:?24h?????????#?Can?be?increased?for?faster?performance?over?longer?query?periods,?uses?more?disk?space
????shared_store:?filesystem
??filesystem:
????directory:?/opt/app/loki/chunks
compactor:
??working_directory:?/opt/app/loki/boltdb-shipper-compactor
??shared_store:?filesystem
limits_config:
??reject_old_samples:?true
??reject_old_samples_max_age:?168h
chunk_store_config:
??max_look_back_period:?0s
table_manager:
??retention_deletes_enabled:?false
??retention_period:?0s
ruler:
??storage:
????type:?local
????local:
??????directory:?/opt/app/loki/rules
??rule_path:?/opt/app/loki/rules-temp
??alertmanager_url:?http://localhost:9093
??ring:
????kvstore:
??????store:?inmemory
??enable_api:?true
EOF
#?解壓包
unzip?loki-linux-amd64.zip?
mv?loki-linux-amd64?/opt/app/loki/loki
#?service文件
$?cat?</etc/systemd/system/loki.service
[Unit]
Description=loki?server
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/opt/app/loki/loki?-config.file=/opt/app/loki/loki.yaml
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=loki
[Install]
WantedBy=default.target
EOF
systemctl?daemon-reload
systemctl?restart?loki
systemctl?status?loki
使用
| grafana 上配置 loki 數(shù)據(jù)源
如下圖:

grafana-loki-dashsource

保存完成后,切換到 grafana 左側(cè)區(qū)域的 Explore,即可進(jìn)入到 Loki 的頁(yè)面:

然后我們點(diǎn)擊 Log labels 就可以把當(dāng)前系統(tǒng)采集的日志標(biāo)簽給顯示出來(lái),可以根據(jù)這些標(biāo)簽進(jìn)行日志的過(guò)濾查詢:

比如我們這里選擇 /var/log/messages,就會(huì)把該文件下面的日志過(guò)濾展示出來(lái),不過(guò)由于時(shí)區(qū)的問(wèn)題,可能還需要設(shè)置下時(shí)間才可以看到數(shù)據(jù):

promtail 容器?/etc/promtail/config.yml:
server:
??http_listen_port:?9080
??grpc_listen_port:?0
positions:
??filename:?/tmp/positions.yaml
clients:
??-?url:?http://loki:3100/loki/api/v1/push
scrape_configs:
-?job_name:?system
??static_configs:
??-?targets:
??????-?localhost
????labels:
??????job:?varlogs
??????__path__:?/var/log/*log
這里的 job 就是 varlog,文件路徑就是?/var/log/*log。
| 在 grafana explore 上配置查看日志
查看日志?rate({job="message"}?|="kubelet"
算 qps rate({job=”message”} |=”kubelet” [1m])
| 只索引標(biāo)簽
之前多次提到 loki 和 es 最大的不同是 loki 只對(duì)標(biāo)簽進(jìn)行索引而不對(duì)內(nèi)容索引。下面我們舉例來(lái)看下。
靜態(tài)標(biāo)簽匹配模式
scrape_configs:
?-?job_name:?system
???pipeline_stages:
???static_configs:
???-?targets:
??????-?localhost
?????labels:
??????job:?message
??????__path__:?/var/log/messages
配置解讀:
上面這段配置代表啟動(dòng)一個(gè)日志采集任務(wù)
這個(gè)任務(wù)有 1 個(gè)固定標(biāo)簽 job=”syslog”
采集日志路徑為?/var/log/messages,會(huì)以一個(gè)名為 filename 的固定標(biāo)簽
在 promtail 的 web 頁(yè)面上可以看到類似 prometheus 的 target 信息頁(yè)面
可以和使用 Prometheus 一樣的標(biāo)簽匹配語(yǔ)句進(jìn)行查詢。
scrape_configs:
?-?job_name:?system
???pipeline_stages:
???static_configs:
???-?targets:
??????-?localhost
?????labels:
??????job:?syslog
??????__path__:?/var/log/syslog
?-?job_name:?system
???pipeline_stages:
???static_configs:
???-?targets:
??????-?localhost
?????labels:
??????job:?apache
??????__path__:?/var/log/apache.log
| 標(biāo)簽匹配模式的特點(diǎn)
原理如下:
和 prometheus 一致,相同標(biāo)簽對(duì)應(yīng)的是一個(gè)流 prometheus 處理 series 的模式
prometheus 中標(biāo)簽一致對(duì)應(yīng)的同一個(gè) hash 值和 refid(正整數(shù)遞增的 id),也就是同一個(gè) series
時(shí)序數(shù)據(jù)不斷的 append 追加到這個(gè) memseries 中
當(dāng)有任意標(biāo)簽發(fā)生變化時(shí)會(huì)產(chǎn)生新的 hash 值和 refid,對(duì)應(yīng)新的 series
查詢過(guò)程:
所以 loki 先根據(jù)標(biāo)簽算出 hash 值在倒排索引中找到對(duì)應(yīng)的 chunk?
然后再根據(jù)查詢語(yǔ)句中的關(guān)鍵詞等進(jìn)行過(guò)濾,這樣能大大的提速
因?yàn)檫@種根據(jù)標(biāo)簽算哈希在倒排中查找 id,對(duì)應(yīng)找到存儲(chǔ)的塊在 prometheus 中已經(jīng)被驗(yàn)證過(guò)了
屬于開(kāi)銷低
速度快
| 動(dòng)態(tài)標(biāo)簽和高基數(shù)
何為動(dòng)態(tài)標(biāo)簽:說(shuō)白了就是標(biāo)簽的 value 不固定
何為高基數(shù)標(biāo)簽:說(shuō)白了就是標(biāo)簽的 value 可能性太多了,達(dá)到 10 萬(wàn),100 萬(wàn)甚至更多
比如 apache 的 access 日志:
11.11.11.11?-?frank?[25/Jan/2000:14:00:01?-0500]?"GET?/1986.js?HTTP/1.1"?200?932?"-"?"Mozilla/5.0?(Windows;?U;?Windows?NT?5.1;?de;?rv:1.9.1.7)?Gecko/20091221?Firefox/3.5.7?GTB6"
scrape_configs:
?-?job_name:?system
???pipeline_stages:
???static_configs:
???-?targets:
??????-?localhost
?????labels:
??????job:?syslog
??????__path__:?/var/log/syslog
?-?job_name:?system
???pipeline_stages:
???static_configs:
???-?targets:
??????-?localhost
?????labels:
??????job:?apache
??????__path__:?/var/log/apache.log
??-?job_name:?system
????pipeline_stages:
???????-?regex:
?????????expression:?"^(?P<ip>\\S+)?(?P<identd>\\S+)?(?P<user>\\S+)?\\[(?P<timestamp>[\\w:/]+\\s[+\\-]\\d{4})\\]?\"(?P<action>\\S+)\\s?(?P<path>\\S+)?\\s?(?P<protocol>\\S+)?\"?(?P<status_code>\\d{3}|-)?(?P<size>\\d+|-)\\s?\"?(?P<referer>[^\"]*)\"?\\s?\"?(?P<useragent>[^\"]*)?\"?$"
?????-?labels:
?????????action:
?????????status_code:
????static_configs:
????-?targets:
???????-?localhost
??????labels:
???????job:?apache
???????env:?dev
???????__path__:?/var/log/apache.log
那么對(duì)應(yīng) action=get/post 和 status_code=200/400?則對(duì)應(yīng) 4 個(gè)流:
11.11.11.11?-?frank?[25/Jan/2000:14:00:01?-0500]?"GET?/1986.js?HTTP/1.1"?200?932?"-"?"Mozilla/5.0?(Windows;?U;?Windows?NT?5.1;?de;?rv:1.9.1.7)?Gecko/20091221?Firefox/3.5.7?GTB6"
11.11.11.12?-?frank?[25/Jan/2000:14:00:02?-0500]?"POST?/1986.js?HTTP/1.1"?200?932?"-"?"Mozilla/5.0?(Windows;?U;?Windows?NT?5.1;?de;?rv:1.9.1.7)?Gecko/20091221?Firefox/3.5.7?GTB6"
11.11.11.13?-?frank?[25/Jan/2000:14:00:03?-0500]?"GET?/1986.js?HTTP/1.1"?400?932?"-"?"Mozilla/5.0?(Windows;?U;?Windows?NT?5.1;?de;?rv:1.9.1.7)?Gecko/20091221?Firefox/3.5.7?GTB6"
11.11.11.14?-?frank?[25/Jan/2000:14:00:04?-0500]?"POST?/1986.js?HTTP/1.1"?400?932?"-"?"Mozilla/5.0?(Windows;?U;?Windows?NT?5.1;?de;?rv:1.9.1.7)?Gecko/20091221?Firefox/3.5.7?GTB6"
高基數(shù)問(wèn)題:就像上面,如果給 ip 設(shè)置一個(gè)標(biāo)簽,現(xiàn)在想象一下,如果您為設(shè)置了標(biāo)簽 ip,來(lái)自用戶的每個(gè)不同的 ip 請(qǐng)求不僅成為唯一的流。可以快速生成成千上萬(wàn)的流,這是高基數(shù),這可以殺死 Loki。
| 全文索引問(wèn)題
加速查詢沒(méi)標(biāo)簽字段:以上邊提到的 ip 字段為例 - 使用過(guò)濾器表達(dá)式查詢。
{job="apache"}?|=?"11.11.11.11"
loki 查詢時(shí)的分片(按時(shí)間范圍分段 grep):
Loki 將把查詢分解成較小的分片,并為與標(biāo)簽匹配的流打開(kāi)每個(gè)區(qū)塊,并開(kāi)始尋找該 IP 地址。
這些分片的大小和并行化的數(shù)量是可配置的,并取決于您提供的資源
如果需要,您可以將分片間隔配置為 5m,部署 20 個(gè)查詢器,并在幾秒鐘內(nèi)處理千兆字節(jié)的日志
或者,您可以發(fā)瘋并設(shè)置 200 個(gè)查詢器并處理 TB 的日志!
兩種索引模式對(duì)比:
es 的大索引,不管你查不查詢,他都必須時(shí)刻存在。比如長(zhǎng)時(shí)間占用過(guò)多的內(nèi)存
loki 的邏輯是查詢時(shí)再啟動(dòng)多個(gè)分段并行查詢
日志量少時(shí)少加標(biāo)簽:
因?yàn)槊慷嗉虞d一個(gè) chunk 就有額外的開(kāi)銷
舉例,如果該查詢是?{app=”loki”,level!=”debug”}
在沒(méi)加 level 標(biāo)簽的情況下只需加載一個(gè) chunk 即?app=“l(fā)oki”?的標(biāo)簽
如果加了 level 的情況,則需要把?level=info,warn,error,critical?5 個(gè) chunk 都加載再查詢
需要標(biāo)簽時(shí)再去添加:
當(dāng) chunk_target_size=1MB 時(shí)代表 以 1MB 的壓縮大小來(lái)切割塊
對(duì)應(yīng)的原始日志大小在 5MB-10MB,如果日志在?max_chunk_age?時(shí)間內(nèi)能達(dá)到 10MB,考慮添加標(biāo)簽
日志應(yīng)當(dāng)按時(shí)間遞增:
這個(gè)問(wèn)題和 tsdb 中處理舊數(shù)據(jù)是一樣的道理
目前 loki 為了性能考慮直接拒絕掉舊數(shù)據(jù)
2.?從 7 分鐘到 10 秒,Mybatis 批處理真的很強(qiáng)!
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù)?Java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過(guò)內(nèi)容,記得讀完點(diǎn)一下“在看”,加個(gè)“星標(biāo)”,這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。
點(diǎn)“在看”支持小哈呀,謝謝啦??

