Fluent Bit 是主流的 log 收集器之一,典型的用法是收到 log 後再轉發給下一手,廣義的說它不只收 log,也收 metric 等其他資料,這裡直接引用 Fluent Bit 網站的概念圖:
發 log 就發 log,為什麼要過一手 Fluent Bit?以我自己而言,每個服務的 log 會集中到 CloudWatch,對於自己寫的程式這沒什麼問題,但對於像是 Traefik、Caddy、Nginx 這類被廣泛使用的服務,它們並不原生支援 CloudWatch,偏偏 web server 的 log 對於稽核或安全方面的分析又很重要,以 Fluent Bit 來收集並轉發就成為主流的解決方案之一。
雖然 AWS 有出 CloudWatch Agent,但我覺得它的目的太過單一、有侷限性,要是哪天想跳槽離開 AWS,又要匆匆忙忙連滾帶爬把它換掉,相較之下支援多輸入/多輸出的 Fluent Bit 是更泛用的選擇。
選用 Fluent Bit 的另一個理由是 Docker logging driver 原生支援 Forward 協議,而 Forward 協議正是 Fluent Bit 團隊開發的,因此採用 Fluent Bit 成為理所當然的選項之一,之所以說「之一」,是因為其他收集器也大多支援 Forward,主流的選手有:
- Fluent Bit:本文主角
- Fluentd:Fluent Bit 的前一代,還活著不過推薦改用 Fluent Bit
- Vector:大廠 DataDog 出的收集器
幾乎每款收集器都標榜自己輕巧不吃資源、處理 log 快吞吐又快、支援各式各樣輸入/輸出,看起來都差不多的情況下挑一套自己看得順眼的就好,也有些人跑了他們之間的評比 benchmark,有的 A 好有的 B 好,參考就好。
配置 Fluent Bit
在使用前必須先寫 Fluent Bit 的配置文件,這裡我們採用較新的 YAML 配置文件,下面是一份簡單的配置文件:
service:
# Log_Level
# =========
# Set the verbosity level of the service, values can be:
#
# - error
# - warning
# - info
# - debug
# - trace
#
# by default 'info' is set, that means it includes 'error' and 'warning'.
# log_level: info
# Parsers File
# ============
# specify an optional 'Parsers' configuration file
parsers_file: parsers.conf
# Plugins File
# ============
# specify an optional 'Plugins' configuration file to load external plugins.
plugins_file: plugins.conf
pipeline:
inputs:
- name: forward
port: 24224
outputs:
- name: stdout
match: "*"
- name: cloudwatch_logs
match: "*"
region: ${AWS_DEFAULT_REGION}
log_group_name: ${LOG_GROUP_NAME}能夠望文生義的部分就不提,其中 service 區塊是配置 Fluent Bit 本身,旗下的 parsers_file、.plugins_file` 兩項指定的檔案不用自己生,都存在現成的 Fluent Bit 映像內。
Pipeline 指的是資料的輸入、處理、輸出的流程,這裡我們不對資料做處理,所以只有 inputs 與 outputs。
前面提到 Docker logging driver 支援 Forward 協議輸出 log,在 Fluent Bit 這邊,就要設定收 Forward log 的 input,設定 input 識別名 forward 與 port 24224 就可以了,非常簡單。
Output 的話就是 stdout 和 CloudWatch,stdout output 的識別名就是 stdout,match: "*" 就是不比對(不過濾)。
CloudWatch output 配置項目比較多一點,但也滿好懂,其中 region 與 log_group_name 的值取自 OS 環境變數。
要打資料給 AWS CloudWatch 還需要 AWS 認證,最簡單的方法是在 OS 環境變數中就設好 AWS_ACCESS_KEY_ID 與 AWS_SECRET_ACCESS_KEY,CloutWatch output 會自己使用,或者是讓運行 Fluent Bit 的 EC2 本身就有具備 CloudWatch permission 的 IAM role,要採用哪種取決於你有多追求把安全性無限上綱以及有多想被 AWS vendor-lock。
配置完存成檔名 fluent-bit.yaml,接著來搞容器。
容器 log 到 Fluent Bit
前面提過,自己寫的服務多半可以利用 AWS SDK 直接把 log 打給 CloudWatch,所以我們關注的重點放在怎麼把 Traefik log 打給 Fluent Bit 再打給 CloudWatch。
先看 compose.yaml:
services:
fluent-bit:
image: cr.fluentbit.io/fluent/fluent-bit:4.2
restart: unless-stopped
ports:
- "24224:24224" # Forward protocal
volumes:
- ./fluent-bit-data/fluent-bit.yaml:/fluent-bit/etc/fluent-bit.yaml
command:
- /fluent-bit/bin/fluent-bit
- --config=/fluent-bit/etc/fluent-bit.yaml
environment:
- AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
traefik:
image: traefik:v3.6
restart: unless-stopped
security_opt:
- no-new-privileges=true
ports:
- "80:80"
- "443:443"
- "443:443/udp"
logging:
driver: fluentd
options:
fluentd-address: 127.0.0.1:24224 # macOS: host.docker.internal:24224
fluentd-async: 'true'
tag: traefik這份 compose.yaml 只保留我們關注的重點,省略其他元素。
在 Fluent Bit 方面,我們把前面的 fluent-bit.yaml 掛載進容器內,並自訂 command 讓 Fluent Bit 讀取之。
在 Traefik 方面,我們啟用 Fluentd logging driver,它本質上就是支援 Forward 協議的 logging driver。
前面提到,Fluent Bit 的 Forward input 的 port 為 24224,而 Traefik 的 fluentd-address 不能填 Docker container network 的位址,所以我們把 fluent-bit:24224 映射到實體素主機的 24224,Traefik 的 fluentd-address 則填入 127.0.0.1:24224,如此方可讓 Traefik log 送到 Fluent Bit,如果宿主機是 Mac,那又多了一層 Docker VM,所以得改成 host.docker.internal:24224。
fluent-bit 服務作為非主要之週邊服務,它不應成為其他服務的依賴,不應列入其他服務的 depends_on,所以啟用 fluentd-async 來讓 Traefik 能自行啟動並送出 log,不需要關心 Fluent Bit 啟用與否。
至此一套具體而微的 Fluent Bit + Traefik 系統就配置完成了,一套組合拳下來,Traefik log 輸出到容器的 stdout / stderr,然後由 Docker logging driver 轉發給 Fluent Bit,再轉發給 CloudWatch,一筆筆 log 就這麼風塵僕僕的抵達了 CloudWatch。
