實作具備自動擴展機制的 fluentd 架構

Auto-Scaling fluentd architecture on Google Cloud Platform

大概從兩年多以前就開始使用 fluentd 作為 Log 管理的架構,由於當時機器主要都還在 IDC 機房內,所以在架設 Log Aggregator 方面也是以位於 IDC 的實體機器為主,採用的是標準的 N+1 架構 ,後端則是將 Log stream 到 BigQuery 去做更進一步的應用,兩年多來相安無事。

具備 autoscaling 的 fluentd+BigQuery 架構

但隨著慢慢的把一些服務往 GCP 上搬,而且也越來越多服務在 GKE 上執行,原有的架構顯得有點不合時宜:已經在雲端上的資料沒道理再繞路回 IDC 再跑去 BigQuery。另外在一陣子之前因為維護的原因,突然同時的重開某一系列的機器,突然且同時的大量連線跟資料量讓 Log Aggregator 無法消化,雖然設計有 redundant 的 standby 機制但是眼前數百台機器聯手 DDoS 的情況連 standby server 都也給打掛了, 最後是緊急調配備用實體機器跟手動管控重啟服務的順序跟數量才得以解決。

原有的 fluentd+BigQuery 架構

面臨到上面所提到的問題之後只好認真來想解決方案,具備 Auto Scaling 的 fluentd 並不是什麼新技術,在 AWS 上也已經有人實作許久,所以這篇只能當做流水帳來分享經驗。

首先原理會是在 Compute Engine 上利用 Instance Template 建立一個 Instance Group,並且指定 Scaling Threshold:80% CPU。建立 Instance Template 其實就是開啟一台 GCE Instance,在上面裝好 fluentd 跟相關的設定檔與 plugin,然後將這個 Instance 的硬碟建立成一個 Image。值得注意的是,你必須要把 Instance 關機並且將硬碟 Detach 之後才能把這個硬碟拿來做成 Image Source。

另外,為了方便後續 Image 更新方便,記得要在建立 Image 時候指定 family。指定 family 的好處是,當你後續有更新的 Image 時,你可以把就的 Image 給設定成 Deprecated,下次 Auto Scaling 需要開機器時,他會選擇同個 family 內非 Deprecated 的 Image,而不需要手動重開所有的 Instance ,這樣也算達到 Rolling Update 的效果與機制。

建立 Image 的畫面,且指定family 名稱

我們回到 fluentd 的設定。在 GCP 端也就是在我們架構中的 Log Aggregator,意思是接取來自 Source 端的資料,並轉發到目的地,也就是 BigQuery。而且因為要讓 Source 端能夠保持最大的 Throughput 跟減少因為 Log 轉送的 overhead,Log Aggregator 會做適當的 buffering。

但因為從 IDC 轉送出來的 log 因為已經是走 Internet 做傳輸,因此在政策上當然是需要做加密的動作。在 fluentd 有 secure_forward 這個套件 (0.12.x 版本需要另外安裝,0.14.x 則已內建),安裝指令為:

# fluent-gem install --no-rdoc --no-ri -V fluent-plugin-secure-forward

因為是走 SSL,那當然少不了是憑證。套件作者對於憑證的說明是可以利用一個小工具產生自簽憑證 self-signed certificate,老實說我也不清楚在這個應用上用自簽憑證會有什麼風險 … 有 MITM 攻擊?建立的指令為:

% secure-forward-ca-generate /path/for/dir/of/certs "notasecret"

建立完成後就是設定 fluentd 了,首先指定 type 為 secure_forward 以及使用 埠號 24284 跟明碼的 24224 分隔出來,且指定與 client 的 shared key。然後就把剛剛建立的憑證與金鑰的路徑給指定,除此之外就是金鑰的密碼。

Log Aggregator 端的fluentd 設定檔

另外為了統計的需要跟等等我們做 Auto Scaling 的 health check 需求,這邊我們使用了 fluentd 內建的 monitor_agent 套件,並將他指定在 24220。而因為有 health check 的需求,所以要 bind 在 0.0.0.0 而不是原本的 127.0.0.1。

fluentd 統計程式

另外為了能夠有效的監控 fluentd 的系統狀態,我們這邊就寫了一個小程式來去將每台 fluentd 的監控資料利用 StatHat 做統計。根據 monitor agent 所回傳的資料,我們可以得到目前這台 fluentd 主機的 queue 的大小 (bytes)、長度 (count) 以及重試的次數。根據這些資料可以判斷目前整體的 fluentd 狀態,例如範例。可以簡單的設定 cron job 來每分鐘執行這個 script 來達到統計的效果。

其餘的部份就沿用原本的 fluentd 設定,在這裡就不贅述 fluentd 轉發到 BigQuery 的設定,有興趣可以研究這個 GitHub repo。更動完設定可以重跑一次 fluentd,看是否有正確的讀入憑證並且正確的在 24284 準備接收資料。

測試完成之後並且建立 Image 之後,我們就回到了 UI 接著建立 Instance Template,Template 這邊主要就是指定你是用哪個 Image,用什麼等級的機器 (我們是用 n1-standard-1),是用一般硬碟還是 SSD。但是因為我們必須要預期他會有資料 buffer 在硬碟內,因此就不能打開 preemptiable 的選項,因為如果資料還沒傳完就被關機器是會掉資料的!

Instance Template 設定畫面

開完 Instance Template 就是 Instance Group 的設定。這邊的設定主要是針對 Auto Scaling 的內容,例如 Instance 要在哪一區或是哪幾區,自動擴展的依據是什麼。根據我這邊的需求,我是開在 asia-east1 的 Multi Zone,並且開啟最低一個,最高 30 個 Instance,且 CPU 80% 的時候作為 threshold。另外為了後續的 Load Balancer 設定需要,我們在這裡指定一個名稱為 fluentd-secure 的 port mapping 到 24284。

Instance Group 的設定範例

上面提到 Health Check,這是用來偵測機器是否正常執行的依據,根據 fluentd 的文件,monitor_agent 套件會在 port 24220 開啟一個 Web Server,透過存取 /api/plugins.json 路徑可以得到相關的統計數據,那我們就可以利用這個作為 fluentd 是否正常執行的依據。

Health Check 的設定範例

完成 Instance Group 的設定後就只差 Load Balancer 的部份了。這裡需要建立的一個是 TCP Load Balancer,且指定我們剛剛所設定的 Instance Group 以及 Health Check。對外的 Service Port 則指定為 24284。記得要把 Load Balancer 的對外 IP 設為 Static IP。

TCP Load Balancer for fluentd 設定範例

設定完成後到 Load Balancer 的內容頁檢查是否一切正常,如果 Health Check 正常運作則 Instance 旁會有綠色的勾,同時也可以用 telnet 連線到 Load Balancer 的對外 IP port 24284 看看是否可以連的上,沒問題就可以準備設定 client 把流量開過來了。

fluentd 的 client 設定相對單純許多,跟 server 端一樣指定 SSL 的公開金鑰以及 preshared key 值。但需要注意的一點是 fluentd 預設使用 UDP 作為 heartbeat 的方式。那因為我們上面是採 TCP load balancer 所以要把heartbeat type 指定為 TCP。如果你有 Proxy 的需求那就使用 proxy_uri 參數來設定 proxy server 位置。

fluentd client 端設定檔

到這裡所有的設定已經大功告成了!重新執行 client 端的 fluentd 看是否有正確連線到遠端,而如果一切順利,你應該可以在 GCP 的 Instance Group 看到 Auto Scaling 的如下圖:

Instance Group 的 Auto Scaling 情況表示圖

至此我們已經完成一個具備有自動擴展機制的 fluentd Client/Server 架構。未來可以不用擔心瞬間資料量突然增加會讓系統無法負荷而掉了資料。且透過 secure_forward 我們也可以達到當資料在透過 Internet 傳輸時能夠加密傳送。 未來或許可以在另外一個 region (例如 asia-northeast1) 建立一個一樣的架構,給 fluentd 作為 standby server 使用,增加可靠度。