在使用前,請先閱讀 數據模型數據格式 的介紹。

1. 概述

神策分析支援使用 Logstash + Filebeat 的方式將 後端數據即時 匯入神策分析。

Logstash 是由 Elastic 公司推出的一款開源的伺服器端數據處理管道,能夠同時從多個來源採集數據,轉換數據,然後將數據傳送指定的儲存庫中。 Logstash 官方介紹

Filebeat 是 Elastic 公司為解決 Logstash "太重" 的問題推出的一款輕量級 log 採集器,在處理數量眾多的伺服器、虛擬機和容器產生的 log 時可使用 Logstash + Filebeat 的 log 採集方式。 Filebeat 官方介紹

基於 Logstash + Filbeat 的數據採集流程為:後端 SDK 產生數據檔案 => Filebeat 讀取檔案 => Logstash Beat input => Logstash sensors_analytic output => 神策分析 。

結構如下圖所示:

本文將介紹以下三個場景中如何使用 Logstash + Filebeat 完成數據採集並傳送至神策分析。

在閱讀詳細方案前,請先閱讀 Logstash 和 Filebeat 的使用說明與 版本支援資訊

2. Logstash 使用说明

無論使用哪種方案 Logstash 都必須裝有 sensors_analytics_output 外掛程式。

請使用 Logstash-6.x 以上版本。

2.1. Logstash 下載與安裝

請参考 installing Logstash 官方說明文件 ,選擇您喜歡的下載與安裝方式。

2.2. 安裝 logstash-output-sensors_analytics 外掛程式

該外掛程式將檢查數據是否為 Json 格式,並加入一些神策需要的欄位值如 lib、data 等,打包數據,壓縮並經過 base64 之後傳送至神策的數據接收網址。
外掛程式已經發布至 Ruby 官方資料庫,Github repository : logstash-output-sensors_analytics 。直接在 Logstash 目錄下執行安裝即可,安裝需要一段時间,請耐心等待。

bin/logstash-plugin install logstash-output-sensors_analytics
CODE

在安裝完成後執行 :

bin/logstash-plugin list
CODE


看見新安裝的外掛程式 logstash-output-sensors_analytics 證明安裝成功。

外掛程式在使用時直接設定在 output 裡即可

output{
    sensors_analytics {
        url => "https://example.sensorsdata.cn/sa"
    }
}
CODE


sensors_analytics 參數說明:

參數名型別必須說明
urllist神策分析的數據接收網址,完整的 url 網址並以 sa 結尾,有埠號需要加上埠號。例如 :https://example.sensorsdata.cn:8106/sa 。可同時設定多個數據接收網址,一條數據實際傳到哪個數據接收網址,是由輸入外掛程式中部分元數據資訊 hash 後決定。在使用 Filebeat 時預設使用 Filebeat 當時的 hostname + filename進行 hash 後選擇數據接收網址。
projectstring專案名,不寫預設為 default ,設定後會覆蓋事件中和 url 中指定的 project。優先級為:project 參數設定 > 事件中指定 > url 中指定。
flush_interval_secnumber觸發 flush 間隔的時間(單位:秒),預設值為 2。
flush_batch_sizenumber觸發批量傳送的最大 record 數量,預設值為 100 。
hash_filedlist用於 hash 選擇數據接收網址的 filed,層級關係與輸入的 logstash.event 結構相同,可在 output 中使用 stdout 觀察。
enable_filebeat_status_reportboolean預設開啟,在 log 展示中一分鐘內活動的 Filebeat 讀取狀態。

2.3. Logstash 設定

2.3.1. Logstash Pipeline 設定

Logstash 支援同時執行多個 Pipeline ,各個 Pipeline 之間互不影響,擁有各自獨立的輸入輸出設定,Pipeline 的設定檔案位於 config/pipelines.yml 。如果您目前正在使用 Logstash 完成一些其他的 log 採集工作,可以在原有的 Logstash 上新增一條 Pipeline 專門負責收集神策的 log 數據,並傳送至神策分析。

  • pipelines.yml 参考範例:
# 原來使用的 Pipeline 設定
- pipeline.id: elastic-output
  pipeline.workers: 4
  path.config: "/home/app/logstash/elastic_output.config"

# 新增的 sensorsdata 的管道設定
- pipeline.id: sensorsdata-output
  # 使用不同的 Logstash 設定
  pipeline.workers: 1
  queue.type: persisted
  # 使用不同的輸入輸出設定
  path.config: "/home/app/logstash/beat_sa_output.config"
YML

注意:神策分析 log 的輸入要不同於其他匯入到 Logstash 的 Piplines 輸入。比如之前使用的 log 匯入方式是透過 Filebeat 採集並傳送到 Logstash 的 5044 Port,那麼負責採集神策 log 的 Filebeat 可以將數據傳送至 5055 Port,從而能夠應用 id = sensorsdata-output 的管道。

更多 Pipeline 資訊請參考 : Multiple-Pipelines 官方說明文件

2.3.2. Logstash 輸入輸出設定

設定中主要包含 input、filter 和 output 三部分,Logstash 處理神策的 log 數據只需設定 input 和 output 即可

  • beat_sa_output.conf 参考範例:
# 使用 beats 作為輸入
input {
    beats {
        port => "5044"
    }
}
# 使用 sensors_analytics 作為輸出
output{
    sensors_analytics {
        url => "https://example.sensorsdata.cn/sa"
    }
}
YML

提醒:在使用 logstash-file-input-plugin 時當 logstash 處於關閉狀態下時重命名已讀檔案(重命名後依然符合 pattern),啟動後會導致重複讀取。

2.3.3. Logstash 執行設定

Logstash 預設使用 config/logstash.yml 作為執行設定。

這裡需要注意的是:

1. 在需要確保數據匯入順序的情況下請更改設定 pipeline.workers 的值為 1。設定項 pipeline.workers 的值預設為 cpu 的核心數,當 workers 的值大於 1 時,會導致處理數據的順序發生變化。
2. 為確保數據的傳輸不會因為程式的意外終止而遺失,請設定 queue.type: persisted,該設定為 Logstash 使用的緩衝佇列類型,這樣設定可在重啟 Logstash 後繼續傳送緩衝佇列中的數據。 queue.type 的預設值為 memory (基於記憶體的)。
3. 建議設定 queue.drain 的值為 true ,該設定項會使 Logstash 在正常退出之前將所有緩衝佇列中的數據全部傳送完畢。

更多 logstash.yml 資訊請參考 : logstash.yml 官方說明文件 。

2.4. Logstash 啟動

  • 直接啟動,會使用 config/pipelines.yml 作為 Pipeline 設定和執行設定。
bin/logstash
CODE
  • 指定 ~/logstash/beat_sa_output.conf 為輸入輸出設定檔案啟動 ,會使用 config/logstash.yml 做為執行設定。
bin/logstash -f ~/logstash/beat_sa_output.conf
CODE
  • 透過命令參數指定輸入輸出啟動,會使用 config/logstash.yml 作為執行設定。
bin/logstash -e 'output { sensors_analytics { url => "https://example.sensorsdata.cn/sa" }}'
CODE

更多啟動相關資訊請參考 : Getting Started with Logstash 官方說明文件

2.5. Logstash 進度

Logstash 在使用Filebeat 作為輸入時檔案的讀取進度是由Filebeat 進行控制的,當使用其他的輸入方式時,例如Logstash 讀取檔案,消費Kafka 等,數據的讀取進度存放在Logstash 目錄 data/plugins 下,基於硬碟的數據緩衝佇列存放在 data/queue 中。可在 logstash.yml 中設定 path.data 來指定 Logstash 啟動時使用的 data/ 目錄的位置。

2.6. sensors-output-plugin 升级與 復原

  • 已安裝外掛程式升級至最新版本
bin/logstash-plugin update logstash-output-sensors_analytics
CODE
  • 安裝指定版本的外掛程式
# v0.1.0
bin/logstash-plugin install --version 0.1.0 logstash-output-sensors_analytics
# v1.1.2
bin/logstash-plugin install --version 0.1.2 logstash-output-sensors_analytics
CODE
  • 移除外掛程式
bin/logstash-plugin remove logstash-output-sensors_analytics
CODE


3. Filebeat 使用说明

3.1. Filebeat 下载與安裝

請參考:Install Filebeat 官方說明文件 。選擇您喜歡的下載與安裝方式。

3.2. Filebeat 設定

使用 Filebeat 讀取後端 SDK 產生的埋點紀錄檔。Filebeat 預設設定檔案為:filebeat.yml 。修改設定檔案請使用 log 類型作為 Filebeat 的輸入,paths 指定數據檔案所在的位置,使用萬用字元 `*` 匹配後端 SDK 輸出的檔案名路徑。

  • Filebeat 的輸入輸出設定 `filebeat.yml` 参考範例:
# Filebeat 收集 /var/logs/ 目錄下所有以 service_log. 開頭的數據檔案
filebeat.shutdown_timeout: 5s
filebeat.inputs:
- type: log
  paths:
    - /var/logs/service_log.*

# 將數據傳送網址為 10.42.32.70:5044 或 10.42.50.1:5044 的 logstash
output.logstash: 
  hosts: ["10.42.32.70:5044","10.42.50.1:5044"]
YML

需要注意的是:

  1.  匯入的數據必須是神策的數據格式。
  2. 在需要確保匯入順序的情況下不要額外設定 loadbalance : true ,當設定了多個 Logstash hosts 作為數據接收端時該設定會使用輪詢的方式將數據傳送至所有的Logstash 這很可能導致數據的順序被打亂。 Filebeat 的預設設定為 loadbalance : false
  3.  Filebeat 的檔案讀取進度存放在 data/registry 目錄下,在啟動時用於恢復進度。
  4. 在 Filebeat 執行時間,避免使用 vim 之類的可能產生檔案副本的編輯器編輯檔案,Filebeat 會讀取目錄中臨時產生的檔案。
  5. 建議新增設定項 filebeat.shutdown_timeout: 5s ,filebeat 在退出時有可能造成少量數據重複。

更多設定相關資訊請參考:Filebeat 官方文件

3.3. 啟動 Filebeat

./filebeat -e -c filebeat.yml 
CODE

-c 用於指定 filebeat.yml  設定檔案的位置,-e 可在終端上顯示 Filebeat 的 log 資訊。

3.4. Filebeat 進度

如果你的目錄下有多個檔案未被讀取,filebeat 會同時讀取多個檔案,檔案的讀取進度存放在 Filebeat 目錄下 data/registry 中,重啟 Filebeat 時會根據進度繼續執行傳送。

4. 伺服器場景下的數據採集

如果您產生 log 的後端應用直接部署在伺服器上,本節內容將介紹如何使用 Filebeat + Logstash 採集產生的 log 數據。該場景下也可使用 LogAgent 完成 log 的收集工作。

4.1. 部署 Logstash

如果您已經在使用 Logstash 做一些其他的 log 收集工作請參考  Logstash + Filebeat #Logstash 設定 。

参考 Logstash + Filebeat #Logstash 使用說明 直接在您的一台或多台伺服器上部署 Logstash 。

  • Logstash 輸入輸出設定 logstash.conf 範例:
# 使用 beats 作為輸入
input {
    beats {
        port => "5044"
    }
}
# 使用 sensors_analytics 作為輸出
output{
    sensors_analytics {
        url => "https://example.sensorsdata.cn/sa"
    }
}
CODE
  • Logstash 執行設定 logstash.yml 範例:
pipeline.workers: 1
queue.type: persisted
queue.drain: true
CODE
bin/logstash -f logstash.conf
CODE

4.2. 部署 Filebeat

在會產生埋點 log 的伺服器上部署 Filebeat 採集指定目錄下的 log 傳送至神策分析。

神策分析各後端語言的 SDK 都支援將數據寫入檔案,例如使用 Java SDK 的 ConcurrentLoggingConsumer,PHP SDK 的 FileConsumer,Python SDK 的 LoggingConsumer 它們能將 log 檔寫入指定的目錄下。

  • 以 Java SDK 為例:
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
// 將數據輸出到 /data/sa_log 下的 service_log 開頭的檔案中,每天一個檔案
final SensorsAnalytics sa = new SensorsAnalytics(
        new SensorsAnalytics.ConcurrentLoggingConsumer("/data/sa_log/service_log"));

// 使用神策分析記錄用戶行為數據
sa.track(distinctId, true, "UserLogin");
sa.track(distinctId, true, "ViewProduct");

// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
JAVA

以上設定將在 /data/sa_log 目錄下產生數據檔案,一天一個檔案,檔案列表如:

service_log.20170923
service_log.20170924
service_log.20170925
CODE

Filebeat 透過設定 filebeat.yml 讀取目錄 /data/sa_log 下的以 service_log. 開頭的 log 檔,傳送至部署好的 Logstash 。

  • filebeat.yml 參考範例:
# Filebeat 收集 /data/sa_log 目錄下所有以 service_log. 開頭的數據檔案
filebeat.inputs:
- type: log
  paths:
    - /data/sa_log/service_log.*

# 將數據傳送至網址為 10.42.32.70:5044 或 10.42.50.1:5044 的 Logstash
output.logstash: 
  hosts: ["10.42.32.70:5044","10.42.50.1:5044"]
YML

當在一台伺服器上有多個產生 log 的目錄時可設定 Filebeat 同時讀取多個目錄。

  • 讀取多目錄 filebeat.yml 參考範例:
filebeat.inputs:
- type: log
  paths:
    # 收集 /data/sa_log 目錄下所有以 service_log. 開頭的數據檔案
    - /data/sa_log/service_log.*
    # 收集 /another/logs/ 目錄下所有以 sdk_log. 開頭的數據檔案
    - /another/logs/sdk_log.*

# 將數據傳送至網址為 10.42.32.70:5044 或 10.42.50.1:5044 的 Logstash
output.logstash:
  hosts: ["10.42.32.70:5044","10.42.50.1:5044"]
YML
  • 後台啟動 Filebeat
nohup ./filebeat -c filebeat.yml > /dev/null 2>&1 &
CODE

5. 使用 Docker 容器化場景下的數據採集

5.1. 部署 Logstash

為確保 Logstash 的穩定工作,建議直接部署 Logstash,下文為 docker 部署方式僅供參考

如果您已經在使用 Logstash 做一些其他的 log 收集工作請參考 Logstash + Filebeat #Logstash 設定 。為避免容器意外關閉導致遺失數據,請設法保存緩衝區內的數據。

首先,取得一個具有 sensors_analytics output 外掛程式的 Logstash 鏡像

方式一:直接下載我們已經安裝好的外掛程式的 Logstash (7.2.0 版本) 鏡像。

docker pull sensorsdata/logstash:7.2.0
CODE

方式二:自行製作帶有 sensors_analytics output 外掛程式的 Logstash 鏡像。

  • Dockerfile 範例:
FROM docker.elastic.co/logstash/logstash:7.2.0

RUN /usr/share/logstash/bin/logstash-plugin install logstash-output-sensors_analytics
CODE

準備需要的設定檔案。

  • Logstash 的輸入輸出設定 logstash.conf 示範:
input {
    beats {
        port => "5044"
    }
}
output {
    sensors_analytics{
        url => "http://10.42.34.189:8106/sa?project=default"
        project => "default"
    }
}
CODE
  • Logstash 的執行設定 logstash.yml 示範:
pipeline.workers: 1
queue.type: persisted
queue.drain: true
CODE

由於 Logstash 需要使用硬碟做緩衝佇列,這裡我們建立一個 Volume 專門用於保存 Logstash 的進度和緩衝佇列,當重啟該 Logstash 容器時請重複使用該 Volume 。

docker volume create logstash-data
CODE

在啟動該容器時掛載設定檔案和存放緩存佇列的數據卷。

  • 啟動命令參考範例:
docker run -d -p 5044:5044 --name logstash \
--mount source=logstash-data,target=/usr/share/logstash/data \
-v ~/local/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf \ 
-v ~/local/logstash/logstash.yml:/usr/share/logstash/config/logstash.yml \ sensorsdata/logstash:7.2.0
CODE

5.2. 部署 Filebeat

5.2.1. 方案一:在 SDK 容器中安裝 Filebeat 採集 log 並傳送至 Logstash(推薦)

在您能夠產生埋點記錄檔的容器上安裝一個Filebeat 採集 log 並傳送至部署好的 Logstash,Filebeat 為一款輕量級的 log 採集器,運作記憶體大概10 MB 左右,並不會給您的工作容器帶來太多的負擔。

  • 優點:部署方便,不用擔心 Filebeat 的進度問題。
  • 缺點:侵入了原有的 SDK 容器。

下面以 JavaSDK 作為工作容器舉例:

  • Dockerfile 示範:
FROM centos

ADD jdk-8u211-linux-x64.tar.gz /usr/local/

ENV JAVA_HOME /usr/local/jdk1.8.0_211
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin

COPY javasdk.jar /home

# 在容器中安裝一個 Filebaet
ADD filebeat-7.2.0-linux-x86_64.tar.gz /home
# 一份預設的設定檔案
COPY filebeat.yml /etc

COPY run.sh /home
WORKDIR /home
# 在 run.sh 中啟動 SDK 程式和 Filebeat 程式
CMD ["/bin/bash","-e","run.sh"]
CODE
  • run.sh 中的内容:
#!/bin/bash
nohup java -jar javasdk.jar > /dev/null 2>&1 &
nohup filebeat-7.2.0-linux-x86_64/filebeat -c /etc/filebeat.yml > /dev/null 2>&1 &

while [[ true ]]; do
    sleep 10000
done
CODE

在容器中要確保 SDK 的 log 寫入路徑與 Filebeat 的 log 讀取路徑相同。

  • 以 Java SDK 為例:
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
// 將數據輸出到 /data/sa_log 下的 service_log 開頭的檔案中,每天一個檔案
final SensorsAnalytics sa = new SensorsAnalytics(
        new SensorsAnalytics.ConcurrentLoggingConsumer("/data/sa_log/service_log"));

// 使用神策分析記錄用戶行為數據
sa.track(distinctId, true, "UserLogin");
sa.track(distinctId, true, "ViewProduct");

// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
JAVA
  • Filebeat 設定範例:
# Filebeat 收集 /data/sa_log 目錄下所有以 service_log. 開頭的數據檔案
filebeat.inputs:
- type: log
  paths:
    - /data/sa_log/service_log.*

# 將數據傳送至網址為 10.42.32.70:5044 或 10.42.50.1:5044 的 Logstash
output.logstash:
  hosts: ["10.42.32.70:5044","10.42.50.1:5044"]
YML
  • 啟動命令參考範例:
docker run -d --name sdk-beat \
-v ~/local/filebeat/filebeat.yml:/filebeat.yml \
sdk-beat
CODE

5.2.2. 方案二:SDK 使用共享數據保存 log Filebeat 進行讀取

後端 SDK 和 Filebeat 分別運作在不同的容器上,SDK 將產生的 log 存放在數據卷上,Filebeat 從數據卷內讀取數據傳送至部署好的 Logstash 。

  • 優點:不会侵入原有的 SDK 容器。
  • 缺點:使用起來比較麻煩。

首先,建立一個數據卷選擇您喜歡的儲存方式,下面以本地硬碟為例,要確保你的容器對該數據卷有寫權限:

docker volume create sa-log
CODE

啟動後端 SDK 容器,將產生 log 目錄掛載到數據卷上。

docker run -d --name sdk \
--mount source=sa-logs,target=/your/logs/path \
your-sdk-image
CODE

啟動 Filebeat 容器,將日誌讀取目錄掛載到數據卷上。同時將存放檔案讀取進度的目錄也掛載到數據卷上,以每一個數據卷為一個讀取進度,當重啟 Filebeat 時重複使用該進度即可繼續執行傳送。

  • 設定檔案 filebeat.yml 範例:
filebeat.inputs:
- type: log
  paths:
    - /usr/share/filebeat/input/service_log.*

# 將數據傳送至網址為 10.42.32.70:5044 或 10.42.50.1:5044 的 Logstash
output.logstash: 
  hosts: ["10.42.32.70:5044","10.42.50.1:5044"]
YML

將讀取目錄和進度目錄同時掛載到數據卷上。

docker run -d --name filebeat \
--mount source=sa-logs,target=/usr/share/filebeat/input \
--mount source=sa-logs,target=/usr/share/filebeat/data/ \
-v ~/docker_workspace/filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml \
docker.elastic.co/beats/filebeat:7.2.0
CODE

如果想用多個 SDK 容器掛載同一個數據卷的,建議容器以環境變數 HOSTNAME 為路徑名存放 log 檔,再將上級目錄掛載到數據卷上。

  • 舉個例子:

容器內 log 的輸出路徑:/mount/${HOSTNAEM}-logs/service_log.20190708

將容器中的 /mount 目錄掛載至 Volume 。

因此 Volume 中日誌的目錄的存放格式為:

|-- Volume
| |-- c1369239e7ba-logs
| |-- fcdfdb3bdb2b-logs
| | |-- service_logs.20190702
| | |-- service_logs.20190703
| |-- da86e6ba6ca1-logs
| | |-- service_logs.20190701
| | |-- service_logs.20190702
| | |-- service_logs.20190703
CODE

更改 Filebeat 的檔案讀取路徑為:

filebeat.inputs:
- type: log
  paths:
    - /usr/share/filebeat/input/*/service_log.*
CODE

以 Java SDK 為例產生帶有 HOSTNAME 的路徑存放 log:

// 取得 HOSTNAME
String hostname = System.getenv("HOSTNAME");
File logPath = new File("/mount/" + hostname + "-logs/");
if (!logPath.exists()) {
    logPath.mkdirs();
}
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
// 將數據輸出到 /mount/${HOSTNAME}-logs/ 目錄下以 service_log 為開頭保存,運作容器時將 /mount 目錄掛載到宿主機器上
final SensorsAnalytics sa = new SensorsAnalytics(
        new SensorsAnalytics.ConcurrentLoggingConsumer(logPath.getAbsolutePath() + "/service_log"));

// 使用神策分析記錄用戶行為數據
sa.track(distinctId, true, "ViewProduct");

// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
JAVA

如果您不希望更改原容器的 log 路徑存放方式,可以在容器啟動時建立一條軟連指向 log 目錄,將軟連掛載到 Volume 上。

rm -rf /your/logs/path \
&& mkdir -p /mount/${HOSTNAME}_logs \
&& ln -s /mount/${HOSTNAME}_logs /your/logs/path \
&& bin/sdk start
CODE

在啟動容器時將 /mount 掛載到 Volume 中。

docker run -d --name sdk \
--mount source=sa-logs,target=/mount \
your-sdk-image
CODE

6. 使用 K8s(Kubernetes)自動編排容器場景下的數據採集

6.1. Logstash 部署

為確保 Logstash 的穩定工作,建議直接部署 Logstash 在伺服器上,下文為 K8s 的部署方式供參考。
如果您已經在使用 Logstash 做一些其他的 log 收集工作請參考 Logstash + Filebeat#Logstash 設定。為避免容器意外關閉導致遺失數據,請設法保存緩衝區內的數據。

註冊一份 Logstash 設定檔案,使用 Filebeat 作為輸入,sensors_analytics 作為輸出,並指定運作設定。

  • 設定檔案 logstash-conf.yaml 參考範例:
apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-set
  labels:
    sa-app: logstash
data:
  logstash.yml: |-
    http.host: 0.0.0.0
    pipeline.workers: 1
    queue.type: persisted
    # 用於限制緩衝佇列的大小預設值為 1024MB ,該數值在設定時應該小於 Pod 使用的儲存卷大小。
    queue.max_bytes: 900mb
    queue.drain: true
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-pipe-conf
  labels:
    sa-app: logstash
data:
  logstash.conf: |-
    input {
      beats {
        port => "5044"
      }
    }

    output {
      sensors_analytics {
          url => "http://10.42.34.189:8106/sa?project=default"
      }
    }
YML

為了不遺失數據,使用了基於硬碟的數據緩衝佇列 (queue.type: persisted),所以需要在容器外保存 Logstash 的進度資訊,這樣在重啟 Logstash 的時候可以繼續完成傳送。

建議透過 StatefulSet 的方式進行部署從而保存 Logstash 的狀態。

首先,建立一個 StorageClass 用於產生保存進度的 PV ,設定手動回收,下面以 NFS 為例。

  • logstash-sc.yaml 的参考範例如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: logstash-nfs-storage
provisioner: nfs-provisioner
reclaimPolicy: Retain


YML

然後,建立一個 StatefulSet 應用 logstash-nfs-storage ,透過 Headless Service 來為每個 Logstash Pod 提供網絡連接方式。

  • logstash-sts.yaml 参考範例如下:
apiVersion: v1
kind: Service
metadata:
  name: logstash
  labels:
    app: logstash
spec:
  ports:
  - port: 5044
    name: beat-in
  clusterIP: None
  selector:
    app: logstash
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: logstash
spec:
  # 使用上面的 Headless Service
  serviceName: "logstash"
  selector:
    matchLabels:
      app: logstash
  replicas: 3
  template:
    metadata:
      labels:
        app: logstash
    spec:
      containers:
      - name: logstash
        image: sensorsdata/logstash:7.2.0
        ports:
        - containerPort: 5044
          name: beat-in
        volumeMounts:
        - name: logstash-pipe-conf
          mountPath: /usr/share/logstash/pipeline/logstash.conf
          subPath: logstash.conf
        - name: logstash-set
          mountPath: /usr/share/logstash/config/logstash.yml
          subPath: logstash.yml
        # 容器中 /usr/share/logstash/data 目錄下保存著緩衝佇列 ,與進度資訊。
        - name: ldata
          mountPath: /usr/share/logstash/data
      volumes:
      - name: logstash-pipe-conf
        configMap:
          name: logstash-pipe-conf
      - name: logstash-set
        configMap:
          name: logstash-set
  volumeClaimTemplates: # Logstash 進度數據使用的 PVC 模板
  - metadata:
      name: ldata
    spec:
      accessModes: [ "ReadWriteOnce" ]
      # 使用的儲存類名稱,需要提前建立。
      storageClassName: "logstash-nfs-storage"
      resources:
        requests:
          # 大小要高於緩衝佇列的最大長度限制
          storage: 1Gi
YML

StatefulSet 建立完成後 Pod name 的產生規則為 StatefulSetName - Pod - 序號

上面的設定檔案會產生 logstash-0、logstash-1,logstash-2 這樣命名的 Pod。 Pod 副本也是按照序號 0 到 N-1 的順序依次進行建立的,在刪除時是按照序號 N-1 到 0 依次刪除。

Headless Service 為控制的每個 Pod 副本建立了一個DNS 域名,完整的域名規則為:(pod name).(headless server name).namespace.svc.cluster.local,因此 Filebeat 是透過網域來尋找Logstash 的,而不是IP 。當使用預設的 namespace 時可省略 namespace.svc.cluster.local

StatefulSet 根據 volumeClaimTemplates,為每個 Pod 建立一個 PVC,PVC 的命名字首為:namespace-volumeMounts.name - volumeClaimTemplates.name - pod_name,刪除一個 Pod 副本不會刪除 PVC ,在重啟後新的 Pod 會重複使用之前 PVC 中的進度繼續完成傳送。

建立完成後檢查一下運作的情況:

kubectl get pods -l app=logstash
NAME READY STATUS RESTARTS AGE
logstash-0 1/1 Running 0 3h56m
logstash-1 1/1 Running 0 3h56m
logstash-2 1/1 Running 0 3h56m
CODE

查看一下數據卷的建立情況:

kubectl get pvc -l app=logstash
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
ldata-logstash-0 Bound pvc-c1833d35-d2ee-49a5-ae16-3a9d3227ebe5 1Gi RWO logstash-nfs-storage 3h56m
ldata-logstash-1 Bound pvc-9aa4b50c-45f7-4b64-9e4d-056838906675 1Gi RWO logstash-nfs-storage 3h56m
ldata-logstash-2 Bound pvc-95bcdbf0-e84d-4068-9967-3c69c731311b 1Gi RWO logstash-nfs-storage 3h56m
CODE

檢查一下叢集內部的 DNS 建立情況:

for i in 0 1; do kubectl exec logstash-$i -- sh -c 'hostname'; done
logstash-0
logstash-1
logstash-2

kubectl run -i --tty --image busybox:1.28.3 dns-test --restart=Never --rm /bin/sh
nslookup logstash-0.logstash
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: logstash-0.logstash
Address 1: 10.244.7.54 logstash-0.logstash.default.svc.cluster.local

nslookup logstash-1.logstash
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: logstash-1.logstash
Address 1: 10.244.5.150 logstash-1.logstash.default.svc.cluster.local


nslookup logstash-2.logstash
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: logstash-2.logstash
Address 1: 10.244.34.177 logstash-2.logstash.default.svc.cluster.local
CODE
  • Logstash 的擴容/縮容

StatefulSet 的更新策略也是有順序的。

將之前設定的 StatefulSet 容量從 3 變成 5 。

kubectl scale sts web --replicas=5
kubectl get pods -l app=logstash
NAME READY STATUS RESTARTS AGE
logstash-0 1/1 Running 0 6h1m
logstash-1 1/1 Running 0 6h1m
logstash-2 1/1 Running 0 6h1m
logstash-3 1/1 Running 0 1h3m
logstash-4 1/1 Running 0 1h3m
CODE

新增的 Pod 在原有的基礎上序號遞增。

將 StatefulSet 容量從 5 變回 3 。

kubectl scale sts web --replicas=3
CODE

之前新增的 PVC 不會被刪除,當下次達到該容量時會繼續重複使用。不用擔心有 Filebeat 會向被刪除的 Logstash 傳送數據,Filebeat 會自行尋找另一個運行正常的 Logstash。
由於設定了 queue.drain: true 所以撤除的 Logstash 在關閉前會將緩衝區內的數據傳送完畢。

6.2. 部署 Filebeat

6.2.1. 方案一:將 Filebeat 與 後端 SDK 封裝在同一個 Pod 裡採集 log 檔(推薦)

將 Filebeat 容器與能夠產生 log 的後端 SDK 容器設定在同一個 Pod 裡,後端 SDK 將 log 寫入 emptyDir 中,由 Filebeat 進行讀取並傳送至 Logstash。

  • 優點:部署方便,哪個 Pod 裡有 log 就在哪個 Pod 裡新增一個 Filebeat。
  • 缺點:與 SDK Pod 有耦合,Filebeat 的數量可能較多,稍顯冗餘。

神策分析各後端語言的 SDK 都支援將數據寫入檔案,例如: Java SDK 的 ConcurrentLoggingConsumer,PHP SDK 的 FileConsumer,Python SDK 的 LoggingConsumer。

  • 以 Java SDK 為例:
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
// 將數據輸出到 /data/sa_log 下的 service_log 開頭的檔案中,每天一個檔案
final SensorsAnalytics sa = new SensorsAnalytics(
        new SensorsAnalytics.ConcurrentLoggingConsumer("/data/sa_log/service_log"));

// 使用神策分析記錄用戶行為數據
sa.track(distinctId, true, "UserLogin");
sa.track(distinctId, true, "ViewProduct");

// 程式結束前,停止神策分析 SDK 所有服務
sa.shutdown();
JAVA

以上設定將在 /data/sa_log 目錄下產生數據檔案,一天一個檔案,檔案列表如下:

service_log.20170923
service_log.20170924
service_log.20170925
YML

在部署 Pod 時首先將 SDK 容器中的 /data/sa_log 目錄下的內容掛載到 emptyDir: {} 上。然後設定 Filebeat 的讀取的檔案目錄為:/var/log/containers/service_log.* 。 Filebeat 將會讀取該目錄下所有以 service_log. 開頭的檔案。最後把 Filebeat 容器的 /var/log/containers/ 目錄也掛載到 emptyDir: {} 上,運行時即可讀取 SDK 容器產生的 log 檔。

  • 部署檔案 pod.yaml 参考範例:
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config-in
  labels:
    sa-app: filebeat
data:
  filebeat.yml: |-
    filebeat.inputs:
    - type: log
      # 讀取 /var/log/containers 目錄下以 service_log 開頭的檔案。
      paths:
        - /var/log/containers/service_log.*

    output.logstash:
      # 叢集內網 Logstash
      hosts: ["logstash-0.logstash:5044","logstash-1.logstash:5044"]
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: javasdk-beat
  labels:
    sa-app: javasdk-beat
spec:
  replicas: 3
  template:
    metadata:
      name: javasdk-beat
      labels:
        sa-app: javasdk-beat
    spec:
      containers:
      - name: javasdk
        image: javasdk:20190705
        command: ["/bin/bash", "-c"]
        args:
        - "bin/javasdk start"
        volumeMounts:
        - name: log-path
          # /data/sa_log 為後端 SDK 存放 log 的目錄,掛載到 emptyDir 上
          mountPath: /data/sa_log
      - name: filebeat
        image: docker.elastic.co/beats/filebeat:7.2.0
        args: [
          "-c", "/etc/filebeat.yml",
          "-e",
        ]
        volumeMounts:
        - name: config
          mountPath: /etc/filebeat.yml
          readOnly: true
          subPath: filebeat.yml
        - name: log-path
          # 檔案讀取目錄也掛載到 emptyDir 上
          mountPath: /var/log/containers
          readOnly: true
      volumes:
      - name: log-path
        emptyDir: {}
      - name: config
        configMap:
          name: filebeat-config-in
YML

6.2.2. 方案二:Filebeat 部署在 K8s 節點上採集 log 檔

Filebeat 以 DaemonSet 的方式部署在 K8s 節點上收集 log 數據。節點上運作的後端 SDK 統一將 log 存放在宿主機的指定目錄內由 Filebeat 進行讀取並傳送至 Logstash。

  • 優點:Filebeat 部署方便,與 SDK Pod 無耦合。
  • 缺點:需要解決目錄問題,在宿主機上會存在額外的 log 檔案。

考慮到在同一宿主機上可能存在多個相同的後端 SDK 容器,因此需要使每個容器在向宿主機目錄寫入 log 的時候使用不同的目錄。建議在啟動容器時使用系統環境變數 HOSTNAME 作為路徑名存放 log 檔,然後將上一級目錄掛載到宿主機目錄上。

  • 舉個例子:

容器内 log 的輸出路徑:/mount/${HOSTNAEM}-logs/service_log.20190708

宿主機存放 log 的路徑:/home/data/javasdk_logs/

將 /mount 掛載至 /home/data/javasdk_logs/ 下。

因此宿主機的 /home/data/javasdk_logs/ 目錄下存放的内容大致如下:

[root@node-1 javasdk_logs]$ pwd
/home/data/javasdk_logs/
[root@node-1 javasdk_logs]$ ls -l
drwxr-xr-x 2 root root 22 Jul 8 12:06 javasdk-7d878c784d-5fpjz_logs
drwxr-xr-x 2 root root 22 Jul 6 18:33 javasdk-7d878c784d-7xmbb_logs
drwxr-xr-x 2 root root 22 Jul 6 18:52 javasdk-7d878c784d-vv9fz_logs
drwxr-xr-x 2 root root 22 Jul 8 12:08 javasdk-7d878c784d-w7q65_logs
drwxr-xr-x 2 root root 22 Jul 8 11:19 javasdk-7d878c784d-wkvxd_logs
[root@node-1 javasdk_logs]$ cd javasdk-7d878c784d-5fpjz_logs
[root@node-1 javasdk-7d878c784d-5fpjz_logs]$ ls -l
-rw-r--r-- 1 root root 6592991 Jul 8 23:59 service_log.20190706
-rw-r--r-- 1 root root 4777188 Jul 8 23:58 service_log.20190707
-rw-r--r-- 1 root root 137778 Jul 8 12:03 service_log.20190708
CODE
  • 以 Java SDK 為例在保存 log 時以 HOSTNAME 做為路徑,參考如下:
// 取得 HOSTNAME
String hostname = System.getenv("HOSTNAME");
File logPath = new File("/mount/" + hostname + "-logs/");
if (!logPath.exists()) {
    logPath.mkdirs();
}
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
// 將數據輸出到 /mount/${HOSTNAME}-logs/ 目錄下以 service_log 為開頭保存,運作容器時將 /mount 目錄掛載到宿主機上
final SensorsAnalytics sa = new SensorsAnalytics(
        new SensorsAnalytics.ConcurrentLoggingConsumer(logPath.getAbsolutePath() + "/service_log"));

// 使用神策分析記錄用戶行為數據
sa.track(distinctId, true, "ViewProduct");

// 程式结束前,停止神策分析 SDK 所有服務
sa.shutdown();
JAVA
  • 參考的 javasdk.yaml 檔案設定:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: javasdk
  labels:
    k8s-app: javasdk
spec:
  replicas: 3
  template:
    metadata:
      name: javasdk
      labels:
        k8s-app: javasdk
    spec:
      containers:
      - name: javasdk
        image: java-sdk-host:0715
        command: ["/bin/bash", "-c"]
        args:
        - "bin/javasdk start"
        volumeMounts:
        - name: logfile
          mountPath: /mount
      volumes:
      - name: logfile
        hostPath:
          path: /home/data/javasdk_logs/
          type: DirectoryOrCreate
YML

如果您不希望更改原容器的 log 路徑存放方式,可以在容器啟動時建立一條軟鏈指向 log 目錄,將軟連掛載在宿主機即可。

  • 參考的 javasdk.yaml 檔案設定:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: javasdk
  labels:
    k8s-app: javasdk
spec:
  replicas: 3
  template:
    metadata:
      name: javasdk
      labels:
        k8s-app: javasdk
    spec:
      containers:
      - name: javasdk
        image: java-sdk:0712
        command: ["/bin/bash", "-c"]
        args:
        - "rm -rf /your/logs/path
        && mkdir -p /mount/${HOSTNAME}_logs
        && ln -s /mount/${HOSTNAME}_logs /your/logs/path
        && bin/javasdk start"
        volumeMounts:
        - name: logfile
          mountPath: /mount
      volumes:
      - name: logfile
        hostPath:
          path: /home/data/javasdk_logs/
          type: DirectoryOrCreate
YML

將Filebeat 匹配的路徑設定為 /home/data/javasdk_logs/*/service_log.*,並且把Filebeat 存放進度的目錄也掛載在宿主機上,這樣在重啟 DaemonSet 的時候節點上的Filebeat 會繼續之前的傳送進度。

  • DaemonSet 設定檔案 filebeat-ds.yaml 参考如下:
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config
  labels:
    sa-app: filebeat
data:
  filebeat.yml: |-
    filebeat.inputs:
    - type: log
      paths:
        # 採集 service_log 開頭的 log 檔
        - /var/log/containers/*/service_log.*

    output.logstash:
      # 部署好的 Logstash
      hosts: ["logstash-0.logstash:5044","logstash-1.logstash:5044"]
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: filebeat
  labels:
    sa-app: filebeat
spec:
  template:
    metadata:
      labels:
        sa-app: filebeat
    spec:
      serviceAccountName: filebeat
      terminationGracePeriodSeconds: 30
      containers:
      - name: filebeat
        image: docker.elastic.co/beats/filebeat:7.2.0
        imagePullPolicy: IfNotPresent
        args: [
          "-c", "/etc/filebeat.yml",
          "-e",
        ]
        volumeMounts:
        - name: config
          mountPath: /etc/filebeat.yml
          readOnly: true
          subPath: filebeat.yml
        - name: inputs
          mountPath: /var/log/containers
          readOnly: true
        - name: data
          mountPath: /usr/share/filebeat/data
      volumes:
      - name: config
        configMap:
          name: filebeat-config
      - name: inputs
        hostPath:
          path: /home/data/javasdk_logs/
      - name: data
        hostPath:
          path: /home/data/filebeat_data/
          type: DirectoryOrCreate
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: filebeat
subjects:
- kind: ServiceAccount
  name: filebeat
  namespace: default
roleRef:
  kind: ClusterRole
  name: filebeat
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: filebeat
  labels:
    sa-app: filebeat
rules:
- apiGroups: [""]
  resources:
  - namespaces
  - pods
  verbs:
  - get
  - watch
  - list
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: filebeat
  namespace: default
  labels:
    sa-app: filebeat
CODE

7. 更新日誌

7.1. 0.1.2

  1. 可同時設定多個數據接收網址。
  2. 新增每分鐘一次的狀態 log(傳送速度、已接收、已傳送,報錯)。
  3. 支援使用 Filebeat 作為輸入時可在 log 中印出被讀檔案的讀取情況。