# 使用 Filebeat 應該要了解的設計細節與原理

### 本篇學習重點

* Filebeat 的設計架構與底層運作原理
* Filebeat Config 配置的一些建議與需注意的設定

## Filebeat 的運作架構與原理

由於實務上的使用情境有許多種變化，透過了解 Filebeat 運作運作架構與原理，能幫助我們在自己所需要的情境之下，能進行比較好的配置及使用，避免效能的浪費或甚至是因為誤用而造成非預期的結果。

### Filebeat 的運作原理

這是在上一篇文章我們有介紹到的 Filebeat 運作架構，接下來我們先深入的了解裡面的組成元素與運作細節。\[1]

![13-Filebeat-Architecture](https://i.imgur.com/BISE7FI.png)

#### Harvesters (收集器)

Harvester 的職責與運作特性如下：

* 每個檔案都會有專門負責的 Harvester 進行處理，並且將新增加的資料讀出，並且交給 `libbeat` 進行後續處理，最終透過 Outputs 的定義將資料往外傳送。
* 讀取檔案時，以 **行 (Line)** 為單位。
* Harvester 會負責實體檔案的 `open` 與 `close`，也就是說只要 Harvester 還在運作時，所負責讀取的檔案的 File descriptor (檔案描述器) 會一直保持 `open` 的狀態，因此如果 Harvester 在保持運作時，若某個檔案被移動或改名後，這個檔案後續增加的內容，依然會被 Harvester 讀取出來，也因此如果檔案被刪除的話，磁碟空間還是會一直被佔用著，直到 Harvester `close` 檔案之後，才會被釋放。
* 要讓 Harvester `close` 檔案的話，會依照 `close_inactive` 所設定的值，等到檔案持續指定的時間長度沒有寫入新資料之後，才會被關閉，而觸發檢查這個設定值會是依照 `scan_frequency` 所指定的時間週期。
* 被 `close` 的檔案，若是又有新的資料寫入，會等到 `scan_frequency` 下次執行的時間週期檢查到檔案時，才會繼續 `open` 並且讀取檔案。

#### Inputs (輸入端)

而 Inputs 的職責與運作特性如下：

* Inputs 負責管理需要被讀取的檔案或資料來源。
* Inputs 將要讀取的檔案及資料交給 Harvester 進行處理，也就是說 Harvesters 其實就是由 Inputs 在管理的。
* 當有設定多組 Inputs 時，每個 Input 擁有自己的執行緒 (Thread)，會併發進行處理。
* 以 `log` Input 為例，若是有指定多個 `paths` ，每個 `paths` 指定的檔案都會由 Input 負責找出來，並且每一個檔會交給一個 Harvester 去處理。
* 同一種類型的 Input 可以重覆被定義很多次，但要留意同一個檔案不應該被重覆指定，這樣可能會發生非預期的行為。
* Input 目前 Filebeat 有支援 20 幾種服務的實作，有興趣的可以參考 [官方文件 - Filebeat Inputs](https://www.elastic.co/guide/en/beats/filebeat/7.15/configuration-filebeat-options.html) \[2]。

> 早期叫 `prospector` ，6.3 版之後將 `prospector` 改成 `input`

#### Spooler (緩衝處理器)

**Spooler** 其實指的是 **Queue**，也就是 `libbeat` 裡面所實作的機制，由 harvesters 所讀取出的資料，會以 events 的方式，透過 events channel 傳送給 `libbeat` ，並且在 `libbeat` 當中透過各種 Pipeline 的方式進行後續的處理，像是如果有定義 `processors` 就會在 `pipelineProcessors` 面進行指定的處理，而 `libbeat` 裡的 **Publisher** ，就負責將 event 最後透過 Output 的定義所往後傳送的機制，這邊是以 **At Least Once** 的方式來實作，也就是會確保資料有被正確的傳送出去，如果沒有收到正確傳送出去的回覆，就會不斷的重試，最終會透過 **Registrar** 將結果寫入到 `Registry` 檔案之中。

> 有興趣了解 Filebeat 與 libbeat 底層實作的，可以參考 [Filebeat 原始碼淺析](https://www.gushiciku.cn/pl/g0kn/zh-tw) \[3]。

#### Outputs (輸出)

Output 這邊是針對 Filebeat 所支援的對外接口種類有各別的實作，目前 Filebeat 的 Output 有提供 6 種 (Elasticsearch, Logstash, Kafka, Redis, File, Console)，如果有定義要批次處理，例如 Elasticsearch Output 會使用 `bulk` API，這個處理就會實作在 Output 當中，另外如果 Filebeat 在關閉時，還沒有成功取得 Output 所發送出去的回應時，Filebeat 不會等待，會直接關閉，一但 Filebeat 重新啟動時，這個 events 就會被重新傳送。

### Filebeat 如何記錄及處理要傳送的檔案

Filebeat 會將每個處理過的檔案，記錄在他的 `Registry` 檔裡面，預設是存放在 `${path.data}/registry/` 的目錄裡。

裡面會有個 `json` 格式的檔案，記錄每一個曾被讀取過的檔案，而如果是保持在 `open` 的檔案，會另外獨立存在一個 `active` 的檔案之中，同時會記錄 `inode` 等實體 disk 的資訊，Filebeat 會持續在記憶體更新每個檔案被讀取過的位置，並且等到資料成功透過 Output 傳送出去之後，便會將記憶體中的記錄寫到 Disk 中，而如果 Filebeat 程式異常中止的話，也會在重新啟動的時候，從 `Registry` 裡將記錄讀出，就能夠知道要繼續從檔案的哪個位置往後讀取。

由於 log 檔名可能會被修改，檔案也可能會搬位置，因此 Filebeat 會以 Disk `inode` 資訊產生另外的 Unique ID 並記錄在 Registry 之中，用以評估檔案是否在先前有被處理過，以防止檔案改名或搬位置之後，被重覆的讀取。

### Filebeat 如何確保需傳送的資料不會漏掉

Filebeat 能確保資料 `At Least Once` 的使命必達，不會漏掉，是因為他會將傳送的狀態記錄在 `Registry` 檔案裡，所以發生錯誤沒辦法成功的傳送，就會透過前面所介紹的 `libbeat` 裡的 **Registrar** 進行重送，同樣的如果 Filebeat 非正常的關閉，也會在重新啟動的時候，透過 `Registry` 將檔案處理的狀態給恢復。

## Filebeat 的 Config 配置方式

這邊會將一些 Config 配置上，實務上會需要留意及較常會使用到的部份與大家進行介紹。

### 一般設定

這部份是設定在 `filebeat.yml` 當中：

* `retistry.flush` ：預設是 `0s`，也就是只要 output 成功寫出，就會執行 `flush` 。如果有非常多的 Logs 同時在處理很大量的資料的情境下，太過頻繁的寫入 `Registry` 檔案會拖慢整個執行的速度，這時就應該將 `registry.flush` 設定 `>0s`。
* `shutdown_timeout`：預設 Filebeat 在關閉程式時，不會等待 publisher 確認 Output 的結果，這樣在大量資料不斷處理的情況下，會常有機會發生關閉 Filebeat 當下的資料，在重新啟動時重覆發送的情況，我們可以設定這個值，讓關閉的時候稍微多等待 Output 的結果，以減少重送的情況發生。
* `tags` 與 `fields`：這兩個值能夠有效的幫我們分類資料的來源，善用資料來源的標示與分類，可以幫助我們在後續的資料分析。
* `processors`：這個功能能讓我們透過 Filebeat 所收集的資料，在往後送之前，進行一些簡單的加工處理，例如我們想要依照一些條件刪掉不需要的 event、使用我們自訂義的 ID 欄位，讓重覆的資料進入 Elasticsearch 時能夠被 deduplicate (去重覆)、把某個 JSON 的字串解析出來並取得當中的值…等。
* `queue.disk`：如果你透過 Filebeat 在處理的資料量很大，並且希望能夠在 **Spooler** 當中先將較大量的資料進行彙總再往後傳送，預設在 memory 當 queue 的配置，可能承受不了太量的資料彙總，這時就可以考慮將 queue 寫在 disk 中。又或著是我們透過 Filebeat 要往後傳送的資料，非常重視資料的可靠性，同時我們又有指定 `flush.min_events` 和 `flush.timeout` 要使用較大量的 queue，這時也會要考慮將 queue 指定到實體的 disk 之中。\[4]

### 實務上的配置技巧 - 切分設定檔

由於實務佈署上，我們有多台的機器時，相同性質的機器的 filebeat 的配置會是一樣的，這時候我們可以將 config 切分出來，也能夠讓我們在做組態管理時更容易，至於 filebeat 能切分的設定檔主要有以下兩種：

#### Input Config

```
filebeat.config.inputs:
  enabled: true
  path: inputs.d/*.yml
```

存放在 `inputs.d` 裡面的 `yml` 檔案格式，會定義如下方的例子：

```
- type: log
  paths:
    - /var/log/mysql.log
  scan_frequency: 10s

- type: log
  paths:
    - /var/log/apache.log
  scan_frequency: 5s
```

#### Module Config

```
filebeat.config.modules:
  enabled: true
  path: ${path.config}/modules.d/*.yml
```

這裡面的檔案格式如下：

```
- module: apache
  access:
    enabled: true
    var.paths: [/var/log/apache2/access.log*]
  error:
    enabled: true
    var.paths: [/var/log/apache2/error.log*]
```

而建議的 Config 檔案管理方式，一般有二種做法：

* 如預設的配置，以 module 名稱來切分檔案，不同的 module 有各自的 `yml` 設定檔。
* 以服務角色來切分，例如 web server、backend service、DB server…等，並在檔案中定義所有該服務使用到的 modules。

### 其他 Input, Output, Module 的配置

在其他 Input, Output Module 的配置上，在不同的情境下，也都有不同的配置建議設定，由於所支援的部份太多，這邊無法一一細談，會建議大家在使用之前，一定要先閱讀過官方文件的資訊，至少先把有哪些設定值看過一遍，再依照情境去判斷可能會需要調整的部份。

如果你也是使用 Filebeat 在讀取實體的 Log 檔案，至少 [官方文件 Input - Log](https://www.elastic.co/guide/en/beats/filebeat/7.15/filebeat-input-log.html) 這份的設定你應該要看過，要知道 `close_*`、`scan_frequency`、`harvester_limit` 等配置是做什麼用的，對於效能的調效也會有所幫助。

## 參考資料

1. [官方文件 - How Filebeat Works](https://www.elastic.co/guide/en/beats/filebeat/7.15/how-filebeat-works.html)
2. [官方文件 - Filebeat Inputs](https://www.elastic.co/guide/en/beats/filebeat/7.15/configuration-filebeat-options.html)
3. [Filebeat 原始碼淺析](https://www.gushiciku.cn/pl/g0kn/zh-tw)
4. [官方文件 - Filebeat Internal Queue](https://www.elastic.co/guide/en/beats/filebeat/7.15/configuring-internal-queue.html)
