面向容器日誌的技術實踐

背景

自 2013 年 dotCloud 公司開源 Docker 以來,以 Docker 爲表明的容器產品憑藉着隔離性好、可移植性高、資源佔用少、啓動迅速等特性迅速風靡世界。下圖展現了 2013 年以來 Docker 和 OpenStack 的搜索趨勢。php

 

容器技術在部署、交付等環節給人們帶來了不少便捷,但在日誌處理領域卻帶來了許多新的挑戰,包括:css

  1. 若是把日誌保存在容器內部,它會隨着容器的銷燬而被刪除。因爲容器的生命週期相對虛擬機大大縮短,建立銷燬屬於常態,所以須要一種方式持久化的保存日誌;
  2. 進入容器時代後,須要管理的目標對象遠多於虛擬機或物理機,登陸到目標容器排查問題會變得更加複雜且不經濟;
  3. 容器的出現讓微服務更容易落地,它在給咱們的系統帶來鬆耦合的同時引入了更多的組件。所以咱們須要一種技術,它既能幫助咱們全局性的瞭解系統運行狀況,又能迅速定位問題現場、還原上下文。

日誌處理流程

本文以 Docker 爲例,依託阿里雲日誌服務團隊在日誌領域深耕多年積累下的豐富經驗,介紹容器日誌處理的通常方法和最佳實踐,包括:html

  1. 容器日誌實時採集;
  2. 查詢分析和可視化;
  3. 日誌上下文分析;
  4. LiveTail - 雲上 tail -f。

容器日誌實時採集

容器日誌分類

採集日誌首先要弄清日誌存在的位置,這裏以 Nginx、Tomcat 這兩個經常使用容器爲例進行分析。linux

Nginx 產生的日誌包括 access.log 和 error.log,根據 nginx Dockerfile 可知 access.log 和 error.log 被分別重定向到了 STDOUT 和 STDERR 上。nginx

Tomcat 產生的日誌比較多,包括 catalina.log、access.log、manager.log、host-manager.log 等,tomcat Dockerfile 並無將這些日誌重定向到標準輸出,它們存在於容器內部。git

容器產生的日誌大部分均可以歸結於上述情形。這裏,咱們不妨將容器日誌分紅如下兩類。github

容器日誌分類 定義
標準輸出 經過 STDOUT、STDERR 輸出的信息,包括被重定向到標準輸出的文本文件。
文本日誌 存在於容器內部而且沒有被重定向到標準輸出的日誌。

標準輸出

使用 logging driver

容器的標準輸出會由 logging driver 統一處理。以下圖所示,不一樣的 logging driver 會將標準輸出寫往不一樣的目的地。docker

 

經過 logging driver 採集容器標準輸出的優點在於使用簡單,例如:json

# 該命令表示在 docker daemon 級別爲全部容器配置 syslog 日誌驅動
dockerd -–log-driver syslog –-log-opt syslog-address=udp://1.2.3.4:1111 # 該命令表示爲當前容器配置 syslog 日誌驅動 docker run -–log-driver syslog –-log-opt syslog-address=udp://1.2.3.4:1111 alpine echo hello world
缺點

除了 json-file 和 journald,使用其餘 logging driver 將使 docker logs API 不可用。tomcat

例如,當您使用 portainer 管理宿主機上的容器,而且使用了上述二者以外的 logging driver,您會發現沒法經過 UI 界面觀察到容器的標準輸出。

使用 docker logs API

對於那些使用默認 logging driver 的容器,咱們能夠經過向 docker daemon 發送 docker logs 命令來獲取容器的標準輸出。使用此方式採集日誌的工具包括 logspoutsematext-agent-docker 等。下列樣例中的命令表示獲取容器自2018-01-01T15:00:00以來最新的5條日誌。

docker logs --since "2018-01-01T15:00:00" --tail 5 <container-id>
缺點

當日志量較大時,這種方式會對 docker daemon 形成較大壓力,致使 docker daemon 沒法及時響應建立容器、銷燬容器等命令。

採集 json-file 文件

默認 logging driver 會將日誌以 json 的格式寫入宿主機文件裏,文件路徑爲/var/lib/docker/containers/<container-id>/<container-id>-json.log。這樣能夠經過直接採集宿主機文件來達到採集容器標準輸出的目的。

該方案較爲推薦,由於它既不會使 docker logs API 變得不可用,又不會影響 docker daemon,而且如今許多工具原生支持採集宿主機文件,如 filebeatlogtail 等。

文本日誌

掛載宿主機目錄

採集容器內文本日誌最簡單的方法是在啓動容器時經過 bind mounts 或 volumes 方式將宿主機目錄掛載到容器日誌所在目錄上,以下圖所示。

 

針對 tomcat 容器的 access log,使用命令docker run -it -v /tmp/app/vol1:/usr/local/tomcat/logs tomcat將宿主機目錄/tmp/app/vol1掛載到 access log 在容器中的目錄/usr/local/tomcat/logs上,經過採集宿主機目錄/tmp/app/vol1下日誌達到採集 tomcat access log 的目的。

計算容器 rootfs 掛載點

使用掛載宿主機目錄的方式採集日誌對應用會有必定的侵入性,由於它要求容器啓動的時候包含掛載命令。若是採集過程能對用戶透明那就太棒了。事實上,能夠經過計算容器 rootfs 掛載點來達到這種目的。

和容器 rootfs 掛載點密不可分的一個概念是 storage driver。實際使用過程當中,用戶每每會根據 linux 版本、文件系統類型、容器讀寫狀況等因素選擇合適的 storage driver。不一樣 storage driver 下,容器的 rootfs 掛載點遵循必定規律,所以咱們能夠根據 storage driver 的類型推斷出容器的 rootfs 掛載點,進而採集容器內部日誌。下表展現了部分 storage dirver 的 rootfs 掛載點及其計算方法。

Storage driver rootfs 掛載點 計算方法
aufs /var/lib/docker/aufs/mnt/<id> id 能夠從以下文件讀到。
/var/lib/docker/image/aufs/layerdb/mounts/\<container-id\>/mount-id
overlay /var/lib/docker/overlay/<id>/merged 完整路徑能夠經過以下命令獲得。
docker inspect -f '{{.GraphDriver.Data.MergedDir}}' <container-id>
overlay2 /var/lib/docker/overlay2/<id>/merged 完整路徑能夠經過以下命令獲得。
docker inspect -f '{{.GraphDriver.Data.MergedDir}}' <container-id>
devicemapper /var/lib/docker/devicemapper/mnt/<id>/rootfs id 能夠經過以下命令獲得。
docker inspect -f '{{.GraphDriver.Data.DeviceName}}' <container-id>

Logtail 方案

在充分比較了容器日誌的各類採集方法,綜合整理了廣大用戶的反饋與訴求後,日誌服務團隊推出了容器日誌一站式解決方案。

 

功能特色

logtail 方案包含以下功能:

  1. 支持採集宿主機文件以及宿主機上容器的日誌(包括標準輸出和日誌文件);
  2. 支持容器自動發現,即當您配置了採集目標後,每當有符合條件的容器被建立時,該容器上的目標日誌將被自動採集;
  3. 支持經過 docker label 以及環境變量過濾指定容器,支持白名單、黑名單機制;
  4. 採集數據自動打標,即對收集上來的日誌自動加上 container name、container IP、文件路徑等用於標識數據源的信息;
  5. 支持採集 K8s 容器日誌。

核心優點

  1. 經過 checkpoint 機制以及部署額外的監控進程保證 at-least-once 語義;
  2. 歷經屢次雙11、雙十二的考驗以及阿里集團內部百萬級別的部署規模,穩定和性能方面很是有保障。

K8s 容器日誌採集

和 K8s 生態深度集成,能很是方便地採集 K8s 容器日誌是日誌服務 logtail 方案的又一大特點。

採集配置管理:

  1. 支持經過 WEB 控制檯進行採集配置管理;
  2. 支持經過 CRD(CustomResourceDefinition)方式進行採集配置管理(該方式更容易與 K8s 的部署、發佈流程進行集成)。

採集模式:

  1. 支持經過 DaemonSet 模式採集 K8s 容器日誌,即每一個節點上運行一個採集客戶端 logtail,適用於功能單一型的集羣;
  2. 支持經過 Sidecar 模式採集 K8s 容器日誌,即每一個 Pod 裏以容器的形式運行一個採集客戶端 logtail,適用於大型、混合型、PAAS 型集羣。

關於 Logtail 方案的詳細說明可參考文章全面提高,阿里雲Docker/Kubernetes(K8S) 日誌解決方案與選型對比

查詢分析和可視化

完成日誌採集工做後,下一步須要對這些日誌進行查詢分析和可視化。這裏以 Tomcat 訪問日誌爲例,介紹日誌服務提供的強大的查詢、分析、可視化功能。

快速查詢

容器日誌被採集時會帶上 container name、container IP、目標文件路徑等信息,所以在查詢的時候能夠經過這些信息快速定位目標容器和文件。查詢功能的詳細介紹可參考文檔查詢語法

實時分析

日誌服務實時分析功能兼容 SQL 語法且提供了 200 多種聚合函數。若是您有使用 SQL 的經驗,可以很容易寫出知足業務需求的分析語句。例如:

  1. 統計訪問次數排名前 10 的 uri。
* | SELECT request_uri, COUNT(*) as c GROUP by request_uri ORDER by c DESC LIMIT 10
  1. 統計當前15分鐘的網絡流量相對於前一個小時的變化狀況。
* | SELECT diff[1] AS c1, diff[2] AS c2, round(diff[1] * 100.0 / diff[2] - 100.0, 2) AS c3 FROM (select compare( flow, 3600) AS diff from (select sum(body_bytes_sent) as flow from log))

該語句使用同比環比函數計算不一樣時間段的網絡流量。

可視化

爲了讓數據更加生動,您可使用日誌服務內置的多種圖表對 SQL 計算結果進行可視化展現,並將圖表組合成一個儀表盤

 

下圖展現了基於 Tomcat 訪問日誌的儀表盤,它展現了錯誤請求率、網絡流量、狀態碼隨時間的變化趨勢等信息。該儀表盤展示的是多個 Tomcat 容器數據聚合後的結果,您可使用儀表盤過濾器功能,經過指定容器名查看單個容器的數據。

 

日誌上下文分析

查詢分析、儀表盤等功能能幫助咱們把握全局信息、瞭解系統總體運行狀況,但定位具體問題每每須要上下文信息的幫助。

上下文定義

上下文指的是圍繞某個問題展開的線索,如日誌中某個錯誤的先後信息。上下文包含兩個要素:

  • 最小區分粒度:區分上下文的最小空間劃分,例如同一個線程、同一個文件等。這一點在定位問題階段很是關鍵,由於它可以使得咱們在調查過程當中避免不少干擾。
  • 保序:在最小區分粒度的前提下,信息的呈現必須是嚴格有序的,即便一秒內有幾萬次操做。

下表展現了不一樣數據源的最小區分粒度。

分類 最小區分粒度
單機文件 IP + 文件
Docker 標準輸出 Container + STDOUT/STDERR
Docker 文件 Container + 文件
K8s 容器標準輸出 Namespace + Pod + Container + STDOUT/STDERR
K8s 容器文件 Namespace + Pod + Container + 文件
SDK 線程
Log Appender 線程

上下文查詢面臨的挑戰

在日誌集中式存儲的背景下,採集端和服務端都很難保證日誌原始的順序:

  1. 在客戶端層面,一臺宿主機上運行着多個容器,每一個容器會有多個目標文件須要採集。日誌採集軟件須要利用機器的多個 cpu 核心解析、預處理日誌,並經過多線程併發或者單線程異步回調的方式處理網絡發送的慢 IO 問題。這使得日誌數據不能按照機器上的事件產生順序依次到達服務端。
  2. 在服務端層面,因爲水平擴展的多機負載均衡架構,使得同一客戶端機器的日誌會分散在多臺存儲節點上。在分散存儲的日誌基礎上再恢復最初的順序是困難的。

原理

日誌服務經過給每條日誌附加一些額外的信息以及服務端的關鍵詞查詢能力巧妙地解決了上述難題。原理以下圖所示。

 

  1. 日誌被採集時會自動加入用於標識日誌來源的信息(即上文提到的最小區分粒度)做爲 source_id。針對容器場景,這些信息包括容器名、文件路徑等;
  2. 日誌服務的各類採集客戶端通常會選擇批量上傳日誌,若干條日誌組成一個數據包。客戶端會向這些數據包裏寫入一個單調遞增的 package_id,而且包內每條日誌都擁有包內位移 offset;
  3. 服務端會將 source_id、package_id、offset 組合起來做爲一個字段併爲其創建索引。這樣,即便各類日誌在服務端是混合存儲的狀態,咱們也能夠根據 source_id、package_id、offset 精肯定位某條日誌。

想了解更多有關上下文分析的功能可參考文章上下文查詢分佈式系統日誌上下文查詢功能

LiveTail - 雲上 tail -f

除了查看日誌的上下文信息,有時咱們也但願可以持續觀察容器的輸出。

傳統方式

下表展現了傳統模式下實時監控容器日誌的方法。

類別 步驟
標準輸出 1. 定位容器,獲取容器 id;
2. 使用命令docker logs –f <container id>kubectl logs –f <pod name>在終端上觀察輸出;
3. 使用grepgrep –v過濾關鍵信息。
文本日誌 1. 定位容器,獲取容器 id;
2. 使用命令docker execkubectl exec進入容器;
3. 找到目標文件,使用命令tail –f觀察輸出;
4. 使用grepgrep –v過濾關鍵信息。

痛點

經過傳統方法監控容器日誌存在如下痛點:

  1. 容器不少時,定位目標容器耗時耗力;
  2. 不一樣類型的容器日誌須要使用不一樣的觀察方法,增長使用成本;
  3. 關鍵信息查詢展現不夠簡單直觀。

功能和原理

針對這些問題,日誌服務推出了 LiveTail 功能。相比傳統模式,它有以下優勢:

  1. 能夠根據單條日誌或日誌服務的查詢分析功能快速定位目標容器;
  2. 使用統一的方式觀察不一樣類型的容器日誌,無需進入目標容器;
  3. 支持經過關鍵詞進行過濾;
  4. 支持設置關鍵列。

 

在實現上,LiveTail 主要用到了上一章中提到的上下文查詢原理快速定位目標容器和目標文件。而後,客戶端按期向服務端發送請求,拉取最新數據。

視頻樣例

您還能夠經過觀看視頻,進一步理解容器日誌的採集、查詢、分析和可視化等功能。


原文連接本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索