???URL: https://grafana.com/blog/2020/04/21/how-labels-in-loki-can-make-log-queries-faster-and-easier/
??Description:
關(guān)于標(biāo)簽在 Loki 中如何真正發(fā)揮作用,你需要知道的一切。它可能與你想象的不同
在我們從事 Loki 項(xiàng)目的第一年的大部分時(shí)間里,問(wèn)題和反饋似乎都來(lái)自熟悉 Prometheus 的人。畢竟,Loki 就像 Prometheus--不過(guò)是針對(duì)日志的!"。
但是最近,我們看到越來(lái)越多的人嘗試使用 Loki,他們沒(méi)有 Prometheus 的經(jīng)驗(yàn),而且許多人來(lái)自于具有不同策略的系統(tǒng),以處理日志。這就帶來(lái)了很多關(guān)于 Loki 一個(gè)非常重要的概念的問(wèn)題,即使是 Prometheus 專家也想了解更多:標(biāo)簽 (Labels)!
這篇文章將涵蓋很多內(nèi)容,以幫助每一個(gè)剛接觸 Loki 的人和想要復(fù)習(xí)的人。我們將探討以下主題。
什么是標(biāo)簽 (Label)?
標(biāo)簽是鍵值對(duì),可以被定義為任何東西!我們喜歡把它們稱為元數(shù)據(jù) (metadata),用來(lái)描述日志流。如果你熟悉 Prometheus,你會(huì)習(xí)慣性地看到一些標(biāo)簽,比如job和instance,我將在接下來(lái)的例子中使用這些。
我們用 Loki 提供的刮削 (scrape) 配置也定義了這些標(biāo)簽。如果你正在使用 Prometheus,在 Loki 和 Prometheus 之間擁有一致的標(biāo)簽是 Loki 的超級(jí)優(yōu)勢(shì)之一,使你 非常容易將你的應(yīng)用程序指標(biāo) (Metrics) 與你的日志 (Logs) 數(shù)據(jù)聯(lián)系起來(lái)。
Loki 如何使用標(biāo)簽
Loki 中的標(biāo)簽執(zhí)行一個(gè)非常重要的任務(wù)。它們定義了一個(gè)流。更確切地說(shuō),每個(gè)標(biāo)簽的鍵和值的組合都定義了流。如果只有一個(gè)標(biāo)簽值發(fā)生變化,就會(huì)產(chǎn)生一個(gè)新的流。
如果你熟悉 Prometheus,那里使用的術(shù)語(yǔ)是系列 (series);但是,Prometheus 有一個(gè)額外的維度:度量名稱 (metric name)。Loki 簡(jiǎn)化了這一點(diǎn),沒(méi)有度量名稱,只有標(biāo)簽,我們決定使用流而不是系列。
讓我們舉個(gè)例子:
scrape_configs:
- job_name: system
pipeline_stages:
static_configs:
- targets:
- localhost
labels:
job: syslog
__path__: /var/log/syslog
這個(gè)配置將跟蹤一個(gè)文件并分配一個(gè)標(biāo)簽:job=syslog。你可以這樣查詢:
{job=”syslog”}
這將在 Loki 創(chuàng)建一個(gè)流。
現(xiàn)在讓我們把這個(gè)例子擴(kuò)大一點(diǎ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
現(xiàn)在我們正在跟蹤兩個(gè)文件。每個(gè)文件只得到一個(gè)標(biāo)簽和一個(gè)值,所以 Loki 現(xiàn)在將存儲(chǔ)兩個(gè)數(shù)據(jù)流。
我們可以用幾種方式查詢這些流:
{job=”apache”} <- 顯示標(biāo)簽 job 是 apache 的日志
{job=”syslog”} <- 顯示標(biāo)簽 job 是 syslog 的日志
{job=~”apache|syslog”} <- 顯示標(biāo)簽 job 是 apache **或** syslog 的日志
在最后一個(gè)例子中,我們使用了一個(gè) regex 標(biāo)簽匹配器來(lái)記錄使用標(biāo)簽 job 的兩個(gè)值的流?,F(xiàn)在考慮一下如何也使用一個(gè)額外的標(biāo)簽:
scrape_configs:
- job_name: system
pipeline_stages:
static_configs:
- targets:
- localhost
labels:
job: syslog
env: dev
__path__: /var/log/syslog
- job_name: system
pipeline_stages:
static_configs:
- targets:
- localhost
labels:
job: apache
env: dev
__path__: /var/log/apache.log
現(xiàn)在我們可以這樣做,而不是使用正則表達(dá)式:
{env=”dev”} <- 返回 env=dev 的所有日志,本例中包括兩個(gè)日志流
希望你現(xiàn)在開(kāi)始看到標(biāo)簽的力量。通過(guò)使用一個(gè)標(biāo)簽,你可以查詢?cè)S多數(shù)據(jù)流。通過(guò)結(jié)合幾個(gè)不同的標(biāo)簽,你可以創(chuàng)建非常靈活的日志查詢。
標(biāo)簽是 Loki 的日志數(shù)據(jù)的索引。它們被用來(lái)尋找壓縮的日志內(nèi)容,這些內(nèi)容以塊形式單獨(dú)存儲(chǔ)。每個(gè)獨(dú)特的標(biāo)簽和值的組合都定義了一個(gè)流,一個(gè)流的日志被分批壓縮,并作為塊存儲(chǔ)。
為了使 Loki 的效率和成本效益,我們必須負(fù)責(zé)任地使用標(biāo)簽。下一節(jié)將更詳細(xì)地探討這個(gè)問(wèn)題。
基數(shù) (Cardinality)
前面的兩個(gè)例子使用的是靜態(tài)定義的標(biāo)簽,只有一個(gè)值;但是,有一些方法可以動(dòng)態(tài)地定義標(biāo)簽。讓我們用 Apache 的日志和你可以用來(lái)解析這樣的日志行的大量的重合詞來(lái)看看。
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"
- 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
這個(gè)詞組匹配日志行的每一個(gè)組件,并將每個(gè)組件的值提取到一個(gè)捕獲組中。在管道代碼中,這些數(shù)據(jù)被放置在一個(gè)臨時(shí)數(shù)據(jù)結(jié)構(gòu)中,允許在處理該日志行時(shí)將其用于多種用途(此時(shí),這些臨時(shí)數(shù)據(jù)被丟棄)。關(guān)于這一點(diǎn)的更多細(xì)節(jié)可以在 這里 找到。
從該重合碼中,我們將使用兩個(gè)捕獲組,根據(jù)日志行本身的內(nèi)容動(dòng)態(tài)地設(shè)置兩個(gè)標(biāo)簽。
action(例如,action="GET",action="POST") status_code(例如, status_code="200", status_code="400")。
現(xiàn)在讓我們看幾個(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"
在 Loki 中,將創(chuàng)建以下數(shù)據(jù)流:
{job=”apache”,env=”dev”,action=”GET”,status_code=”200”} 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"
{job=”apache”,env=”dev”,action=”POST”,status_code=”200”} 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"
{job=”apache”,env=”dev”,action=”GET”,status_code=”400”} 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"
{job=”apache”,env=”dev”,action=”POST”,status_code=”400”} 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"
這四條日志行將成為四個(gè)獨(dú)立的流,并開(kāi)始填充四個(gè)獨(dú)立的塊。
任何符合這些標(biāo)簽/值組合的額外日志行將被添加到現(xiàn)有的流中。如果有另一個(gè)獨(dú)特的標(biāo)簽組合進(jìn)來(lái)(例如 status_code="500"),就會(huì)創(chuàng)建另一個(gè)新的流。
現(xiàn)在想象一下,如果你為 ip 設(shè)置一個(gè)標(biāo)簽。不僅每個(gè)來(lái)自用戶的請(qǐng)求都成為一個(gè)獨(dú)特的流。每個(gè)來(lái)自同一用戶的具有不同動(dòng)作或狀態(tài)代碼的請(qǐng)求都將得到它自己的流。
做一些簡(jiǎn)單的計(jì)算,如果有四個(gè)常見(jiàn)的動(dòng)作(GET, PUT, POST, DELETE)和四個(gè)常見(jiàn)的狀態(tài)代碼(雖然可能不止四個(gè)?。?,這將是 16 個(gè)流和 16 個(gè)獨(dú)立的塊?,F(xiàn)在,如果我們用一個(gè)標(biāo)簽來(lái)表示 ip,就把這個(gè)數(shù)字乘以每個(gè)用戶。你可以很快有幾千或幾萬(wàn)個(gè)流。
這會(huì)導(dǎo)致很高的 cardinality。會(huì)殺死 Loki。
當(dāng)我們談?wù)?cardinality 時(shí),我們指的是標(biāo)簽和值的組合以及它們創(chuàng)造的流的數(shù)量。高 cardinality 是指使用具有大范圍可能值的標(biāo)簽,如 ip,或結(jié)合許多標(biāo)簽,即使它們有一個(gè)小而有限的值集,如使用 status_code 和 action。
高 cardinality 導(dǎo)致 Loki 建立一個(gè)巨大的索引(讀作:????????),并將成千上萬(wàn)的小塊沖到對(duì)象存儲(chǔ)中(讀作:慢)。目前,Loki 在這種配置下表現(xiàn)很差,運(yùn)行和使用起來(lái)將是最不劃算和最沒(méi)有樂(lè)趣的。
使用并行化 (parallelization) 的最佳 Loki 性能
現(xiàn)在你可能會(huì)問(wèn):如果使用大量的標(biāo)簽或有大量數(shù)值的標(biāo)簽是不好的,那么我應(yīng)該如何查詢我的日志呢?如果沒(méi)有一個(gè)數(shù)據(jù)是有索引的,那查詢豈不是很慢?
當(dāng)我們看到使用 Loki 的人習(xí)慣于使用其他索引重復(fù)的解決方案時(shí),他們似乎覺(jué)得有義務(wù)定義大量的標(biāo)簽,以便有效地查詢他們的日志。畢竟,許多其他的日志解決方案都是關(guān)于索引的,這也是常見(jiàn)的思維方式。
在使用 Loki 時(shí),你可能需要忘記你所知道的東西,看看如何用并行化的方式來(lái)解決這個(gè)問(wèn)題。Loki 的超能力是將查詢分解成小塊,并將其并行調(diào)度,這樣你就可以在小時(shí)間內(nèi)查詢大量的日志數(shù)據(jù)。
這種粗暴的方法聽(tīng)起來(lái)可能并不理想,但讓我解釋一下為什么會(huì)這樣。
大型索引是復(fù)雜而昂貴的。通常情況下,你的日志數(shù)據(jù)的全文索引與日志數(shù)據(jù)本身的大小相同或更大。為了查詢你的日志數(shù)據(jù),你需要加載這個(gè)索引,而且為了性能,它可能應(yīng)該在內(nèi)存中。這是很難擴(kuò)展的,當(dāng)你攝入更多的日志時(shí),你的索引會(huì)很快變大。
現(xiàn)在讓我們來(lái)談?wù)?Loki,它的索引通常比你攝入的日志量小一個(gè)數(shù)量級(jí)。因此,如果你能很好地保持你的數(shù)據(jù)流和數(shù)據(jù)流的流失,那么與攝取的日志相比,索引的增長(zhǎng)非常緩慢。
Loki 將有效地保持你的靜態(tài)成本盡可能低(索引大小和內(nèi)存要求以及靜態(tài)日志存儲(chǔ)),并使查詢性能成為你可以在運(yùn)行時(shí)控制的水平擴(kuò)展。
為了了解這一點(diǎn),讓我們回過(guò)頭來(lái)看看我們查詢特定 IP 地址的訪問(wèn)日志數(shù)據(jù)的例子。我們不想用一個(gè)標(biāo)簽來(lái)存儲(chǔ) IP。相反,我們使用一個(gè)過(guò)濾器表達(dá)式來(lái)查詢它。
{job=”apache”} |= “11.11.11.11”
在幕后,Loki 會(huì)將該查詢分解成更小的片段(分片),并為標(biāo)簽所匹配的流打開(kāi)每個(gè)分片,開(kāi)始尋找這個(gè) IP 地址。
這些分片的大小和并行化的數(shù)量是可配置的,并基于你提供的資源。如果你愿意,你可以把分片的間隔配置到 5m,部署 20 個(gè)查詢器,在幾秒鐘內(nèi)處理幾十億字節(jié)的日志。或者你可以瘋狂地配置 200 個(gè)查詢器,處理 TB 級(jí)的日志。
這種較小的索引和平行的暴力查詢與較大/較快的全文索引之間的權(quán)衡,使得 Loki 能夠比其他系統(tǒng)節(jié)省成本。操作大型索引的成本和復(fù)雜性很高,而且通常是固定的--無(wú)論你是否查詢它,你都要一天 24 小時(shí)為它付費(fèi)。
這種設(shè)計(jì)的好處是,你可以決定你想擁有多少查詢能力,而且你可以按需改變。查詢性能成為你想在上面花多少錢的一個(gè)函數(shù)。同時(shí),數(shù)據(jù)被大量壓縮并存儲(chǔ)在低成本的對(duì)象存儲(chǔ)中,如 S3 和 GCS。這使固定的運(yùn)營(yíng)成本降到最低,同時(shí)還能實(shí)現(xiàn)令人難以置信的快速查詢能力
最佳實(shí)踐
這里有一些 Loki 目前最有效的標(biāo)簽做法,可以給你帶來(lái) Loki 的最佳體驗(yàn)。
1. 推薦靜態(tài)標(biāo)簽
像主機(jī)、應(yīng)用程序和環(huán)境這些東西是很好的標(biāo)簽。它們對(duì)于一個(gè)給定的系統(tǒng)/應(yīng)用程序來(lái)說(shuō)是固定的,并且有限定的值。使用靜態(tài)標(biāo)簽可以使你更容易在邏輯上查詢你的日志(例如,給我看一個(gè)給定的應(yīng)用程序和特定環(huán)境的所有日志,或者給我看一個(gè)特定主機(jī)上的所有應(yīng)用程序的所有日志)。
2. 謹(jǐn)慎使用動(dòng)態(tài)標(biāo)簽
太多的標(biāo)簽值組合會(huì)導(dǎo)致太多的數(shù)據(jù)流。在 Loki 中,這樣做的懲罰是一個(gè)大索引和存儲(chǔ)中的小塊,這反過(guò)來(lái)又會(huì)降低性能。
為了避免這些問(wèn)題,在你知道你需要它之前,不要為某樣?xùn)|西添加標(biāo)簽。使用過(guò)濾表達(dá)式 ( |= "text", |~ "regex", ...) 并對(duì)這些日志進(jìn)行暴力處理。這很有效--而且速度很快。
從早期開(kāi)始,我們就使用 promtail 管道為level動(dòng)態(tài)地設(shè)置了一個(gè)標(biāo)簽。這對(duì)我們來(lái)說(shuō)似乎很直觀,因?yàn)槲覀兘?jīng)常想只顯示level="error"的日志;然而,我們現(xiàn)在正在重新評(píng)估這一點(diǎn),因?yàn)閷懸粋€(gè)查詢。{app="loki"} |= "level=error"對(duì)我們的許多應(yīng)用來(lái)說(shuō),證明與{app="loki",level="error"}一樣快。
這似乎令人驚訝,但如果應(yīng)用程序有中等至低容量,該標(biāo)簽導(dǎo)致一個(gè)應(yīng)用程序的日志被分成多達(dá)五個(gè)流,這意味著 5 倍的塊被存儲(chǔ)。而加載塊有一個(gè)與之相關(guān)的開(kāi)銷。想象一下,如果這個(gè)查詢是{app="loki",level!="debug"}。這將不得不比{app="loki"} != "level=debug"}加載多的多數(shù)據(jù)塊。
上面,我們提到在你需要它們之前不要添加標(biāo)簽,那么你什么時(shí)候會(huì)需要標(biāo)簽?zāi)??再往下一點(diǎn)是關(guān)于 chunk_target_size 的部分。如果你把這個(gè)設(shè)置為 1MB(這是合理的),這將試圖以 1MB 的壓縮大小來(lái)切割塊,這大約是 5MB 左右的未壓縮的日志(可能多達(dá) 10MB,取決于壓縮)。如果你的日志有足夠的容量在比max_chunk_age更短的時(shí)間內(nèi)寫入 5MB,或者在這個(gè)時(shí)間范圍內(nèi)有多的多的塊,你可能要考慮用動(dòng)態(tài)標(biāo)簽把它分成獨(dú)立的流。
你想避免的是將一個(gè)日志文件分割成流,這將導(dǎo)致塊被刷新,因?yàn)榱魇强臻e的或在滿之前達(dá)到最大年齡。從 Loki 1.4.0 開(kāi)始,有一個(gè)指標(biāo)可以幫助你了解為什么要刷新數(shù)據(jù)塊sum by (reason) (rate(loki_ingester_chunks_flushed_total{cluster="dev"}[1m]))。
每個(gè)塊在刷新時(shí)都是滿的,這并不關(guān)鍵,但它將改善許多方面的操作。因此,我們目前的指導(dǎo)思想是盡可能避免動(dòng)態(tài)標(biāo)簽,而傾向于過(guò)濾器表達(dá)式。例如,不要添加 level 的動(dòng)態(tài)標(biāo)簽,而用|= "level=debug"代替。
3. 標(biāo)簽值必須始終是有界的
如果你要?jiǎng)討B(tài)地設(shè)置標(biāo)簽,千萬(wàn)不要使用可以有無(wú)界值或無(wú)限值的標(biāo)簽。這總是會(huì)給 Loki 帶來(lái)大問(wèn)題。
盡量將值限制在盡可能小的范圍內(nèi)。我們對(duì) Loki 能處理的數(shù)值沒(méi)有完美的指導(dǎo),但對(duì)于動(dòng)態(tài)標(biāo)簽來(lái)說(shuō),要考慮個(gè)位數(shù),或者10 個(gè)數(shù)值。這對(duì)靜態(tài)標(biāo)簽來(lái)說(shuō)就不那么重要了。例如,如果你的環(huán)境中有 1,000 臺(tái)主機(jī),那么有 1,000 個(gè)值的主機(jī)標(biāo)簽就會(huì)很好。
4. 注意客戶端的動(dòng)態(tài)標(biāo)簽
Loki 有幾個(gè)客戶端選項(xiàng)。Promtail(也支持 systemd 日志攝取和基于 TCP 的系統(tǒng)日志攝?。?a target="_blank">FluentD,Fluent Bit,一個(gè) Docker 插件,以及更多!
每一個(gè)都有方法來(lái)配置用什么標(biāo)簽來(lái)創(chuàng)建日志流。但要注意可能會(huì)用哪些動(dòng)態(tài)標(biāo)簽。使用 Loki 系列 API 來(lái)了解你的日志流是什么樣子的,看看是否有辦法減少流和 cardinality。系列 API 的細(xì)節(jié)可以在 這里 找到,或者你可以使用 logcli 來(lái)查詢 Loki 的系列信息。
5. 配置緩存
Loki 可以對(duì)數(shù)據(jù)進(jìn)行多層次的緩存,這可以極大地提高性能。這方面的細(xì)節(jié)將在今后的文章中介紹。
6. 每條流的日志必須按時(shí)間順序遞增(新版本默認(rèn)接受無(wú)序日志)
??Notes:
許多人在使用 Loki 時(shí)遇到的一個(gè)問(wèn)題是,他們的客戶端收到了錯(cuò)誤的日志條目。這是因?yàn)?Loki 內(nèi)部有一條硬性規(guī)定。
- 對(duì)于任何單一的日志流,日志必須總是以遞增的時(shí)間順序發(fā)送。如果收到的日志的時(shí)間戳比該流收到的最新日志的時(shí)間戳大,該日志將被放棄。
從這個(gè)聲明中,有幾件事需要剖析。首先,這個(gè)限制是針對(duì)每個(gè)流的。讓我們看一個(gè)例子:
{job=”syslog”} 00:00:00 i’m a syslog!
{job=”syslog”} 00:00:01 i’m a syslog!
如果 Loki 收到這兩行是針對(duì)同一流的,那么一切都會(huì)好起來(lái)。但這種情況呢?
{job=”syslog”} 00:00:00 i’m a syslog!
{job=”syslog”} 00:00:02 i’m a syslog!
{job=”syslog”} 00:00:01 i’m a syslog! <- 拒絕不符合順序的!
嗯,額。..... 但我們能做些什么呢?如果這是因?yàn)檫@些日志的來(lái)源是不同的系統(tǒng)呢?我們可以用一個(gè)額外的標(biāo)簽來(lái)解決這個(gè)問(wèn)題,這個(gè)標(biāo)簽在每個(gè)系統(tǒng)中是唯一的。
{job=”syslog”, instance=”host1”} 00:00:00 i’m a syslog!
{job=”syslog”, instance=”host1”} 00:00:02 i’m a syslog!
{job=”syslog”, instance=”host2”} 00:00:01 i’m a syslog! <- 被接受,這是一個(gè)新的流!
{job=”syslog”, instance=”host1”} 00:00:03 i’m a syslog! <- 被接受,流 1 仍是有序的
{job=”syslog”, instance=”host2”} 00:00:02 i’m a syslog! <- 被接受,流 2 仍是有序的
但是,如果應(yīng)用程序本身產(chǎn)生的日志是不正常的呢?嗯,這恐怕是個(gè)問(wèn)題。如果你用類似 promtail 管道階段的東西從日志行中提取時(shí)間戳,你反而可以不這樣做,讓 Promtail 給日志行分配一個(gè)時(shí)間戳?;蛘吣憧梢韵M趹?yīng)用程序本身中修復(fù)它。
但是我想讓 Loki 來(lái)解決這個(gè)問(wèn)題!為什么你不能為我緩沖數(shù)據(jù)流并重新排序?說(shuō)實(shí)話,因?yàn)檫@將給 Loki 增加大量的內(nèi)存開(kāi)銷和復(fù)雜性,而正如這篇文章中的一個(gè)共同點(diǎn),我們希望 Loki 簡(jiǎn)單而經(jīng)濟(jì)。理想情況下,我們希望改進(jìn)我們的客戶端來(lái)做一些基本的緩沖和排序,因?yàn)檫@似乎是解決這個(gè)問(wèn)題的一個(gè)更好的地方。
另外值得注意的是,Loki 推送 API 的批處理性質(zhì)可能會(huì)導(dǎo)致收到一些順序錯(cuò)誤的情況,這其實(shí)是誤報(bào)。(也許一個(gè)批處理部分成功了,并出現(xiàn)了;或者任何以前成功的東西都會(huì)返回一個(gè)失序的條目;或者任何新的東西都會(huì)被接受)。
7. 使用 chunk_target_size
這是在 2020 年早些時(shí)候我們 發(fā)布 Loki v1.3.0 時(shí)添加的,我們已經(jīng)用它實(shí)驗(yàn)了幾個(gè)月?,F(xiàn)在我們?cè)谒械沫h(huán)境中都有chunk_target_size: 1536000。這指示 Loki 嘗試將所有的 chunks 填充到 1.5MB 的目標(biāo)壓縮大小。這些較大的塊對(duì) Loki 來(lái)說(shuō)是更有效的處理。
其他幾個(gè)配置變量會(huì)影響到一個(gè)塊的大小。Loki 默認(rèn)的 max_chunk_age 為 1 小時(shí),chunk_idle_period 為 30 分鐘,以限制所使用的內(nèi)存量,以及在進(jìn)程崩潰時(shí)丟失日志的風(fēng)險(xiǎn)。
根據(jù)使用的壓縮方式(我們一直使用 snappy,它的可壓縮性較低,但性能較快),你需要 5-10 倍或 7.5-10MB 的原始日志數(shù)據(jù)來(lái)填充 1.5MB 的塊。記住,一個(gè)塊是每一個(gè)流,你把你的日志文件分成的流越多,在內(nèi)存中的塊就越多,在它們被填滿之前,它們被擊中上述的超時(shí)的可能性就越大。
很多小的、未填充的塊目前是 Loki 的頑石。我們一直在努力改善這一點(diǎn),并可能考慮在某些情況下使用壓縮器來(lái)改善這一點(diǎn)。但是,一般來(lái)說(shuō),指導(dǎo)原則應(yīng)該保持不變:盡力填充塊。
如果你有一個(gè)應(yīng)用程序,它的記錄速度足以迅速填滿這些塊(遠(yuǎn)遠(yuǎn)小于max_chunk_age),那么使用動(dòng)態(tài)標(biāo)簽將其分解成獨(dú)立的數(shù)據(jù)流就變得更加合理。
總結(jié)
我最后再?gòu)?qiáng)調(diào)一次這個(gè)死馬當(dāng)活馬醫(yī)的主意吧!
為了性能而使用并行化,而不是標(biāo)簽和索引
對(duì)標(biāo)簽要嚴(yán)格要求。靜態(tài)標(biāo)簽通常是好的,但動(dòng)態(tài)標(biāo)簽應(yīng)該少用。(如果你的日志流以每分鐘 5-10MB 的速度寫入,那么考慮一個(gè)動(dòng)態(tài)標(biāo)簽如何將其分成兩到三個(gè)流,這可以提高查詢性能。如果你的量比較少,堅(jiān)持使用 過(guò)濾表達(dá)式。
索引不一定是 Loki 的性能之路!首先要優(yōu)先考慮并行化和 LogQL 查詢過(guò)濾。
請(qǐng)記住:與其他日志存儲(chǔ)解決方案相比,Loki 需要一種不同的思維方式。我們正在對(duì) Loki 進(jìn)行優(yōu)化,以獲得更少的數(shù)據(jù)流和更小的索引,這有助于填充更大的塊,更容易通過(guò)并行化進(jìn)行查詢。
我們正在積極改進(jìn) Loki,并研究如何做到這一點(diǎn)。請(qǐng)務(wù)必繼續(xù)關(guān)注 Loki 故事的展開(kāi),我們都在琢磨如何將這個(gè)真正有效的工具發(fā)揮到極致!
Grafana 系列文章
三人行, 必有我?guī)? 知識(shí)共享, 天下為公. 本文由東風(fēng)微鳴技術(shù)博客 EWhisper.cn 編寫.