在企業(yè)大數(shù)據(jù)體系建設(shè)過(guò)程中,數(shù)據(jù)采集是其中的首要環(huán)節(jié)。然而,當(dāng)前行業(yè)內(nèi)的相關(guān)開(kāi)源數(shù)據(jù)采集組件,并無(wú)法滿(mǎn)足企業(yè)大規(guī)模數(shù)據(jù)采集的需求與有效的數(shù)據(jù)采集治理,所以大部分企業(yè)都采用自研開(kāi)發(fā)采集組件的方式。本文通過(guò)在vivo的日志采集服務(wù)的設(shè)計(jì)實(shí)踐經(jīng)驗(yàn),為大家提供日志采集Agent在設(shè)計(jì)開(kāi)發(fā)過(guò)程中的關(guān)鍵設(shè)計(jì)思路。
一、概述
在企業(yè)大數(shù)據(jù)體系的建設(shè)過(guò)程中,數(shù)據(jù)的處理一般包含4個(gè)步驟:采集、存儲(chǔ)、計(jì)算和使用。其中,數(shù)據(jù)采集,是建設(shè)過(guò)程中的首要的環(huán)節(jié),也是至關(guān)重要的環(huán)節(jié),如果沒(méi)有采集就沒(méi)有數(shù)據(jù),更談不上后續(xù)的數(shù)據(jù)處理與使用。所以,我們看到的企業(yè)中的運(yùn)營(yíng)報(bào)表、決策報(bào)表、日志監(jiān)控、審計(jì)日志等的數(shù)據(jù)來(lái)源都是基于數(shù)據(jù)采集。一般的,我們對(duì)數(shù)據(jù)采集的定義是,把各種分散的源頭上的數(shù)據(jù)(可以包括企業(yè)產(chǎn)品的埋點(diǎn)的日志、服務(wù)器日志、數(shù)據(jù)庫(kù)、IOT設(shè)備日志等)統(tǒng)一匯聚到大數(shù)據(jù)存儲(chǔ)組件的過(guò)程(如下圖所示)。其中,日志文件類(lèi)型的采集場(chǎng)景,是各種數(shù)據(jù)采集類(lèi)型中最常見(jiàn)的一種。接下來(lái),將圍繞該場(chǎng)景提出我們的設(shè)計(jì)實(shí)踐方案。
通常,日志采集服務(wù)可以分為幾個(gè)部分(業(yè)界常見(jiàn)的架構(gòu)如下圖所示):日志采集Agent組件(常見(jiàn)的開(kāi)源采集Agent組件有Flume、Logstash、Scribe等)、采集傳輸與存儲(chǔ)組件(如kafka、HDFS)、采集管理平臺(tái)。Bees采集服務(wù)是vivo自研的日志采集服務(wù),本文章是通過(guò)在Bees采集服務(wù)中的關(guān)鍵組件bees-agent的開(kāi)發(fā)實(shí)踐后,總結(jié)出一個(gè)通用的日志采集Agent設(shè)計(jì)中的核心技術(shù)點(diǎn)和一些關(guān)鍵思考點(diǎn),希望對(duì)大家有用。
二、特性&能力
-
具備基本的日志文件的實(shí)時(shí)與離線(xiàn)采集能力
-
基于日志文件,無(wú)侵入式采集日志
-
具備自定義的過(guò)濾超大日志的能力
-
具備自定義的過(guò)濾采集、匹配采集、格式化的能力
-
具備自定義的限速采集的能力
-
具備秒級(jí)別的實(shí)時(shí)采集時(shí)效性
-
具備斷點(diǎn)續(xù)傳能力,升級(jí)和停止不丟數(shù)據(jù)
-
具備可視化的、中心化的采集任務(wù)管理平臺(tái)
-
豐富的監(jiān)控指標(biāo)與告警(包括采集流量、時(shí)效性、完整性等)
-
低系統(tǒng)資源開(kāi)銷(xiāo)(包括磁盤(pán)、內(nèi)存、CPU及網(wǎng)絡(luò)等)
三、設(shè)計(jì)原則
-
簡(jiǎn)單優(yōu)雅
-
健壯穩(wěn)定
四、關(guān)鍵設(shè)計(jì)
目前業(yè)界流行的日志采集Agent組件,開(kāi)源的有Flume、Logstash、Scribe、FileBeats、Fluentd等,自研的有阿里的Logtail。它們都有不錯(cuò)的性能與穩(wěn)定性,如果想要快速上手,可以不妨使用它們。但是一般大企業(yè)會(huì)有個(gè)性化的采集需求,比如采集任務(wù)大規(guī)模管理、采集限速、采集過(guò)濾等,還有采集任務(wù)平臺(tái)化、任務(wù)可視化的需求,為了滿(mǎn)足上面這些需求我們自研了一個(gè)日志采集Agent。
在做一切的設(shè)計(jì)和開(kāi)發(fā)之前,我們?cè)O(shè)定了采集Agent最基本的設(shè)計(jì)原則,即簡(jiǎn)單優(yōu)雅、健壯穩(wěn)定。
日志文件采集的一般流程會(huì)包括:文件的發(fā)現(xiàn)與監(jiān)聽(tīng)、文件讀取,日志內(nèi)容的格式化、過(guò)濾、聚合與發(fā)送。當(dāng)我們開(kāi)始著手開(kāi)始設(shè)計(jì)這樣一個(gè)日志采集Agent時(shí),會(huì)遇到不少關(guān)鍵的難點(diǎn)問(wèn)題,比如:日志文件在哪里?如何發(fā)現(xiàn)日志文件新增?如何監(jiān)聽(tīng)日志內(nèi)容追加?如何識(shí)別一個(gè)文件?宕機(jī)重啟怎么辦?如何斷點(diǎn)續(xù)傳?等等問(wèn)題,接下來(lái),我們針對(duì)日志采集Agent設(shè)計(jì)過(guò)程中遇到的關(guān)鍵問(wèn)題,為大家一一解答。(注:下文出現(xiàn)的文件路徑與文件名都為演示樣例非真實(shí)路徑)
4.1 日志文件發(fā)現(xiàn)與監(jiān)聽(tīng)
Agent要如何知道采集哪些日志文件呢?
最簡(jiǎn)單的設(shè)計(jì),就是在Agent的本地配置文件中,把需要采集的日志文件路徑都一一羅列進(jìn)去,比如 /home/sample/logs/access1.log、/home/sample/logs/access2.log、/home/sample/logs/access3.log 等,這樣Agent通過(guò)讀取配置文件得到對(duì)應(yīng)的日志文件列表,這樣就能遍歷文件列表讀取日志信息。但是實(shí)際情況是,日志文件是動(dòng)態(tài)生成的,像一般tomcat的業(yè)務(wù)日志,每個(gè)小時(shí)都會(huì)滾動(dòng)生成一個(gè)新的的日志文件,日志名字通常會(huì)帶上時(shí)間戳,命名類(lèi)似 /data/sample/logs/
access.2021110820.log,所以采用直接配置固定的文件列表方式是行不通的。
所以,我們想到可以使用一個(gè)文件夾路徑和日志文件名使用正則表達(dá)式或者通配符來(lái)表示(為了方便,下文統(tǒng)一使用通配符來(lái)表示)。機(jī)器上的日志一般固定存在某一個(gè)目錄下,比如 /data/sample/logs/下,文件名由于某種規(guī)則是滾動(dòng)產(chǎn)生的(比如時(shí)間戳),類(lèi)似 access.2021110820.log、
access.2021110821.log、
access.2021110822.log,我們可以簡(jiǎn)單粗暴使用 access.*.log 的通配方法來(lái)匹配這一類(lèi)的日志,當(dāng)然實(shí)際情況可以根據(jù)你需要的匹配粒度去選擇你的正則表達(dá)式。有了這個(gè)通配符方法,我們的Agent就能的匹配滾動(dòng)產(chǎn)生的一批日志文件了。
如何持續(xù)發(fā)現(xiàn)和監(jiān)聽(tīng)到新產(chǎn)生的日志文件呢?
由于新的日志文件會(huì)由其他應(yīng)用程序(比如Nginx、Tomcat等)持續(xù)的按小時(shí)動(dòng)態(tài)產(chǎn)生的,Agent如何使用通配符快速去發(fā)現(xiàn)這個(gè)新產(chǎn)生的文件呢?
最容易想到的,是使用輪詢(xún)的設(shè)計(jì)方案,即是通過(guò)一個(gè)定時(shí)任務(wù)來(lái)檢查對(duì)應(yīng)目錄下的日志文件是否有增加,但是這種簡(jiǎn)單的方案有個(gè)問(wèn)題,就是如果輪詢(xún)間隔時(shí)間太長(zhǎng),比如間隔設(shè)置為10s、5s,那么日志采集的時(shí)效性滿(mǎn)足不了我們的需求;如果輪詢(xún)間隔時(shí)間太短,比如500ms,大量的無(wú)效的輪詢(xún)檢查又會(huì)消耗許多CPU資源。幸好,Linux內(nèi)核給我們提供一種高效的文件事件監(jiān)聽(tīng)機(jī)制:Linux Inotify機(jī)制。該機(jī)制可監(jiān)聽(tīng)任意文件的操作,比如文件創(chuàng)建、文件刪除和文件內(nèi)容變更,內(nèi)核會(huì)給應(yīng)用層一個(gè)對(duì)應(yīng)的事件通知。Inotify這種的事件機(jī)制比輪詢(xún)機(jī)制高效的多,也不存在CPU空跑浪費(fèi)系統(tǒng)資源的情況。在java中,使用java.nio.file.WatchService,可以參考如下核心代碼:
/**
* 訂閱文件或目錄的變更事件
*/
public synchronized BeesWatchKey watchDir(File dir, WatchEvent.Kind>... watchEvents) throws IOException {
if (!dir.exists() && dir.isFile()) {
throw new IllegalArgumentException("watchDir requires an exist directory, param: " + dir);
}
Path path = dir.toPath().toAbsolutePath();
BeesWatchKey beesWatchKey = registeredDirs.get(path);
if (beesWatchKey == null) {
beesWatchKey = new BeesWatchKey(subscriber, dir, this, watchEvents);
registeredDirs.put(path, beesWatchKey);
logger.info("successfully watch dir: {}", dir);
}
return beesWatchKey;
}
public synchronized BeesWatchKey watchDir(File dir) throws IOException {
WatchEvent.Kind>[] events = {
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY
};
return watchDir(dir, events);
}
綜合以上思考,日志文件的發(fā)現(xiàn)和日志內(nèi)容變更的監(jiān)聽(tīng),我們使用的是"inotify機(jī)制為主+輪詢(xún)機(jī)制兜底"、"通配符"的設(shè)計(jì)方案,如下圖所示:
4.2日志文件的唯一標(biāo)識(shí)
要設(shè)計(jì)日志文件的唯一標(biāo)識(shí),如果直接使用日志文件的名稱(chēng)是行不通的,日志文件名可能被頻繁重復(fù)使用,比如,一些應(yīng)用程序使用的日志框架在輸出日志時(shí),對(duì)于當(dāng)前應(yīng)用正在輸出的日志命名是不帶任何時(shí)間戳信息的,比如固定是 access.log,只有等到當(dāng)前小時(shí)寫(xiě)入文件完畢時(shí),才把文件重命名為 access.2021110820.log,此時(shí)新生產(chǎn)的日志文件命名也是 access.log,該文件名對(duì)于采集Agent來(lái)說(shuō)是重復(fù)的,所以文件名是無(wú)法作為文件唯一標(biāo)識(shí)。
我們想到使用Linux操作系統(tǒng)上的文件inode號(hào)作為文件標(biāo)識(shí)符。Unix/Linux文件系統(tǒng)使用inode號(hào)來(lái)識(shí)別不同文件,即使移動(dòng)文件或重命名文件,inode號(hào)是保持不變的,創(chuàng)建一個(gè)新文件,會(huì)給這個(gè)新文件分配一個(gè)新的不重復(fù)的inode號(hào),這樣就能與現(xiàn)有磁盤(pán)上的其他文件很好區(qū)分。我們使用 ls -i access.log 可以快速查看該文件的inode號(hào),如下代碼塊所示:
ls -i access.log
62651787 access.log
一般來(lái)說(shuō),使用系統(tǒng)的inode號(hào)作為標(biāo)識(shí),已經(jīng)能滿(mǎn)足大多數(shù)的情況了,但是為了更嚴(yán)謹(jǐn)?shù)目紤],還可以進(jìn)一步升級(jí)方案。因?yàn)長(zhǎng)inux 的inode號(hào)存在復(fù)用的情況,這里的"復(fù)用"要和"重復(fù)"區(qū)別一下,在一臺(tái)機(jī)器上的所有文件不會(huì)同一時(shí)刻出現(xiàn)重復(fù)的兩個(gè)inode號(hào),但是當(dāng)文件刪除后,另一個(gè)新文件創(chuàng)建時(shí),這個(gè)文件的inode號(hào)是可能復(fù)用之前刪除文件的inode號(hào)的,代碼邏輯處理不好,很可能造成日志文件漏采集,這一點(diǎn)是要注意的。為了規(guī)避這個(gè)問(wèn)題,我們把文件的唯一標(biāo)識(shí)設(shè)計(jì)為" 文件inode與文件簽名組合",這里的文件簽名使用的是該文件內(nèi)容前128字節(jié)的Hash值,代碼參考如下:
public static String signFile(File file) throws IOException {
String filepath = file.getAbsolutePath();
String sign = null;
RandomAccessFile raf = new RandomAccessFile(filepath, "r");
if (raf.length() >= SIGN_SIZE) {
byte[] tbyte = new byte[SIGN_SIZE];
raf.seek(0);
raf.read(tbyte);
sign = Hashing.sha256().hashBytes(tbyte).toString();
}
return sign;
}
關(guān)于inode再補(bǔ)充點(diǎn)小知識(shí)。Linux inode是會(huì)滿(mǎn)的,inode的信息存儲(chǔ)本身也會(huì)消耗一些硬盤(pán)空間,因?yàn)閕node號(hào)只是inode內(nèi)容中的一小部分,inode內(nèi)容主要是包含文件的元數(shù)據(jù)信息:如文件的字節(jié)數(shù)、文件數(shù)據(jù)block的位置、文件的讀寫(xiě)執(zhí)行權(quán)限、文件的時(shí)間戳等,可以用stat命令,查看某個(gè)文件完整的inode信息(stat access.log)。因?yàn)檫@樣的設(shè)計(jì),操作系統(tǒng)是將硬盤(pán)分成兩個(gè)區(qū)域的:一個(gè)是數(shù)據(jù)區(qū),存放文件數(shù)據(jù);另一個(gè)是inode區(qū),存放inode所包含的信息。每個(gè)inode節(jié)點(diǎn)的大小,一般是128字節(jié)或256字節(jié)。查看每個(gè)硬盤(pán)分區(qū)的inode總數(shù)和已經(jīng)使用的數(shù)量,可以使用df -i命令。由于每個(gè)文件都必須有一個(gè)inode,如果一個(gè)日志機(jī)器上,日志文件小而且數(shù)量太多,是有可能發(fā)生操作系統(tǒng)inode用完了即是inode區(qū)磁盤(pán)滿(mǎn)了,但是我們使用的數(shù)據(jù)區(qū)硬盤(pán)還未存滿(mǎn)的情況。這時(shí),就無(wú)法在硬盤(pán)上創(chuàng)建新文件。所以在日志打印規(guī)范上是要避免產(chǎn)生大量的小日志文件的。
4.3 日志內(nèi)容的讀取
發(fā)現(xiàn)并且能有效監(jiān)聽(tīng)日志文件后,我們應(yīng)該如何去讀取這個(gè)日志文件中實(shí)時(shí)追加的日志內(nèi)容呢?日志內(nèi)容的讀取,我們期望從日志文件中把每一行的日志內(nèi)容逐行讀取出來(lái),每一行以 或者 為分隔符。很顯然,我們不能直接簡(jiǎn)單采用InputStreamReader去讀取,因?yàn)镽eader只能按照字符從頭到尾讀取整個(gè)日志文件,不適合讀取實(shí)時(shí)追加日志內(nèi)容的情況;最合適的選擇應(yīng)該是使用RandomAccessFile。RandomAccessFile它為代碼開(kāi)發(fā)者提供了一個(gè)可供設(shè)置的指針,通過(guò)指針開(kāi)發(fā)者可以訪(fǎng)問(wèn)文件的隨機(jī)位置,參考下圖:
通過(guò)這種方式,當(dāng)某一時(shí)刻出現(xiàn)線(xiàn)程讀取到文件末尾時(shí),只需要記錄當(dāng)前的位置,線(xiàn)程就進(jìn)入等待狀態(tài),直到有新的日志內(nèi)容寫(xiě)入后,線(xiàn)程又重新啟動(dòng),啟動(dòng)后可以接著上次的尾部往下讀取,代碼參考如下。另外,在進(jìn)程掛或者宕機(jī)恢復(fù)后,也會(huì)用到RandomAccessFile來(lái)從指定點(diǎn)位開(kāi)始讀取,不需要從整個(gè)文件頭部重新讀取。關(guān)于斷點(diǎn)續(xù)傳的能力后文會(huì)提到。
RandomAccessFile raf = new RandomAccessFile(file, "r");
byte[] buffer;
private void readFile() {
if ((raf.length() - raf.getFilePointer()) < BUFFER_SIZE) {
buffer = new byte[(int) (raf.length() - raf.getFilePointer())];
} else {
buffer = new byte[BUFFER_SIZE];
}
raf.read(buffer, 0, buffer.length);
}
4.4 實(shí)現(xiàn)斷點(diǎn)續(xù)傳
機(jī)器宕機(jī)、Java進(jìn)程O(píng)OM重啟、Agent升級(jí)重啟等這些是常有的事,那么如何在這些情況下保障采集數(shù)據(jù)的正確呢?這個(gè)問(wèn)題主要考慮的是采集Agent斷點(diǎn)續(xù)傳的能力。一般的,我們?cè)诓杉^(guò)程中需要記錄當(dāng)前的采集點(diǎn)位(采集點(diǎn)位,即RandomAccessFile中最后的指針指向的位置,一個(gè)整型數(shù)值),當(dāng)Agent把對(duì)應(yīng)緩沖區(qū)的數(shù)據(jù)成功發(fā)送到kafka后,此時(shí)可以先把最新點(diǎn)位的數(shù)值更新到內(nèi)存,并且通過(guò)一個(gè)定時(shí)任務(wù)(默認(rèn)是3s)持久化內(nèi)存中的采集點(diǎn)位數(shù)值到本地的磁盤(pán)的點(diǎn)位文件中。這樣,當(dāng)出現(xiàn)進(jìn)程停止,重新啟動(dòng)時(shí),加載本次磁盤(pán)文件中的采集點(diǎn)位,并使用RandomAccessFile移動(dòng)到對(duì)應(yīng)的點(diǎn)位,實(shí)現(xiàn)了從上一次停止的點(diǎn)位繼續(xù)往下采集的能力,Agent可以恢復(fù)到原有的狀態(tài),從而實(shí)現(xiàn)了斷點(diǎn)續(xù)傳,有效規(guī)避重復(fù)采集或者漏采集的風(fēng)險(xiǎn)。
Agent針對(duì)的每一個(gè)采集任務(wù)會(huì)有一個(gè)對(duì)應(yīng)的點(diǎn)位文件,一個(gè)Agent如果有多個(gè)采集任務(wù),將會(huì)對(duì)應(yīng)多個(gè)點(diǎn)位文件。一個(gè)點(diǎn)位文件存儲(chǔ)的內(nèi)容格式為JSON數(shù)組(如下圖所示)。其中file表示任務(wù)所采集的文件的名字,inode即文件的inode,pos即position的縮小,表示點(diǎn)位的數(shù)值;
[
{
"file": "/home/sample/logs/bees-agent.log",
"inode": 2235528,
"pos": 621,
"sign": "cb8730c1d4a71adc4e5b48931db528e30a5b5c1e99a900ee13e1fe5f935664f1"
}
]
4.5實(shí)時(shí)數(shù)據(jù)發(fā)送
前面主要介紹了,日志文件的實(shí)時(shí)的發(fā)現(xiàn)、實(shí)時(shí)的日志內(nèi)容變更監(jiān)聽(tīng)、日志內(nèi)容的讀取等設(shè)計(jì)方案,接下來(lái)介紹Agent的數(shù)據(jù)發(fā)送。
最簡(jiǎn)單的模型是,Agent通過(guò)Kafka Client把數(shù)據(jù)直接發(fā)送到Kafka分布式消息中間件,這也是一種簡(jiǎn)潔可行的方案。實(shí)際上在Bees的采集鏈路架構(gòu)中,在Agent與Kafka的數(shù)據(jù)鏈路中我們?cè)黾恿艘粋€(gè)"組件bees-bus“(如下圖所示)。
bees-bus組件主要起到匯聚數(shù)據(jù)的作用,類(lèi)似于Flume在采集鏈路中聚合的角色。Agent基于Netty開(kāi)源框架實(shí)現(xiàn)NettyRpcClient與Bus之間通訊實(shí)現(xiàn)數(shù)據(jù)發(fā)送。網(wǎng)絡(luò)傳輸部分展開(kāi)講內(nèi)容較多,非本文章重點(diǎn)就此帶過(guò)(具體可參考Flume NettyAvroRpcClient實(shí)現(xiàn))。
這里稍微補(bǔ)充下,我們引入bees-bus的目的主要有以下幾個(gè):
-
收斂來(lái)自于Agent過(guò)多的網(wǎng)絡(luò)連接數(shù),避免所有Agent直連Kafka broker對(duì)其造成較大的壓力;
-
數(shù)據(jù)匯聚到Bus后,Bus具備流量多路輸出的能力,可以實(shí)現(xiàn)跨機(jī)房Kafka數(shù)據(jù)容災(zāi);
-
在遇到流量陡增的情況下, 會(huì)導(dǎo)致topic分區(qū)所在broker機(jī)器磁盤(pán)IO繁忙進(jìn)而導(dǎo)致數(shù)據(jù)反壓到客戶(hù)端, 由于kafka副本遷移比較耗時(shí)所以出現(xiàn)問(wèn)題后恢復(fù)較慢,Bus可以起到一層緩沖層的作用。
4.6離線(xiàn)采集能力
除了上面常見(jiàn)的實(shí)時(shí)日志采集的場(chǎng)景外(一般是日志采集到kafka這類(lèi)消息中間件),Bees采集還有一個(gè)離線(xiàn)日志采集的場(chǎng)景。所謂離線(xiàn)日志采集,一般是指把日志文件是采集到HDFS下(參考下圖)。
這些日志數(shù)據(jù)是用于下游的Hive離線(xiàn)數(shù)倉(cāng)建設(shè)、離線(xiàn)報(bào)表分析使用。該場(chǎng)景數(shù)據(jù)時(shí)效性沒(méi)有那么強(qiáng),一般是按天為單位使用數(shù)據(jù)(我們常說(shuō)的T+1數(shù)據(jù)),所以日志數(shù)據(jù)采集無(wú)需像實(shí)時(shí)日志采集一樣,實(shí)時(shí)的一行一行的采集。離線(xiàn)采集一般可以按照固定時(shí)間一個(gè)批次采集。我們默認(rèn)是每隔一小時(shí)定時(shí)采集上個(gè)小時(shí)產(chǎn)生的一個(gè)完整的小時(shí)日志文件,比如在21點(diǎn)的05分,采集Agent則開(kāi)始采集上個(gè)小時(shí)產(chǎn)生的日志文件(access.2021110820.log),該文件保存了20點(diǎn)內(nèi)產(chǎn)生的完整的(20:00~20:59)日志內(nèi)容。
實(shí)現(xiàn)離線(xiàn)的采集能力,我們的Agent通過(guò)集成HDFS Client的基本能力來(lái)實(shí)現(xiàn),HDFS Client中使用 FSDataOutputStream 可以快速的完成一個(gè)文件PUT到HDFS的目錄下。
尤其要關(guān)注的一點(diǎn)是,離線(xiàn)采集需要特別的增加了一個(gè)限流采集的能力。由于離線(xiàn)采集的特點(diǎn)是,在整點(diǎn)左右的時(shí)刻,所有的機(jī)器上的Agent會(huì)幾乎同時(shí)全量開(kāi)啟采集,如果日志量大、采集速度過(guò)快,可能會(huì)造成該時(shí)刻公司網(wǎng)絡(luò)帶寬被快速占用飆升,超出全網(wǎng)帶寬上限,進(jìn)一步會(huì)影響其他業(yè)務(wù)的正常服務(wù),引發(fā)故障;還有一個(gè)需要關(guān)注的就是離線(xiàn)采集整點(diǎn)時(shí)刻對(duì)機(jī)器磁盤(pán)資源的需求是很大,通過(guò)限流采集,可以有效削平對(duì)磁盤(pán)資源的整點(diǎn)峰值,避免影響其他服務(wù)。
4.7 日志文件清理策略
業(yè)務(wù)日志源源不斷的產(chǎn)生落到機(jī)器的磁盤(pán)上,單個(gè)小時(shí)的日志文件大小,小的可能是幾十MB,大的可以是幾十GB,磁盤(pán)很有可能在幾小時(shí)內(nèi)被占滿(mǎn),導(dǎo)致新的日志無(wú)法寫(xiě)入造成日志丟失,另一方面可能導(dǎo)致更致命的問(wèn)題,linux 操作系統(tǒng)報(bào) “No space left on device 異常",引發(fā)其他進(jìn)程的各種故障;所以機(jī)器上的日志文件需要有一個(gè)清理的策略。
我們采用的策略是,所有的機(jī)器都默認(rèn)啟動(dòng)了一個(gè)shell的日志清理腳本,定期檢查固定目錄下的日志文件,規(guī)定日志文件的生命周期為6小時(shí),一旦發(fā)現(xiàn)日志文件是6小時(shí)以前的文件,則會(huì)對(duì)其進(jìn)行刪除(執(zhí)行 rm 命令)。
因?yàn)槿罩疚募膭h除,不是由日志采集Agent自身發(fā)起和執(zhí)行的,那么可能出現(xiàn)”采集速度跟不上刪除速度(采集落后6小時(shí))“的情況。比如日志文件還在采集,但是刪除腳本已經(jīng)檢測(cè)到該文件生命周期已達(dá)6小時(shí)準(zhǔn)備對(duì)其進(jìn)行刪除;這種情況,我們只需要做好一點(diǎn),保證采集Agent對(duì)該日志文件的讀取句柄是正常打開(kāi)的,這樣的話(huà),即使日志清理進(jìn)程對(duì)該文件執(zhí)行了rm操作(執(zhí)行rm后只是將該文件從文件系統(tǒng)的目錄結(jié)構(gòu)上解除鏈接 unlink,實(shí)際文件還未從磁盤(pán)徹底刪除),采集Agent持續(xù)打開(kāi)的句柄,依然能正常采集完此文件;這種"采集速度跟不上刪除速度"是不能長(zhǎng)時(shí)間存在,也有磁盤(pán)滿(mǎn)的風(fēng)險(xiǎn),需要通過(guò)告警識(shí)別出來(lái),根本上來(lái)說(shuō),需要通過(guò)負(fù)載均衡或者降低日志量的方法,來(lái)減少單機(jī)器日志長(zhǎng)時(shí)間采集不過(guò)來(lái)的情況。
4.8 系統(tǒng)資源消耗與控制
Agent采集進(jìn)程是隨著業(yè)務(wù)進(jìn)程一起部署在一個(gè)機(jī)器上的,共同使用業(yè)務(wù)機(jī)器的資源(CPU、內(nèi)存、磁盤(pán)、網(wǎng)絡(luò)),所以在設(shè)計(jì)時(shí),要考慮控制好Agent采集進(jìn)程對(duì)機(jī)器資源的消耗,同時(shí)要做好對(duì)Agent進(jìn)程對(duì)機(jī)器資源消耗的監(jiān)控。一方面保障業(yè)務(wù)有穩(wěn)定的資源可以正常運(yùn)行;另外可以保障Agent自身進(jìn)程正常運(yùn)作。通常我們可以采用以下方案:
1. 針對(duì)CPU的消耗控制。
我們可以較方便采用Linux系統(tǒng)層面的CPU隔離的方案來(lái)控制,比如TaskSet;通過(guò)TaskSet命令,我們可以在采集進(jìn)程啟動(dòng)時(shí),設(shè)定采集進(jìn)程綁定在某個(gè)限定的CPU核心上面(進(jìn)程綁核,即設(shè)定進(jìn)程與CPU親和性,設(shè)定以后Linux調(diào)度器就會(huì)讓這個(gè)進(jìn)程/線(xiàn)程只在所綁定的核上面去運(yùn)行);這樣的設(shè)定之后,可以保障采集進(jìn)程與業(yè)務(wù)進(jìn)程在CPU的使用上面互相不影響。
2. 針對(duì)內(nèi)存的消耗控制。
由于采集Agent采用java語(yǔ)言開(kāi)發(fā)基于JVM運(yùn)行,所以我們可以通過(guò)JVM的堆參數(shù)配置即可控制;bees-agent一般默認(rèn)配置512MB,理論上最低值可以是64MB,可以根據(jù)實(shí)際機(jī)器資源情況和采集日志文件大小來(lái)配置;事實(shí)上,Agent的內(nèi)存占用相對(duì)穩(wěn)定,內(nèi)存消耗方面的風(fēng)險(xiǎn)較小。
3.針對(duì)磁盤(pán)的消耗控制。
由于采集Agent是一個(gè)IO密集型進(jìn)程,所以磁盤(pán)IO的負(fù)載是我們需要重點(diǎn)保障好的;在系統(tǒng)層面沒(méi)有成熟的磁盤(pán)IO的隔離方案,所以只能在應(yīng)用層來(lái)實(shí)現(xiàn)。我們需要清楚進(jìn)程所在磁盤(pán)的基準(zhǔn)性能情況,然后在這個(gè)基礎(chǔ)上,通過(guò)Agent自身的限速采集能力,設(shè)置采集進(jìn)程的峰值的采集速率(比如:3MB/s、5MB/s);除此之外,還需要做好磁盤(pán)IO負(fù)載的基礎(chǔ)監(jiān)控與告警、采集Agent采集速率大小的監(jiān)控與告警,通過(guò)這些監(jiān)控告警與值班分析進(jìn)一步保障磁盤(pán)IO資源。
4.針對(duì)網(wǎng)絡(luò)的消耗控制。
這里說(shuō)的網(wǎng)絡(luò),重點(diǎn)要關(guān)注是跨機(jī)房帶寬上限。避免同一時(shí)刻,大批量的Agent日志采集導(dǎo)致跨機(jī)房的帶寬到達(dá)了上限,引發(fā)業(yè)務(wù)故障。所以,針對(duì)網(wǎng)絡(luò)帶寬的使用也需要有監(jiān)控與告警,相關(guān)監(jiān)控?cái)?shù)據(jù)上報(bào)到平臺(tái)匯總計(jì)算,平臺(tái)通過(guò)智能計(jì)算后給Agent下發(fā)一個(gè)合理的采集速率。
4.9 自身日志監(jiān)控
為了更好的監(jiān)控線(xiàn)上所有的Agent的情況,能夠方便地查看這些Agent進(jìn)程自身的log4j日志是很有必要的。為了達(dá)成這一目的,我們把Agent自身產(chǎn)生的日志采集設(shè)計(jì)成一個(gè)普通的日志采集任務(wù),就是說(shuō),采集Agent進(jìn)程自身,自己采集自己產(chǎn)生的日志,于是就可以把所有Agent的日志通過(guò)Agent采集匯聚到下游Kafka,再到Elasticsearch存儲(chǔ)引擎,最后通過(guò)Kibana或其他的日志可視化平臺(tái)可以查看。
4.10 平臺(tái)化管理
目前的生產(chǎn)環(huán)境Agent實(shí)例數(shù)量已經(jīng)好幾萬(wàn),采集任務(wù)數(shù)量有上萬(wàn)個(gè)。為了對(duì)這些分散的、數(shù)據(jù)量多的Agent進(jìn)行有效的集中的運(yùn)維和管理,我們?cè)O(shè)計(jì)了一個(gè)可視化的平臺(tái),管理平臺(tái)具備以下Agent控制能力:Agent 的現(xiàn)網(wǎng)版本查看,Agent存活心跳管理,Agent采集任務(wù)下發(fā)、啟動(dòng)、停止管理,Agent采集限速管理等;需要注意的是,Agent與平臺(tái)的通訊方式,我們?cè)O(shè)計(jì)采用簡(jiǎn)單的HTTP通訊方式,即Agent以定時(shí)心跳的方式(默認(rèn)5分鐘)向平臺(tái)發(fā)起HTTP請(qǐng)求,HTTP請(qǐng)求體中會(huì)包含Agent自身信息,比如idc、ip、hostname、當(dāng)前采集任務(wù)信息等,而HTTP返回體的內(nèi)容里會(huì)包含平臺(tái)向Agent下發(fā)的任務(wù)信息,比如哪個(gè)任務(wù)啟動(dòng)、哪個(gè)任務(wù)停止、任務(wù)的具體參數(shù)變更等。
五、與開(kāi)源能力對(duì)比
bees-agent與flume-agent對(duì)比
-
內(nèi)存需求大大降低。bees-agent 采用無(wú) Channel 設(shè)計(jì),大大節(jié)省內(nèi)存開(kāi)銷(xiāo),每個(gè) Agent 啟動(dòng) ,JVM 堆棧最低理論值可以設(shè)置為64MB;
-
實(shí)時(shí)性更好。bees-agent 采用Linux inotify事件機(jī)制,相比 Flume Agent 輪詢(xún)機(jī)制,采集數(shù)據(jù)的時(shí)效性可以在1s以?xún)?nèi);
-
日志文件的唯一標(biāo)識(shí),bees-agent 使用inode+文件簽名,更準(zhǔn)確,不會(huì)出現(xiàn)日志文件誤采重采;
-
用戶(hù)資源隔離。bees-agent 不同 Topic 的日志采集任務(wù),采用不同的線(xiàn)程隔離采集,互相無(wú)影響;
-
真正的優(yōu)雅退出。bees-agent 在正常采集過(guò)程中,隨時(shí)使用平臺(tái)的"停止命令"讓 Agent 優(yōu)雅退出,不會(huì)出現(xiàn)無(wú)法退出的尷尬情況,也能保證日志無(wú)任何丟失;
-
更豐富的指標(biāo)數(shù)據(jù)。bees-agent 包括采集速率、采集總進(jìn)度,還有 機(jī)器信息、JVM 堆情況、類(lèi)數(shù)量、JVM GC次數(shù)等;
-
更豐富的定制化能力。bees-agent 具備關(guān)鍵字匹配采集能力、日志格式化能力、平臺(tái)化管理的能力等;
六、總結(jié)
前文介紹了vivo日志采集Agent在設(shè)計(jì)過(guò)程中的一些核心技術(shù)點(diǎn):包括日志文件的發(fā)現(xiàn)與監(jiān)聽(tīng)、日志文件的唯一標(biāo)識(shí)符設(shè)計(jì)、日志文件的實(shí)時(shí)采集與離線(xiàn)采集的架構(gòu)設(shè)計(jì)、日志文件的清理策略、采集進(jìn)程對(duì)系統(tǒng)資源的消耗控制、平臺(tái)化管理的思路等,這些關(guān)鍵的設(shè)計(jì)思路覆蓋了自研采集agent大部分的核心功能,同時(shí)也覆蓋了其中的難點(diǎn)痛點(diǎn),能讓后續(xù)的開(kāi)發(fā)環(huán)節(jié)更加暢通。當(dāng)然,還有一些高階的采集能力未涵蓋本文介紹在內(nèi),比如"如何做好日志采集數(shù)據(jù)的完整性對(duì)賬","數(shù)據(jù)庫(kù)類(lèi)型的場(chǎng)景的采集設(shè)計(jì)"等,大家可以繼續(xù)探索解決方案。
從2019年起,vivo大數(shù)據(jù)業(yè)務(wù)的日志采集場(chǎng)景就是由Bees數(shù)據(jù)采集服務(wù)支撐。bees-agent在生產(chǎn)環(huán)境持續(xù)服務(wù),至今已有3年多的穩(wěn)定運(yùn)行的記錄,有數(shù)萬(wàn)個(gè)bees-agent實(shí)例正在運(yùn)行,同時(shí)在線(xiàn)支撐數(shù)萬(wàn)個(gè)日志文件的采集,每天采集PB級(jí)別的日志量。實(shí)踐證明,bees-agent的穩(wěn)定行、健壯性、豐富的功能、性能與合理的資源情況,都符合最開(kāi)始設(shè)計(jì)的預(yù)期,本文的設(shè)計(jì)思路的也一再被證實(shí)行之有效。
審核編輯 :李倩
-
Agent
+關(guān)注
關(guān)注
0文章
105瀏覽量
26761 -
日志
+關(guān)注
關(guān)注
0文章
138瀏覽量
10656 -
大數(shù)據(jù)
+關(guān)注
關(guān)注
64文章
8896瀏覽量
137516
原文標(biāo)題:vivo大數(shù)據(jù)日志采集Agent設(shè)計(jì)實(shí)踐
文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論