概述
本文介紹如何利用騰訊雲容器服務 TKE 的日志功能對日志進行采集、存儲與查詢,分析各種功能用法與場景,給出一些最佳實踐建議。
注: 本文僅適用於 TKE 集群。
如何快速上手 ?
TKE 的日志功能入口在 集群運維-日志規則
,更多關於如何為 TKE 集群啟用日志采集與基礎用法,參考官方文檔 日志采集。
技術架構是怎樣的 ?
TKE 集群開啟日志采集后,tke-log-agent 作為 DaemonSet 部署在每個節點上,負責根據采集規則采集節點上容器的日志,然后上報到 CLS 日志服務,由 CLS 進行統一存儲、檢索與分析:
采集哪里的日志 ?
在 TKE 使用日志采集時,需要在 集群運維-日志規則
里新建日志采集規則,首先需要確定采集的目標數據源是什么,下面介紹支持的 3 種類型數據源及其各自使用場景與建議。
采集標准輸出
最簡單也是最推薦的方式是將 Pod 內容器的日志輸出到標准輸出,日志內容就會由容器運行時 (docker, containerd) 來管理,有以下幾點好處:
- 不需要額外掛載 volume。
- 可以直接通過
kubectl logs
查看日志內容。 - 業務不需要關心日志輪轉,容器運行時會對日志進行存儲和自動輪轉,避免因個別 Pod 日志量大將磁盤寫滿。
- 不需要關心日志文件路徑,可以使用比較統一的采集規則,用更少的采集規則數量覆蓋更多的工作負載,減少運維復雜度。
采集配置示例:
采集容器內的文件
很多時候業務通過寫日志文件的方式來記錄日志,使用容器跑業務時,日志文件被寫到容器內:
- 如果日志文件所在路徑沒有掛載 volume,日志文件會被寫入容器可寫層,落盤到容器數據盤里,通常路徑是
/var/lib/docker
(建議給此路徑掛盤,避免與系統盤混用),容器停止后日志會被清理。 - 如果日志文件所在路徑掛載了 volume,日志文件會落盤到對應 volume 類型的后端存儲;通常用 emptydir,容器停止后日志會被清理,運行期間日志文件會落盤到宿主機的
/var/lib/kubelet
路徑下,此路徑通常沒有單獨掛盤,也就是會使用系統盤;由於使用了日志采集,有統一存儲的能力,不推薦再掛載其它持久化存儲來存日志文件(如雲硬盤CBS, 對象存儲COS, 共享存儲CFS)。
許多開源日志采集器需要給 Pod 日志文件路徑掛載 volume 才能采集,使用 TKE 的日志采集則不需要,所以如果將日志輸出到容器內的文件里,不需要關心是否掛載 volume。
采集配置示例:
采集宿主機上的文件
如果業務將日志寫入日志文件,但又想容器停止之后還能保留原始日志文件,好有個備份,避免采集異常時導致日志完全丟失,這時可以給日志文件路徑掛載 hostPath,日志文件會落盤到宿主機指定目錄,並且容器停止后不會清理日志文件。
由於不會自動清理日志文件,有同學就可能會擔心日志會被重復采集,比如 Pod 調度走又調度回來,日志文件被寫在之前相同路徑。是否會重復采集,這里分兩種情況:
- 文件名相同,比如固定文件路徑
/data/log/nginx/access.log
。此時不會重復采集,因為采集器會記住之前采集過的日志文件的位點,只采集增量部分。 - 文件名不同,通常是業務用的日志框架會按照一定時間周期自動進行日志輪轉,一般是按天輪轉,自動為舊日志文件進行重命名,加上時間戳后綴。如果采集規則里使用了 "*" 作為通配符匹配日志文件名,可能就會重復采集,因為日志框架對日志文件重命名后,采集器就會認為匹配到了新寫入的日志文件,就又對其進行采集一次。
所以,一般不會重復采集,如果日志框架會對日志進行自動輪轉,建議采集規則不要使用通配符 "*" 來匹配日志文件。
采集配置示例:
日志吐到哪里 ?
知道了采集哪里的數據之后,我們還需要知道采集到的日志往哪里存。根據前面講的技術架構可以知道,TKE 日志采集與雲上的 CLS 日志服務集成,日志數據也將統一上報到日志服務。日志服務通過日志集和日志主題來對日志進行管理,日志集是 CLS 的項目管理單元,可以包含多個日志主題;一般將同一個業務的日志放在一個同一日志集,同一業務中的同一類的應用或服務使用相同日志主題,在 TKE 中,日志采集規則與日志主題是一一對應的;TKE 創建日志采集規則時選擇消費端,就需要指定日志集與日志主題,日志集通常提前創建好,日志主題通常選擇自動創建:
創建好后可以根據情況對自動創建的日志主題進行重命名,方便后續檢索時找到日志所在的日志主題:
如何配置日志格式解析 ?
有了日志的原始數據,我們還需要告訴日志服務如何去解析日志,以方便后續對其進行檢索。在創建日志采集規則時,需要配置日志的解析格式,下面針對各項配置給出分析與建議。
使用哪種抓取模式 ?
首先,我們需要確定日志的抓取模式,支持 5 種:單行文本、JSON、分隔符、多行文本和完全正則。
推薦使用 JSON,因為 JSON 格式本身就將日志給結構化了,日志服務可以提取 JSON 的 key 作為字段名,value 作為對應的字段值,不再需要根據業務日志輸出格式配置復雜的匹配規則,日志示例:
{"remote_ip":"10.135.46.111","time_local":"22/Jan/2019:19:19:34 +0800","body_sent":23,"responsetime":0.232,"upstreamtime":"0.232","upstreamhost":"unix:/tmp/php-cgi.sock","http_host":"127.0.0.1","method":"POST","url":"/event/dispatch","request":"POST /event/dispatch HTTP/1.1","xff":"-","referer":"http://127.0.0.1/my/course/4","agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0","response_code":"200"}
使用 JSON 抓取模式的前提是業務的日志本身是以 JSON 格式輸出的,如果不是 JSON 格式,但切換到使用 JSON 格式輸出成本不大,就建議進行切換,如果實在不好切換,再考慮其它抓取模式。
如果日志內容是以固定格式輸出的單行文本,考慮使用 "分隔符" 或 "完全正則" 抓取模式。"分隔符" 適用簡單格式,日志中每個字段值都以固定的字符串分隔開,比如用 ":::" 隔開,某一條日志內容是:
10.20.20.10 ::: [Tue Jan 22 14:49:45 CST 2019 +0800] ::: GET /online/sample HTTP/1.1 ::: 127.0.0.1 ::: 200 ::: 647 ::: 35 ::: http://127.0.0.1/
可以配置 ":::" 自定義分隔符,並且為每個字段按順序配置字段名,示例:
"完全正則" 適用復雜格式,使用正則表達式來匹配日志的格式。如日志內容為:
10.135.46.111 - - [22/Jan/2019:19:19:30 +0800] "GET /my/course/1 HTTP/1.1" 127.0.0.1 200 782 9703 "http://127.0.0.1/course/explore?filter%5Btype%5D=all&filter%5Bprice%5D=all&filter%5BcurrentLevelId%5D=all&orderBy=studentNum" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0" 0.354 0.354
正則表達式就可以設置為:
(\S+)[^\[]+(\[[^:]+:\d+:\d+:\d+\s\S+)\s"(\w+)\s(\S+)\s([^"]+)"\s(\S+)\s(\d+)\s(\d+)\s(\d+)\s"([^"]+)"\s"([^"]+)"\s+(\S+)\s(\S+).*
日志服務會使用 ()
捕獲組來區分每個字段,我們還需要為每個字段設置字段名,配置示例:
如果日志沒有固定的輸出格式,則考慮使用 "單行文本" 或 "多行文本" 的抓取模式。使用這兩種模式,不會對日志內容本身進行結構化處理,不會提取日志字段,每條日志的時間戳也固定由日志采集的時間決定,檢索的時候也只能進行簡單的模糊查詢。這兩種模式的區別在於日志內容是單行還是多行,如果是單行最簡單,不需要設置任何匹配條件,每行都是一條單獨的日志;如果是多行則需要設置首行正則表達式,也就是匹配每條日志第一行的正則,當某行日志匹配上預先設置的首行正則表達式,就認為是一條日志的開頭,而下一個行首出現作為該條日志的結束標識符。假如多行日志內容是:
10.20.20.10 - - [Tue Jan 22 14:24:03 CST 2019 +0800] GET /online/sample HTTP/1.1 127.0.0.1 200 628 35 http://127.0.0.1/group/1
Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0 0.310 0.310
那么首行正則表達式就可以設置為: \d+\.\d+\.\d+\.\d+\s-\s.*
如何過濾掉不需要的內容 ?
有些不重要或不關心的日志可以選擇將其過濾掉,降低成本。
如果使用 "JSON"、"分隔符" 或 "完全正則" 的抓取模式,日志內容會進行結構化處理,可以通過指定字段來對要保留的日志進行正則匹配:
對於 "單行文本" 和 "多行文本" 抓取模式,由於日志內容沒有進行結構化處理,無法指定字段來過濾,通常直接使用正則來對要保留的完整日志內容進行模糊匹配:
需要注意的是,匹配內容一定記住是用正則而不是完整匹配,比如想只保留 a.test.com
域名的日志,匹配的表達式應該寫 a\.test\.com
而不是 a.test.com
。
日志時間戳如何自定義 ?
每條日志都需要有個時間戳,這個時間戳主要用於檢索,在檢索的時候可以選擇時間范圍。默認情況下,日志的時間戳由采集的時間決定,也可以進行自定義,選擇某個字段作為時間戳,這樣在某些情況下可能更精確些,比如在創建采集規則之前,服務已經運行了一段時間,如果不設置自定義時間格式,采集時會將之前的舊日志的時間戳設置為當前的時間,導致時間不准確。
如何進行自定義呢?由於 "單行文本" 和 "多行文本" 抓取模式不會對日志內容進行結構化處理,也就沒有字段可以指定為時間戳,無法自定義時間格式解析。其它的抓取模式都可以支持,具體做法時關閉 "使用采集時間",然后選取要作為時間戳的字段名稱,並配置時間格式。
假如使用日志的 time
字段作為時間戳,其中一條日志 time
的值為 2020-09-22 18:18:18
,時間格式就可以設置為 %Y-%m-%d %H:%M:%S
, 示例:
更多時間格式配置參考日志服務官方文檔 配置時間格式。
需要注意的是,日志服務時間戳暫時只支持精確到秒,也就是如果業務日志的時間戳字段精確到了毫秒,將無法使用自定義時間戳,只能使用默認的采集時間作為時間戳,不過時間戳精確到毫秒后續將會得到支持。
如何查詢日志 ?
日志采集規則配好了,采集器就會自動開始采集日志並上報到日志服務,然后就可以在 日志服務-檢索分析
中查詢日志了,支持 Lucene 語法,但前提是需要開啟索引,有以下 3 類索引:
- 全文索引。用於模糊搜索,不用指定字段。
- 鍵值索引。索引結構化處理過的日志內容,可以指定日志字段進行檢索。
- 元字段索引。上報日志時額外自動附加的一些字段,比如 pod 名稱、namespace 等,方便檢索時指定這些字段進行檢索。
查詢示例:
如何將日志投遞到其它地方 ?
日志服務支持將日志投遞到 COS 對象存儲和 Ckafka (騰訊雲托管的 Kafka),可以在日志主題里設置投遞:
可以用在以下場景:
- 對日志數據進行長期歸檔存儲。日志集默認存儲 7 天的日志數據,可以調整時長,但數據量越大,成本就越高,通常只保留幾天的數據,如果需要將日志存更長時間,可以投遞到 COS 進行低成本存儲。
- 需要對日志進行進一步處理 (如離線計算),可以投遞到 COS 或 Ckafka,由其它程序消費來處理。
參考資料
- TKE 日志采集用法指引: https://cloud.tencent.com/document/product/457/36771
- 日志服務配置時間格式: https://cloud.tencent.com/document/product/614/38614
- 日志服務投遞 COS: https://cloud.tencent.com/document/product/614/37908
- 日志服務投遞 Ckafka: https://cloud.tencent.com/document/product/614/33342
【騰訊雲原生】雲說新品、雲研新術、雲游新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多干貨!!