使用 Fluent Bit 將 Docker 日誌傳送至 S3 上儲存
我們公司內部有些服務是使用 Docker 容器運行的。前陣子我們想要把容器運行的日誌(Log)上傳到雲端中儲存,所以我開始尋找有哪些方式可以達到這個目的。幸好 Docker 本身就支援多種日誌驅動程式,所以要將日誌上傳到雲端上並不困難。我也藉這次的研究,好好的認識了 Fluent Bit 這個十分好用的日誌傳送工具,本篇文章就來簡單的說明一下我是如何使用 Fluent Bit 把 Docker 日誌傳送至 AWS S3 上儲存。
如何將 Docker 日誌發送到雲端?
Docker 預設的日誌驅動程式是 json-file。你可以使用下面的指令查看 Docker 目前預設的日誌驅動程式。
# 檢查預設的日誌驅動程式
docker info --format '{{.LoggingDriver}}'json-file 會把日誌儲存在主機的檔案系統上,如果是 Linux 系統的話,容器日誌會放在 /var/lib/docker/containers/<容器 ID>/ 底下。
除了 json-file,Docker 也支援多種日誌驅動程式,例如 awslogs、fluentd、journald 與 syslog …等。
awslogs驅動程式是將日誌送往 AWS CloudWatch。因為這個服務很貴,所以我們就不考慮了。🤣
在仔細看過每個驅動程式的用途後,我們最後選擇了 syslog 驅動程式,因為 syslog 支援 UDP 協定,所以就算傳送 Syslog 的服務出現問題,也不影響 Docker 容器的運行。
相反的,如果設定為使用 TCP 協定的
syslog,要是傳送 Syslog 的服務出現問題,Docker 容器是無法啟動的。
什麼是 Fluent Bit?
Fluent Bit 是 Fluentd 團隊為了較為受限的硬體環境,而從頭開發的一款輕量級且高效能的日誌處理器和轉發器。你可以使用 Fluent Bit 將服務的日誌進行解析與過濾,然後傳送到其他地方,是一個十分方便又好用的工具。
除了傳送日誌,Fluent Bit 也支援傳送指標(Metrics),你可以使用 Fluent Bit 將 CPU 與記憶體的指標傳送到 Prometheus。
Fluent Bit 的日誌傳送流程
Fluent Bit 傳送日誌的流程如下圖。

一個日誌在 Fluent Bit 中,會經過以下流程傳送到其它地方:
- 輸入(Input):Fluent Bit 提供多種不同的輸入外掛,讓你可以從多種不同的來源取得日誌資料。
- 解析(Parser):轉變資料格式。如果從輸入取得的是非結構化資料,你可以在這個步驟將其轉換成結構化的資料。
- 過濾(Filter):修改輸入的資料內容,或是捨棄不需要的資料內容,減少傳輸成本。
- 緩衝(Buffer):在資料被傳送出去之前,提供一個可以暫存資料的地方。
- 路由(Routing):根據輸入的標籤(Tag)與輸出的匹配(Match),Fluent Bit 會去判斷資料要從哪一個輸出傳送資料。
- 輸出(Output):Fluent Bit 提供多種不同的輸出外掛,讓你可以把資料發送至多種不同的目的地。
這一整個流程,被稱為資料管道(Data Pipeline)。
Fluent Bit 的緩衝與儲存機制
緩衝是 Fluent Bit 資料管道中至關重要的一環,透過此機制,處理後的資料會暫存於一個臨時位置,直到資料準備好進行傳送。暫存的地方預設是記憶體,而這些被暫存在記憶體中的資料,又被稱為區塊(Chunk)。
什麼是區塊(Chunk)?
當輸入外掛收到一條又一條的日誌後,會將多個日誌打包在一起變成「區塊」,區塊的大小預設為 2 MB。 Fluent Bit 的引擎會根據設定來決定要將區塊放在哪裡,預設是放在記憶體中。
因為區塊儲存在記憶體中,為了避免反壓(Backpressure)而導致記憶體使用率過高,你可以在 Fluent Bit 中限制記憶體的使用率。
什麼是反壓(Backpressure)?
有時候日誌或資料的生成速度,會快於將其轉移至目標位置的速度。
常見情境是 Fluent Bit 從大型日誌檔案讀取資料後(尤其是存在大量積壓資料的檔案),需要透過網路將日誌傳送至其它後端服務,因為網路回應需要時間。 如果回應時間不夠快,就有可能會產生反壓,導致大量資料囤積在記憶體,造成記憶體使用率過高。
為了避免反壓,Fluent Bit 提供了
mem_buf_limit與storage.max_chunks_up這兩個參數來限制輸入的資料量。對於某些輸入外掛,暫停處理資料可能會導致資料遺失。例如 TCP 輸入在暫停期間無法接收新的網路連線,可能會導致客戶端連線被拒絕或資料遺失;而 Tail 輸入則不會有這個問題,因為它是讀取檔案,可以先暫停處理,再從上次讀取的位置繼續處理資料。
Fluent Bit 的緩衝模式
Fluent Bit 提供兩種緩衝模式來暫存資料,分別是記憶體緩衝模式(in-memory)與記憶體搭配檔案系統緩衝模式(in-memory and filesystem),前者為預設值。
記憶體緩衝模式
如果沒有調整設定,所有的區塊都會放在記憶體中。
為了避免反壓導致記憶體使用率過高,你可以在輸入外掛上使用 mem_buf_limit 參數來限制記憶體的使用率,當觸發記憶體限制,輸入外掛就會被暫停(Pause),等資料被傳送出去,讓記憶體空間空出來後,輸入外掛才會恢復(Resume)並繼續接收資料。設定範例如下:
pipeline:
inputs:
- name: tcp
listen: 0.0.0.0
port: 5170
format: none
tag: tcp-logs
mem_buf_limit: 50MB如果輸入的資料量達到了 mem_buf_limit,那麼 Fluent Bit 就不會在輸入更多的資料。並在日誌中印出如下的訊息:
[warn] [input] {input name or alias} paused (mem buf overlimit)
mem_buf_limit只有在storage.type設定為memory時才會啟用。
記憶體緩衝搭配檔案系統緩衝
可以將 storage.type 設定為 filesystem 來啟用檔案系統緩衝,但這同時也會使 mem_buf_limit 設定失效。
記憶體與檔案系統緩衝機制並非互斥。在輸入外掛啟用檔案系統緩衝功能,可以同時提升效能與資料安全性。
啟用檔案系統緩衝後,區塊還是會儲存在記憶體,但同時也會使用記憶體映射(mmap)將相同的資料副本儲存在檔案系統上。新建立的區塊在記憶體中處於活躍狀態,同時在檔案系統中又有備份,這種狀態可以稱爲 up,說明區塊已經載入記憶體,並準備好傳送出去了。
Fluent Bit 預設記憶體內可以有 128 個狀態為 up 的區塊,每個區塊的上限是 2MB,因此記憶體使用量為 128 × 2MB = 256MB。
storage.max_chunks_up 可以設定記憶體能有幾個 up 狀態的區塊,預設為 128。
當輸入外掛的 up 區塊數量達到上限時,輸入外掛並不會被暫停,而是轉而將區塊儲存在檔案系統上,這些區塊的狀態為 down。等到 up 區塊被傳送出去後,down 狀態的區塊才會被載入記憶體中,變成 up 狀態。這樣的流程可以保證資料不會遺失。
storage.max_chunks_up 設定於 service 區段。設定範例如下:
service:
flush: 1
log_level: info
storage.path: /var/log/flb-storage/
storage.sync: normal
storage.checksum: off
storage.max_chunks_up: 128
storage.backlog.mem_limit: 5M
pipeline:
inputs:
- name: cpu
storage.type: filesystem
- name: mem
storage.type: memory將 Docker 日誌傳送至 S3 上儲存
介紹完 Fluent Bit 的資料管道流程與緩衝機制後,終於可以來說明如何將 Docker 日誌傳送至 AWS S3 上儲存了。接下來以我們的 FRRouting 容器為例,解說我是怎麼把 FRRouting 容器內的日誌傳送到 S3 上儲存。
首先建立一個 fluent-bit 資料夾,用來放置解析器外掛程式與資料管道的設定檔案。
解析器設定
使用 Syslog 輸入外掛就需要指定解析器,雖然 Fluent Bit 本身有提供多種解析器,但並沒有專門給 FRRouting 日誌用的解析器。話雖如此,我們還是可以使用 regex 解析器來解析 FRRouting 的日誌。
在 fluent-bit 資料夾底下建立一個 parsers.conf 檔案,並寫入以下的內容。
[PARSER]
Name frrouting
Format regex
Regex (?<time>\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) (?<log>.*)
Time_Key time
Time_Format %Y/%m/%d %H:%M:%S設定資料管道
有了解析器之後,我們就可以開始設定資料管道,其中會包含輸入與輸出外掛的設定。
在 fluent-bit 資料夾底下建立一個 pipeline.yaml 檔案,並寫入以下的內容。
services:
# 指定解析器設定檔案
parsers_file: /opt/fluent-bit/parsers.conf
pipeline:
inputs:
- name: syslog
# 使用 UDP 協定
mode: udp
listen: 0.0.0.0
port: 5140
# Syslog 輸入必須指定一個解析器,這裡指定我們剛剛建立的 frrouting 解析器
parser: frrouting
buffer_chunk_size: 2M
buffer_max_size: 128M
tag: frrouting
outputs:
- name: s3
# 匹配所有輸入來源
match: "*"
bucket: log-collection
region: us-west-2
total_file_size: 10M
upload_timeout: 1m
# 上傳到 s3 上的路徑與檔案名稱
s3_key_format: /$TAG/%Y/%m/%d/%H-%M-%S-$UUID.json
s3_key_format_tag_delimiters: .-可以看到我在輸入上使用 syslog,並打上了一個標籤 frrouting。輸出外掛使用 s3,並且使用 match 去匹配輸入標籤,這裡我使用萬用字元 "*",代表匹配所有輸入標籤。
s3_key_format 可以設定上傳到 S3 的路徑與檔案名稱。這裡有一個小技巧,我們可以在路徑中使用一些特殊語法,例如使用 %Y、%m 與 %d 來設定日期,使用 $UUID 來產生亂碼避免檔案名稱重複,還有使用 $TAG 來取得輸入的標籤資訊。
s3_key_format_tag_delimiters 可以設定要用什麼符號來對輸入標籤進行切片。設定 .- 的意思就是使用 . 與 - 符號來進行切片。假設輸入的標籤是 this-is-an-input-tag,那麼切分出來就會是 ['this', 'is', 'an', 'input', 'tag']。切分後的標籤字串可以使用索引來取得部分字串,例如用 $TAG[0] 可以取得 this,用 $TAG[2] 可以取得 an,以此類推。如果需要根據來源動態調整 S3 的路徑,那麼標籤切片會非常好用。
運行 Fluent Bit 服務
寫好設定檔案之後,使用 Docker 來運行 Fluent Bit 服務。
docker run -d --name fluent-bit \
--memory 512MB \
--publish 5140:5140/udp \
--restart always \
--mount type=bind,src=fluent-bit,dst=/opt/fluent-bit \
cr.fluentbit.io/fluent/fluent-bit --config /opt/fluent-bit/pipeline.yaml修改容器的日誌驅動程式
使用 --log-driver 與 --log-opt 參數,將原本的 FRRouting 容器改為使用 syslog 日誌驅動程式。
docker create --cap-add NET_ADMIN --cap-add SYS_ADMIN \
--log-driver syslog --log-opt syslog-address=udp://0.0.0.0:5140 \
--name frr \
--restart always \
--network host \
--ulimit nofile=100000:100000 \
--security-opt apparmor=unconfined --security-opt seccomp=unconfined \
--mount type=bind,source=/opt/bgp/frr,target=/etc/frr,ro \
--mount type=bind,source=/opt/bgp/run-frr,target=/var/run/frr \
quay.io/frrouting/frr:master重新啟動容器之後,我們可以使用 docker logs -f fluent-bit 指令查看日誌是否有成功的上傳到 AWS S3。
[2025/09/21 15:59:29] [ info] [output:s3:s3.0] Successfully uploaded object /frrouting/2025/09/19/07-33-19-Lesnda2K.json
[2025/09/21 15:59:29] [ info] [output:s3:s3.0] Successfully uploaded object /frrouting/2025/09/19/07-33-21-BnABKDOJ.json
[2025/09/21 16:00:39] [ info] [output:s3:s3.0] Successfully uploaded object /frrouting/2025/09/19/07-33-31-TQ96biRM.json成功的把容器的日誌上傳到 S3 了!