數(shù)棧技術(shù)分享:淺談云原生系統(tǒng)日志收集在數(shù)棧的實(shí)踐
本文整理自:淺談云原生系統(tǒng)日志收集在數(shù)棧的實(shí)踐
數(shù)棧是云原生—站式數(shù)據(jù)中臺PaaS,我們在github上有一個有趣的開源項目:FlinkX,歡迎給我們點(diǎn)個star!star!star!
https://github.com/DTStack/flinkx
FlinkX是一個基于Flink的批流統(tǒng)一的數(shù)據(jù)同步工具,既可以采集靜態(tài)的數(shù)據(jù),比如MySQL,HDFS等,也可以采集實(shí)時變化的數(shù)據(jù),比如MySQL binlog,Kafka等,是全域、異構(gòu)、批流一體的數(shù)據(jù)同步引擎,大家如果有興趣,歡迎來github社區(qū)找我們玩~

一、常規(guī)打法ELK
談到日志收集,估計大家第一個想到的就是ELK這個比較成熟的方案,如果是特別針對云原生上的,那么將采集器稍微變一下為 Fluentd 組成 EFK 即可。以上兩種方案其實(shí)沒有本質(zhì)上的區(qū)別,采集器換了換而已。最終存儲、查詢等等采用的還是 elasticsearch 這一套。
elasticsearch確實(shí)功能豐富、非常強(qiáng)大,但是代價也是極其昂貴的,elasticsearch 采用全文索引的方式,對存儲以及內(nèi)存的要求比較高,而這些代價換來的功能對于日常日志管理來說卻是不常用的。這些缺點(diǎn)在主機(jī)模式下其實(shí)是可以容忍的,但在云原生模式下就顯得比較臃腫了。
二、不講武德PLG
PLG 是 promtail+loki+grafana 的合稱,這是一套非常適合云原生下日志的采集方案。grafana 大家會比較熟悉,一個非常棒的可視化的框架,支持多種數(shù)據(jù)源。最常見的就是將prometheus的數(shù)據(jù)進(jìn)行可視化展示。而loki就是今天我們要談的主角,這個也是grafana 家的產(chǎn)品,promtail 則是 loki 的官方日志采集器。
與elk相比這一套方案非常輕量級,功能實(shí)用,使用起來簡單易用,并且在展示上采用 grafana 減少引入可視化的框架,展示終端上的統(tǒng)一也有利于用戶的使用。
(一) 日志新貴loki

Loki是受Prometheus啟發(fā)的水平可擴(kuò)展,高度可用的多租戶日志聚合系統(tǒng)。它的設(shè)計具有很高的成本效益,并且易于操作。它不索引日志的內(nèi)容,而是為每個日志流設(shè)置一組標(biāo)簽。
與其他日志聚合系統(tǒng)相比,Loki
不對日志進(jìn)行全文本索引。通過存儲壓縮的,非結(jié)構(gòu)化的日志以及僅索引元數(shù)據(jù),Loki更加易于操作且運(yùn)行成本更低。
使用與Prometheus相同的標(biāo)簽對日志流進(jìn)行索引和分組,從而使您能夠使用與Prometheus相同的標(biāo)簽在指標(biāo)和日志之間無縫切換。
特別適合存儲Kubernetes Pod日志。諸如Pod標(biāo)簽之類的元數(shù)據(jù)會自動被抓取并建立索引。
Grafana原生支持(需要Grafana v6.0以上)。
這段是loki 在 GitHub 上的介紹,可以看出這是一款為云原生而打造的輕量級日志聚合系統(tǒng)。目前社區(qū)非常活躍。而且采用 prometheus 類似的標(biāo)簽的思想,與 grafana 打通進(jìn)行可視化展示,無論是思想還是用法都非常的“云原生”。
(二) ?♂? 親兒子Promtail
promtail 是 loki 的官方日志采集器,本身代碼就在 loki 項目中。原生支持journal、syslog、file、docker類型的日志。采集器的本質(zhì)無非都是根據(jù)模式找到要采集的文件,然后對著個文件進(jìn)行類似tail的監(jiān)控,再把寫入文件的內(nèi)容發(fā)送給存儲端promtail 也是這樣,上面這些類型的本質(zhì)也都是文件,只不過這些類型的文件的格式是公開穩(wěn)定的規(guī)范,promtail 可以預(yù)先對其進(jìn)行進(jìn)行更深解析與封裝。
(三) Promtail 服務(wù)發(fā)現(xiàn)
1、 找到文件作為一個采集器,其第一步自然是要找到文件在哪里,然后才能做下面的收集與打標(biāo)簽推送等功能。普通靜態(tài)類型的日志是很好發(fā)現(xiàn)的,直接將你在配置文件中寫的路徑信息進(jìn)行匹配即可,比如 promtail 中path為 "/var/log/*.log"即將 /var/log目錄下所有的以.log 結(jié)尾的后綴文件作為要采集的對象即可。而要采集 k8s 模式內(nèi)的日志就稍顯麻煩。
首先我們想一下k8s 上跑的服務(wù)的日志到底是在哪里?
文件類型的日志
這種自然是還在你自定義的路徑上,如果這個路徑目錄沒有被掛載出來,那么就在容器內(nèi)部,如果掛載到了宿主機(jī)或者 pv 內(nèi),那么在 宿主機(jī)與 pv 內(nèi)也是可見的,這種類型的日志 promtail 是無法動態(tài)發(fā)現(xiàn)的,必須手工設(shè)置進(jìn)去。標(biāo)準(zhǔn)輸出的日志
這類日志其實(shí)是k8s推薦的日志輸出方式,這類日志其實(shí)是我們?nèi)粘S?kubectl log 看到的日志,這類日志存儲路徑在宿主機(jī)遵循/var/log/pods/
{namespace}_/{pod_id}_UUID/{container_name}/*.log這種格式
所以我們需要將這/var/log/pods 作為hostpath 掛載進(jìn) k8s 的容器內(nèi)部,才能讓 promtail 訪問到這些日志。
2、 打上標(biāo)簽
日志promtail可以訪問到了,但是還有一個問題還是如何為區(qū)分這些日志,loki采用類似prometheus的思想,將數(shù)據(jù)打上標(biāo)簽。也就是將日志打上pod的標(biāo)簽,那么單單憑借這個路徑自然是無法知道該pod上有哪些標(biāo)簽信息的。這里就需要用到服務(wù)發(fā)現(xiàn)了。
promtail的服務(wù)發(fā)現(xiàn)是直接采用的prometheus的服務(wù)發(fā)現(xiàn)做的。熟悉prometheus 的同學(xué)肯定配置過prometheus的服務(wù)發(fā)現(xiàn)的配置,kubernetes_sd_configs與relabel_configs。
這里promtail直接引入prometheus的代碼,與prometheus不同的是prometheus請求的資源對對象比較多,node、ingress、pod、deployment 等等都有,最終拼接的是metric的請求url,而promtail請求的對象為pod,并且過濾掉了不在該主機(jī)上的 pod。
拿到該主機(jī)的pod的信息后,再根據(jù)namespace, pod 的 id 拼接路徑,由于這個目錄已經(jīng)掛載進(jìn)去容器了,那么promtail 就可以關(guān)聯(lián)起容器的標(biāo)簽與容器的日志了。剩下的就是監(jiān)控與推送了。
(四) PLG 最佳實(shí)踐
loki 官方推薦的最佳實(shí)踐為采用 DamonSet部署 promtail 的方式,將 node 的 /var/lib/pods目錄掛載進(jìn)容器內(nèi)部,借助prometheus 的服務(wù)發(fā)現(xiàn)機(jī)制動態(tài)的為日志加上標(biāo)簽,無論是資源的占用程度還是部署維護(hù)難度都是非常低。這也是主流的云原生日志采集范式。

三、數(shù)棧日志實(shí)踐
(一) 數(shù)棧日志需求
全局 grep
根據(jù)關(guān)鍵字,搜索系統(tǒng)中所有出現(xiàn)的地方快速定位日志
根據(jù)機(jī)器名、ip、服務(wù)名等條件快速定位日志主機(jī)與云原生統(tǒng)一技術(shù)棧
減少使用學(xué)習(xí)成本,降低系統(tǒng)復(fù)雜性
(二) ? 主機(jī)模式
數(shù)棧主機(jī)模式日志聚合采用類似PLG DameonSet 的模式。每臺主機(jī)部署一臺 promtail,然后整個集群部署一套服務(wù)端 loki 與可視化端grafana。
promtail 采用static_configs定義采集的日志。但是promtail 畢竟還是太年輕了,定位偏向于云原生,所以針對主機(jī)功能還不夠完善,因此我們做了一些二次開發(fā)滿足我們的需求:
1、logtail 模式
原生 promtail 并不支持從文件尾部開始收集,當(dāng) promtail 啟動后,會將監(jiān)控的所有文件的內(nèi)容都進(jìn)行推送,這樣的情況在云原生并沒有太大問題.
主機(jī)模式下如果要監(jiān)控的日志已經(jīng)存在并且有大量的內(nèi)容的話,promtail 啟動會將文件的內(nèi)容從頭開始推送,短時間內(nèi)造成大量的日志往loki推送,很大的概率會被 loki 限流導(dǎo)致推送失敗。
所以最好的方式就是有類似 filebeat 的 logtail 的模式,及只推送服務(wù)啟動后的文件寫入的日志。
這個地方我們對此作了二次開發(fā),增加一個logtail 模式的開關(guān),如果該開關(guān)為 true,這第一次啟動 promtail 的時候?qū)⒉粫念^推送日志。
2、path 支持多路徑
原生 promtail 不支持多路徑 path 參數(shù)只能寫一個表達(dá)式,但是現(xiàn)實(shí)的需求可能是既要看業(yè)務(wù)的日志還要看 gc 的日志。
但是他們又是屬于同一類別的標(biāo)簽。單個path的匹配無法涵蓋其兩個,不改代碼的解決方法就是再為其寫一個 target。
這樣做繁瑣且不利于維護(hù)。所以我們這里也對其做了二次開發(fā)

(三) 云原生模式
傳統(tǒng)的云原生模式采用 PLG 的主流模式就好了,但是數(shù)棧作為一整套系統(tǒng)對企業(yè)交付的時候有諸多限制會導(dǎo)致demoset模式并不可用,最大的挑戰(zhàn)是權(quán)限,只有一個 namespace 的權(quán)限,不能掛載/var/lib/pods
在這種情況下如何使用 PLG呢?
其實(shí)主要變化的地方在于promtail的使用,這里首先要聲明的一點(diǎn)是,數(shù)棧的服務(wù)的日志都為文件輸出。
首先是選擇damonset 模式部署還是sidecar模式部署,demonset模式的優(yōu)點(diǎn)是節(jié)省資源,缺點(diǎn)是權(quán)限有要求。sidecar模式與之相反,為了適用更嚴(yán)格的交付條件,我們選擇采用 sidecar 的模式進(jìn)行采集。
sidecar 模式就是為當(dāng)每個服務(wù)進(jìn)行部署的時候就自動為其添加一個log容器,該容器與服務(wù)容器共同掛載一個共同的空的數(shù)據(jù)卷,服務(wù)容器將日志寫入該數(shù)據(jù)卷中,log容器對數(shù)據(jù)卷下的日志進(jìn)行采集。

1、? promtail 在數(shù)棧如何動態(tài)配置標(biāo)簽
通過sidecar的模式我們讓log Container與Master Container共享一個日志目錄,這樣就promtail容器內(nèi)就可以拿到了日志的文件,但是promtail還不知道要采集哪些日志,以及他的標(biāo)簽是什么。
因?yàn)槟憧赡苤幌氩杉?log的日志,也可能只想采集.json的日志,或者都有的服務(wù)這個配置可能是不同的,所以也不能寫死,那如何解決這個問題呢?
promtail 在 v2.10中新增加了一個feature ,就是可以在配置文件中引用環(huán)境變量,通過這個特性我們可以將promtail的path參數(shù)寫成${LOG_PATH},然后將服務(wù)的logpath以環(huán)境變量的方式設(shè)置進(jìn)去比如LOG_PATH=/var/log/commonlog/*.log
既然我們可以通過環(huán)境變量的方式在服務(wù)創(chuàng)建的時候設(shè)置path,那么標(biāo)簽我們也可以動態(tài)的設(shè)置進(jìn)去。那么我們都需什么維度的標(biāo)簽?zāi)??這個不同的公司肯定有不同的維度,但是必須遵循的一個原則就是可以唯一確定該pod。一般的維度有deployment、podid、node等。這些標(biāo)簽在創(chuàng)建的時候就通過環(huán)境變量注入進(jìn)去,而podid 這些環(huán)境變量利用的是k8s 的 downward api 的方式注入的。
注意:這里不可用使用 promtail 的服務(wù)發(fā)現(xiàn)機(jī)制配置標(biāo)簽,因?yàn)閜romtail 的服務(wù)發(fā)現(xiàn)的原理是請求 APIServer 獲取所有pod 的標(biāo)簽。然后利用路徑進(jìn)行匹配,將標(biāo)簽與日志關(guān)聯(lián)。在沒有掛載宿主機(jī)/var/log/pods目錄到promtail 時,即使拿到了標(biāo)簽也無法與日志進(jìn)行關(guān)聯(lián)。
2、? promtail 在數(shù)棧如何部署
為每個服務(wù)增加一個Log Container如果手工操作的話實(shí)在是太繁瑣了,而且不利于維護(hù)。最好的方式就將原本的服務(wù)抽象為是注冊一個CRD,然后編寫 k8s operator通過list&watch該類型的對象,在該對象創(chuàng)建的時候,動態(tài)的注入一個Logcontainer,以及相應(yīng)的環(huán)境變量和為其掛載共同目錄。
這樣當(dāng)該CR創(chuàng)建的時候,promtail就作為sidecar注入了其中。并且讀到的環(huán)境變量就是operator 動態(tài)設(shè)置的環(huán)境變量,靈活度非常高。

四、總結(jié)
(一) 數(shù)棧日志收集優(yōu)勢
一套日志聚合分析框架解決主機(jī)與云原生兩種場景,減少了系統(tǒng)復(fù)雜度
日志可視化采用 grafana,可視化效果較好,而且grafana 與 prometheus已經(jīng)是云原生監(jiān)控的是事實(shí)上的標(biāo)準(zhǔn)了,開發(fā)運(yùn)維都比較熟悉,減少了學(xué)習(xí)成本
loki 查詢語法簡單,但是功能強(qiáng)大
與 ELK 相比,更加輕量級
(二)??未來規(guī)劃
當(dāng)前使用 sidecar 模式,資源占用較多,后續(xù)考慮在進(jìn)一步優(yōu)化
loki 分布式部署優(yōu)化
最后給大家分享一下數(shù)棧當(dāng)前日志模塊可視化的效果,是不是超級酷炫?