fbpx

用 ElasticSearch + FluentD 打造 Log 神器與數據分析工具

fluentd

Log 的好朋友 Fluentd

之前的工作只管理幾台 Server,以往一個服務需要的應用伺服器與資料庫不會超過十台。後來開發企業雲端硬碟 SaaS 服務,到後期所有開發、測試、上線環境的 Server 數量已經快要破百,尤其在有水平擴充能力的分散式架構與高可用架構下,VM 除了數量多之外,還分佈在不同地區的 IDC,每次查起 Log 都相當吃力,一直很想集中 Log 進行管理。由於我本身也不是系統管理的專家 (寫霸各才是我的專長啊!?),一般都是弱弱的 SSH 進 Server 查 Log,常常系統出問題牽扯的 Log 的種類少說數 10 種,像是 syslog, dmesg, secure, mysql, mongo, php-fpm, application log, nginx, haproxy, jos-server, nodejs… 說著說著我頭都暈了,這時候心想如果有 Log Server 那該有多好?知名的商業 Log Server Solution 一定先想到貴鬆鬆的 Splunk,話說能用錢解決的問題都不是問題,可惜沒有錢,只好自己找找 Open Source Solution。Google 之後 Fluentd 出現了,一個可以整合所有 Log 的資料搜集工具,不用錢的就好棒棒!我們先來看一下 FluentD 怎麼安裝?

安裝 Fluentd

Fluentd 在 Linux 中的套件名稱為 td-agent,但 Linux Ubuntu 內建並沒有這個套件,需要透過以下命令安裝:

sudo curl -L https://toolbelt.treasuredata.com/sh/install-ubuntu-trusty-td-agent2.sh | sh

一開始安裝完成後,首先修改 Fluentd Servive 執行身份改用 root 執行,這樣改是因為後面要實作的案例很多 Log File 都是各自 Service User 擁有,為了要順利讀出 Log File 只好出動大無敵招式,如果你不需要這樣的 root 權限,建議保留原始設定即可。先編輯 /etc/init.d/td-agent 檔案,直接用 sed 命令置換 Fluentd Service User,如下:

sudo sed -i "s/TD_AGENT_USER=td-agent/TD_AGENT_USER=root/g" /etc/init.d/td-agent

sudo sed -i "s/TD_AGENT_GROUP=td-agent/TD_AGENT_GROUP=root/g" /etc/init.d/td-agent

上述的 Script 執行後會修改以下這兩個變數,設定執行的使用者為 root

TD_AGENT_USER=td-agent

TD_AGENT_GROUP=td-agent

修改後重新啟動 Fluentd 服務

sudo service td-agent restart

Fluentd 服務的 Log 在這裡,啟動時會載入相關 Log Configuration。如下:

cat /var/log/td-agent/td-agent.log

Fluentd (td-agent) 服務啟用後,預設會開啟 127.0.0.1:24230, 0.0.0.0:24224 與 0.0.0.0:8888 這三個 Port。分別介紹一下這些 Port 的作用:

第一個 127.0.0.1:24230 是 Debug Agent,預設僅開放本機連線,Log 才不會被看光光,可以透過 fluent-debug 進行連線操作。對應 /etc/td-agent/td-agent.conf 中的 Source Block (@type debug_agent),如下:

<source>
 @type debug_agent
 bind 127.0.0.1
 port 24230
</source>

第二個 0.0.0.0:8888 是用來接收 Log 資訊的 Log HTTP Service,可以直接透過 HTTP 送 Log 進來。不需要認證直接透過 CURL 就可以測試,CURL 命令如下:

curl -X POST -d 'json={"message":"show me!"}' http://localhost:8888/td.myTag.test

上述的命令就會建立一筆 {"message":"show me!"} 內容的 Log,Log HTTP Service 對應 /etc/td-agent/td-agent.conf 中的 Source Block (@type http) 如下:

<source>
 @type http
 port 8888
</source>

最後一個 0.0.0.0:24224 是 Forward Service,可以用來接收其他 Fluentd 傳送過來的 Log,對應 /etc/td-agent/td-agent.conf 中的 Source Block (@type forward) 如下:

<source>
 @type forward
</source>

Fluentd 基本概念

介紹完設定檔之後,先來了解一下 Fluentd 基本概念,Fluentd 所有的 Log 都是 Json 格式,而且沒有 Schema 限制,想怎麼存都可以,如下:

fluentd log

主要的資料流Input PluginOutput Plugin 組成,在設定檔中以 source Block 的方式存在,表示定義進入 Fluentd 訊息流中心的「來源」提供者,Log 訊息在 Fluentd 中可以被接收、傳遞、處理、過濾、再傳遞,一直這樣下去。一般我們會把本機各種型態的 Log 匯流至自己本機的 Fluentd,統整之後透過 Output Plugin 匯流至 Log Server Fluentd 的 Input,最終再由 Log Server 中的 Fluentd Output 匯流至資料庫中,也可以與像是 datadog 之類的 Cloud Log Monitor 進行整合。參考下圖左邊是 Input 右邊是 Output:

fluent-arch

Fluentd 設定教學

開始設定之前,我們先已經了解 Fluentd 基本提供的設定元素 Input Plugin/Output PluginFluentd 的設定檔放在 /etc/td-agent/td-agent.conf 這裡,主要由 source 與 match Block 組成。source Block 用來接收 Input,match Block 可以用來 Match Log Tag 進行 Output 或其他處理。

由於不同服務的 Log Format 都不一樣。Input 來源常用的方法就是 @type tail 開檔的方法,畢竟幾乎所有的 Log 機制都會寫檔到 Disk 中,用這樣的方法可以直接讀取程式的 Log File 進行解析,不需要在程式中外掛函式,比較方便。我打算規劃的架構一台 Log Server 啟用 24224 Forward Service 讓其他眾多異質的服務統一以非同步的方式傳送 Log 進來,然後由 Log Server Output 轉存進 MongoDB 或者 ElasticSearch 進行快速調閱與分析。接著我們先設定 Server:

設定 Log Server

Log Server 只需要留下 Forward Service,因此我先修改 Log Server 設定檔,僅保留 Forward Service,並且指定所有的 Log 先寫到檔案 /var/log/fluent 中進行測試,後面確定 Log 有搜集進來再轉存 MongoDB 或 ElasticSearch,完整的設定檔如下:

<source>
 @type forward
</source>

<match **>
 @type file
 path /var/log/fluent
 time_slice_format %Y%m%d
 time_slice_wait 10s
 time_format %Y%m%dT%H%M%S%z
 compress gzip
 utc
</match>

Disk I/O 是效能的殺手,為了不要太頻繁寫檔,所以定義 time_slice_wait 10s 延遲寫 Log 到檔案的時間。然後重啟 td-agent,重啟會只剩下開啟 24224 Port。

sudo service td-agent restart

設定其他 Log Client

把 Server 設定好之後,再來就要設定 Client 了,一樣所有的機器都裝好 FluentD。一開始我們先用 tail 測試,把本機 Linux 的 syslog 傳送到 Log Server 集中存放。除了上述 Log Server 之外,其他的 Client 機器全部關閉 Log Service,最簡單的關閉方法就是註解 /etc/td-agent/td-agent.conf 其中的 Source Block,並且建立一個設定為 @type tail 的 Input Source Block 資料來源。

雖然剛剛提到 Fluentd Log 是一個沒有特定格式的 Json,但有一點要注意,由於 Log 最重要的元素就是「時間」(機器記得啟用 NTPD 校時啊),然而 Fluentd Log 傳統的傳送方式都是非同步模式,Log Server (Forward Service) 收到 Log 的時間並非 Log 真正發生的時間,因此每一筆 Log 強烈建議透過 time_format 指定時間格式,透過 time 欄位傳送一致的 Fluentd 時間格式,這樣收集到的 Log 分析才會正確。Client 完整的設定檔如下:

<source>
  @type tail
  path /var/log/syslog
  pos_file /var/log/td-agent/syslog.log.pos
  tag td.syslog
  format /^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
  time_format %b %d %H:%M:%S
</source>

<match **>
  @type forward
  send_timeout 10s
  recover_wait 8s
  heartbeat_interval 1s
  phi_threshold 16
  hard_timeout 10s
  <server>
    name elastic-db
    host log-server
    port 24224
  </server>
  <secondary>
    @type file
    path /var/log/fluent/forward-failed
  </secondary>
</match>

上述 source 中的 tag 可以自行定義,透過 match ** 表示所有 tag 都進到這裡處理,透過 @type forward 把資料送到 host log-server 我們剛剛設定好的 Server (host log-server) 中。當然 log-server 也可以用 IP 指定,只是我的環境習慣用 host 進行佈署,好實現設定檔可攜。

設定好之後重新啟動 td-agent,然後當 /var/log/syslog 有新資料時,就會自動傳到 log-server 這裡了,如同前一個章節 Server 的設定,Log 最終會被集中儲存到檔案中。

整合 MongoDB 儲存 Log

Log 存到檔案其實很難檢索,Fluentd Log 是 JSON 格式,可以直接存進 NoSQL 特性的 MongoDB 中,先在 log-server 上安裝好 MongoDB (安裝方式可以參考以前的文章),然後把上述的 <match> Block 改成下面的設定:

<match **>
  @type mongo
  database lodge
  collection fluentd
  capped
  capped_size 100m
  host 127.0.0.1
  port 27017
  user <MONGO_USER>
  password <MONGO_PASS>
  time_key time
</match>

上面設定檔中的 <MONGO_USER> 與 <MONGO_PASS> 請替換為你的 MongoDB 登入帳密。這裡有一個很好用的功能,就是 capped 設定,可以指定 capped_size 讓 Log 循環存放,不用擔心 Log 會把 MongoDB 塞爆,因為這個功能所以讓 MongoDB 晉升為我心目中的 Log Server 神器!Log 存進 MongoDB 之後就可以進行 Query 囉,超方便的,如下:

mongo-fluent

整合 ElasticSearch 儲存 Log

ElasticSearch 是一個全文搜尋引擎,核心當然是神作品 Apache Lucene 所打造,今天不對 ElasticSearch 深入介紹,簡單的安裝方式如下:

先安裝 Oracle Java

sudo echo 'deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' > /etc/apt/sources.list.d/webupd8team-java.list

sudo echo 'deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' >> /etc/apt/sources.list.d/webupd8team-java.list

sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com EEA14886

apt-get update

echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections

echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections

apt-get -y install oracle-java8-installer

 

下載與安裝 ElasticSearch

 

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.3.0.deb

sudo dpkg -i elasticsearch-5.3.0.deb

sudo systemctl enable elasticsearch.service

安裝 fluent-plugin-elasticsearch

想要把 Log 餵進 ElasticSerch 需要用到 fluent-plugin-elasticsearch 外掛,我 Ruby 超弱的,不知道是手殘還是怎樣,透過 gem 一直裝不起來,直接透過 td-agent-gem 來安裝就很順利了,td-agent-gem 顧名思義就是 td-agent 在用的 GEM 囉,如下:

sudo td-agent-gem install elasticsearch

最後是設定 Fluent (td-agent) 把資料導流到 ElasticSearch,設定檔內容如下:

<match **>
  @type elasticsearch
  host localhost
  port 9200
  index_name fluentd
  type_name log
</match>

重開後就會把 Log 存進 ElasticSearch 囉,如下:

 

fluentd-elasticsearch

進到 ElasticSearch 後可以快速查詢,算是很方便,但是 ElasticSearch 在 5.0 之後的版本我找不到像是 MongoDB 這樣的 Capped 功能,深怕 Log 灌爆磁碟,如果那位大大知道 ElasticSearch 可以限制 index 的容量或筆數,跪求分享!掰~