# 使用 APM Server 來收集 APM 數據

### 本篇學習重點

* 更深入的了解 APM Server
* 如何安裝以及設定 APM Server
* APM Server 的校能調校技巧

## APM Server 概觀

![20-apm-architecture-diy](https://i.imgur.com/pXdxEB3.png)

APM Server 的定位，是用來專門收集 APM Agents 所發送的資料，身為同樣是**收集資料**的服務之一，APM Server 被 Elastic 歸類在 Beats 的生態圈之中，因此 APM Server 就是使用 `libbeat` 所開發出來的產品，如此一來 APM Server 就擁有 Beats framework 所開發出來針對資料收集處理的各種能力與機制。

### APM Server 的任務

* 負責收集 APM Agents 所傳送出來的 APM 資料。
* 將收集到的資料，進行基本的驗證。
* 將收集到的資料，進行加工處理，轉換成後續易於分析使用的格式。
* 負責將資料傳送到 Elasticsearch 儲存。
* 負責在 Elasticsearch 安裝 Ingest Pipeline 設定，讓資料匯入至 Elasticsearch 時，能經由 Ingest Pipeline 進行處理。
* 負責向 Elasticsearch 設定 APM 的 Index Template、ILM (Index Lifecycle Management) Policy。
* 如果 Elasticsearch 在某段時間無法承受大量的資料寫入時，APM 擁有的 Beats framework 當中的 queue 機制，會扮演 APM Agents 與 Elasticsearch 之間資料緩存的角色。

### 為何要有 APM Server 這個獨立存在的元件？

* 讓 APM Agents 輕量化，因為 Agents 是要安裝在『服務』或『應用程式』之中，有些功能可以不用存在於 Agents 身上。
* 由於 APM Server 的設計是 stateless (無狀態) 的，所以可以輕易的 scale out (向外擴展)，提升接收及處理大量 APM 資訊的能力。
* Elasticsearch 是架構中的元件，就像是資料庫一樣，不適合直接讓外部存取，APM Server 的存在，避免讓 APM Agents 直接存取到 Elasticsearch，因為有些 APM Agents 例如：RUM (Real User Monitoring) 是以 Javascript 的方式在 Browser 端執行，也就是在一般用戶端執行，若是直接存取 Elasticsearch 會有安全性上的疑慮。
* APM Server 可以掌控資料傳送進 Elasticsearch 的流量，避免 Elasticsearch 被過量的資料在短時間寫入，造成效能影響。
* 如果 Elasticsearch 發生問題，APM Server 可以緩存 APM Agents 送來的資料，避免增加 Agents 端的負擔，因為 Agents 有可能是安裝在我們的『服務』或『應用程式』之中，這樣會影響到『服務』或『應用程式』正常的運作。
* 在使用 RUM (Real User Monitoring) 時，Javascript 在前端有被 minify (縮小)，要在 APM 端能有效的解讀，會需要使用 Source Mapping 的定義，這樣在 APM UI 端時，所看到的資訊才能和原始檔對應起來，這個 Source Mapping 的對應的動作，就會在 APM Server 端處理。
* 經由定義專門接受 APM Data 的 JSON API，並且在 APM Agents 與 Elasticsearch 之中當作緩衝的角色，可以提升不同 Agents 版本與 Elasticsearch 之間的相容性，也就是某些版本有更動時，這些相容性的格式轉換，會在 APM Server 端處理掉。

## 如何安裝及使用 APM Server

### 安裝 APM Server

以下安裝的步驟，以自行安裝於 `MacOS` 環境為例：

1. 下載 APM Server 壓縮檔，並進行解壓縮

```
curl -L -O https://artifacts.elastic.co/downloads/apm-server/apm-server-7.15.0-darwin-x86_64.tar.gz
tar xzvf apm-server-7.15.0-darwin-x86_64.tar.gz
```

1. 進入目錄後，透過 APM Server 向 Elasticsearch 設定 Index Template、ILM (Index Lifecycle Management) Policy、Alias。

```
./apm-server setup --index-management
```

> `apm-server setup --pipelines` 這個指令可以省略，預設 APM Server 啟動時就會執行，當然也可以手動先設定好。

1. 在 `apm-server.yml` 中調整合適的配置設定。
2. 啟動 APM Server

```
./apm-server -e
```

1. 當 APM Server 啟動後，再來就是要安裝 APM Agents，讓 Agents 傳送收集的資料到 APM Server，APM Agents 的部份我們在下一個章節進行介紹。

### APM Server 常用設定

在使用 APM Server 時，在 `apm-server.yml` 有一些設定值可能會需要調整：

* `output.elasticsearch`：指定 Elasticsearch 的主機位置，或是 Security 相關的設定。
* `queue.mem.*`：Queue 的大小，如果 APM 收集的資料量較大、並且 APM Server 也配置較好的硬體規格時，這部份應該要有對應的調整。
* `max_procs`：如果對於 APM Server 能使用的 CPU 數量有要進行限制或調整的話，在此設定。
* `app-server.rum.enable`：如果要開啟 RUM (Real User Monitoring) 的功能，要特別設定啟用。(預設是關閉)
* `apm-server.kibana.*`：如果要透過 Kibana 來控制 APM Agents 的話，會需要設定這些配置。
* `logging.*`：要收集 APM 產生的 Logs 檔的話，要設定啟用寫入檔案，一般建議會啟用並配合 Filebeat 來收集 APM Server 的 Logs。
* `http.*`：如果我們要透過 Metricbeat 收集 APM Server 的 Metrics 時，會需要啟用 HTTP Endpoint，提供 Metricbeat 取得內部 Metrics 的 API。
* `apm-server.auth.anonymous.*`：當 RUM 設定啟用時，這個設定值也會自動被啟用，有些 `rate_limit` 的設定在量級較大的環境可能會需要被重新檢視設定。

## 多了解一點 APM Server

### APM Server 如何接收 APM Agents 的資料

APM Server 定義了一個 Events `Intake` 的 API，而 APM Agents 主要也就是使用這個 API，將我們先前介紹到的以下四種資料傳送給 APM Server：

* Transactions
* Spans
* Errors
* Metrics

`Intake` API 的 Endpoint 如下：

```
http(s)://{hostname}:{port}/intake/v2/events
```

RUM 有另外獨立的 Endpoints：

```
http(s)://{hostname}:{port}/intake/v2/rum/events
```

存取這個 API 使用的是 `HTTP POST`，並且如同 Elasticsearch `_bulk` API 的設計一樣，使用 [newline delimited JSON (NDJSON)](http://ndjson.org/) 的 `Content-Type` 來接收一次多筆 Events 的傳送，同時在回傳結果若有錯誤時，也會回傳每一個 Events 及獨立的錯誤資訊。

[官方文件 - APM Events API](https://www.elastic.co/guide/en/apm/server/current/events-api.html) 裡面有詳細的介紹四種資料各自的 Schema，有興趣的讀者可以參考。

### APM Server 效能調校技巧

針對 APM Server 的效能調校，這邊參考官方文件的介紹，有包含以下幾點：

#### 1. 調整 `output.elasticsearch` 的參數

包含以下三種調整方式：

* 適度的增加 `output.elasticsearch.worker` 的數量。
* 調大 `output.elasticsearch.bulk_max_size` 的數量，預設值 `50` 是蠻小的一個數字，如果硬體規格還不錯，甚至可以調高到 `5120` 來試試。
* 確認 `queue.mem.events` 的數量有被正確的設定是 `output.elasticsearch.worker` \* `output.elasticsearch.bulk_max_size` 的大小。

#### 2. 調整 Queue Size

透過調整 `queue.mem.events` 的大小，在 APM Server 使用更多的記憶體來緩存 APM Agents 所傳送進來的資料，如果為了能承受 Elasticsearch 發生一段時間無法正常運作，又要保持 APM Server 能接受 APM Agents 不斷傳送進來的資料時，可以從這邊下手。

#### 3. 增加 APM Server 的數量

如果發生 request timeouts 的錯誤時，通常是因為 APM Server 處理不了當下的資料量了，這時最簡單且有效的方式，就是增加 APM Server 的數量。

#### 4. 減少傳進 APM Server 資料的 Payload 大小

這部份要從 APM Agents 端下手，如果一次傳送到 APM Server 的資料量太大，有可能會發生 Request timeout，這時可以調小 `flush interval` 的設定，或甚至是降低 `sample rate` (取樣率)。

#### 5. 調整 Anonymous Auth 的 Rate Limit

當 APM Server 處理的量已經消化不完的時候，透過從 `Intake` API 進行節流，設定 `rate_limit.event_limit` 來限制一次能進來的資料量，能幫助 APM Server 有效的處理他能處理的資料量，整體的效能使用率會更佳。

#### 6. 記得刪除舊資料

預設 APM Server 建立的 ILM (Index Lifecycle Management) Policy 沒有包含刪除資料、或移到 Cold phase 等操作，這部份記得在使用 APM 時，也一樣要做好資料管理的規劃，避免 Elasticsearch 的資料隨時間不斷增長，最終導致資料量過多而影響服務的正常使用。

## 參考資料

1. [官方文件 - APM Server](https://www.elastic.co/guide/en/apm/server/current/overview.html)
2. [官方文件 - APM Events API](https://www.elastic.co/guide/en/apm/server/current/events-api.html)
