Docker 日誌最佳實踐

當運行在 docker 容器中的應用程序打印日誌時,日誌會輸出到標準輸出流 stdout 和標準錯誤流 stderr。容器日誌驅動可以訪問這些流,並將日誌發送到文件、本機運行的日誌收集器或遠端的日誌服務端點(endpoint)。本文將介紹選擇不同的日誌驅動及配置對容器應用的性能和穩定性帶來的影響,以及推薦最能滿足日誌記錄需求和性能的日誌驅動及配置。

選擇日誌驅動

Docker 提供了若干內置的日誌驅動,不同的日誌驅動會把日誌分發到不同的地方,每個容器只能只能指定一種日誌驅動。

Docker 使用 json-file 作爲默認日誌驅動,該驅動把日誌以 json 的格式寫入宿主機指定文件:

/var/lib/docker/containers/{CONTAINER_ID}-json.log

下面是由 hello world 鏡像啓動的容器創建的 json 日誌實例:

{"log":"Hello from Docker!\n","stream":"stdout","time":"2022-05-29T22:51:31.549390877Z"}
{"log":"This message shows that your installation appears to be working correctly.\n","stream":"stdout","time":"2022-05-29T22:51:31.549396749Z"}

也可以通過命令行直接查看日誌:

docker logs <CONTAINER_ID> or docker logs <CONTAINER_NAME>
Hello from Docker!
This message shows that your installation appears to be working correctly.

其中,docker 提供了以下內置日誌驅動:

禁用日誌,且 docker logs 指令不返回任何內容。

保存日誌內容到本地文件並對內容進行壓縮。

以 json 格式保存日誌內容到本地文件,默認日誌驅動。

保存日誌內容到 syslog,syslog daemon 必須運行在宿主機上。

保存日誌內容到 journald,journald daemon 必須運行在宿主機上。

保存日誌內容到 GELF - Graylog Extended Log Format 端點,如 Graylog 和 Logstash。

保存日誌內容到 fluentd,fluentd daemon 必須運行在宿主機上。

保存日誌內容到 Amazon CloudWatch。

保存日誌內容到 splunk。

保存日誌內容到 ETW - Event Tracing for Windows,只能在 Windows 平臺上使用。

保存日誌內容到 GCP - Google Cloud Platform。

保存日誌內容到 Rapid7 Logentries。

配置日誌驅動

Docker 容器默認使用 json-file 日誌驅動,大部分情況下,建議直接使用該驅動。如果你的應用場景確實需要使用其它驅動,可以在 docker run 指令中使用 --log-driver 選項來覆蓋默認驅動,以下是使用 fluntd 作爲日誌驅動的示例:

docker run --log-driver fluentd httpd:latest

也可以通過修改 daemon.json 來配置日誌驅動,以下是使用 fluntd 作爲日誌驅動的示例:

{
  "log-driver": "fluentd",
  "log-opts": {
    "fluentd-address": "fluentd-ip:fluentd-port"
  }
}

重啓 docker 使上述配置生效,後續創建的容器都將使用 fluentd 作爲日誌驅動,並把日誌發送到指定的 fluentd address。

日誌分發模式

無論選擇了什麼日誌驅動,都可以指定日誌的分發模式:阻塞或非阻塞。

阻塞模式

阻塞模式是日誌分發的默認模式,在該模式下,日誌每次發送時會阻塞應用程序,以保證日誌能正常到達指定位置,這會增加應用程序的處理延遲,降低應用程序的性能。如果日誌驅動一直處於繁忙,容器會延遲應用程序的其他任務,直到日誌傳輸完畢。

阻塞模式對應用程序性能的潛在影響取決於選擇的日誌驅動。例如,json-file 日誌驅動日誌寫入速度非常快,並且寫入的是本地文件,所以不太可能出現阻塞並導致延遲。相反的,gcplogs 和 awslogs 這些需要打開遠程連接的日誌驅動,很可能會出現長時間的阻塞,並對應用程序帶來明顯的延遲。

非阻塞模式

在非阻塞模式下,容器首先把日誌寫入內存環形緩衝區,直到日誌驅動可以處理它們爲止。即使日誌驅動很忙,容器也可以立即將日誌輸出到緩衝區,並繼續執行應用程序。這確保了大量日誌活動不會影響容器中運行的應用程序的性能。

與阻塞模式相比,非阻塞模式不能保證日誌能正常到達指定位置。如果應用程序產生日誌的速度快於日誌驅動處理日誌的速度,環形緩衝區的內存將會耗盡。如果發生這種情況,緩衝區較早的 日誌將被刪除,然後才能傳遞給日誌驅動,造成日誌丟失。可以通過 max-buffer-size 選項來設置環形緩衝區的大小,max-buffer-size 的默認值爲 1MB。如果宿主機有足夠內存的話,

配置分發模式

阻塞模式是日誌分發的默認模式,可以在創建容器時使用 --log-opt 選項來設置非阻塞模式:

docker run --log-opt mode=non-blocking httpd:latest

也可以通過修改 daemon.json 來配置分發模式:

{
  "log-driver": "fluentd",
  "log-opts": {
    "fluentd-address": "fluentd-ip:fluentd-port",
    "mode": "non-blocking"
  }
}

選擇適合的日誌驅動及分發模式

大部分場景下,建議使用阻塞模式 + json-file,日誌寫入本地文件速度非常快,在阻塞模式下能夠捕獲所有日誌,但不會降低應用程序性能。

可以使用日誌收集 agent,如 fluentd/fluent-bit 來 tail 本地日誌文件,並將日誌轉發到日誌中心。json 格式的日誌易於解析,可以按屬性篩選日誌,識別日誌趨勢並作相應警報,以及對容器應用進行分析。

阻塞模式 + json-file 雖然能滿足大部分應用場景,但在某些情況下,可能需要考慮其它的日誌驅動及分發模式組合。

磁盤密集型應用

如果應用程序產生大量日誌並頻繁執行 I/O 操作,請考慮使用非阻塞模式 + json-file。該組合可保持應用程序的性能,同時仍可提供可靠的結構化日誌記錄。將日誌寫入本地存儲很快,環形緩衝區不太可能被填滿。如果不需要支持記錄峯值時的日誌(此時可能會填滿環形緩衝區),那麼非阻塞模式 + json-file 可以捕獲所有的日誌,而不會中斷應用程序。

不能使用本地日誌的內存密集型應用

如果應用程序本身很需要內存資源,並且日誌不能存放在本地,需要使用 gcplogs 或 awslogs 等驅動把日誌發送到遠端時,應考慮阻塞模式。因爲如果出現網絡阻塞時,已無足夠內存可分配給環形緩衝區。

雖然使用阻塞模式可以確保捕獲所有日誌,但通常不建議在阻塞模式下使用 json-file 以外的日誌驅動,因爲應用程序的性能可能會受到影響。

對於日誌無法存放到本地,但性能要求又高於日誌可靠性的應用程序,建議爲環形緩衝區提供足夠的內存,並使用非阻塞模式。這樣可以確保應用程序性能不會受到日誌記錄的影響,同時也爲捕獲主要日誌提供了足夠的空間。

參考總結

以上就是本文希望分享的內容,如果大家有什麼問題,歡迎在公衆號 - 跬步之巔留言交流。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/sLtciMl_HTvX0AZGQiWdzg