# 使用 Metricbeat 掌握 Infrastructure 的健康狀態 Docker 篇

### 本篇學習重點

* 如何使用 Docker 來佈署 Metricbeat
* 使用 Metricbeat 在 Docker 環境中，如何取得宿主實體機器或是整體 Docker Containers 的系統 Metrics
* 如何透過 Metricbeat 輕鬆的來掌握 Docker 環境內的各 Containers 運作服務的健康狀態

## 使用 Docker 部署 Metricbeat

這篇要介紹的主題是 **Docker** ，因此這邊我們先來介紹如何使用 Docker 來部署 Metricbeat。\[1]

### Docker Image

Elastic 官方在 [Docker @ Elastic](https://www.docker.elastic.co/) 發佈了兩種版本的 Metricbeat Docker Image，底層是使用 CentOS 7。

* beats/metricbeat
* beats/metricbeat-oss

差別是什麼，老實說我稍微查了一下找不出差異，官方說授權的部份參考 [Subscription](https://www.elastic.co/subscriptions) 頁面，不過裡面針對 Metricbeat 的部份沒有描述到不同，不過大概可以確定的是 X-Pack 基本上是 Elastic License 的功能，所以 OSS 這個 Open Source 的版本應該是沒有 X-Pack 的功能的，至於其他的部份可能要再另外深入挖掘才知道。

不過由於 Elastic License 目前已經很寬鬆了，除了是要提供 SaaS 的服務，不然不太會踩到 License 的問題，接下來會用一般 Elastic License 的版本來操作。

可以透過 `docker pull` 取得 Docker Image，讓這個 image 下載到本機端。

```
docker pull docker.elastic.co/beats/metricbeat:7.15.0
```

### 透過 Docker 環境執行 Metricbeat 的 Setup

由於 Metricbeat 在第一次運行之前，我們會需要執行下面兩個動作：

* 在 Elasticsearch 設定好 Metricbeat 要使用的 Index Template
* 在 Kibana 匯入 Metricbeat 內建好的 Dashboard

這兩動作我們需要執行 `./metricbeat setup` 的使令，所以在 Docker 的環境中，我們也要先單獨執行一次這個指令：

```
docker run \
docker.elastic.co/beats/metricbeat:7.15.0 \
setup -E setup.kibana.host=kibana:5601 \
-E output.elasticsearch.hosts=["elasticsearch:9200"]
```

這邊的 `kibana:5601` 與 `elasticsearch:9200` 的位置，要自己視情況修改成正確的 Kibana 與 Elasticsearch 的位置。

### 使用 Docker 運行 Metricbeat

要開始運作 Metricbeat 也就是執行 `docker run` 並執行 `metricbeat -e` 的 Command：

```
docker run -d \
  --name=metricbeat \
  --user=root \
  --network=testnet \
  --volume="$(pwd)/metricbeat.docker.yml:/usr/share/metricbeat/metricbeat.yml:ro" \
  --volume="/var/run/docker.sock:/var/run/docker.sock:ro" \
  docker.elastic.co/beats/metricbeat:7.15.0 metricbeat -e \
  -E output.elasticsearch.hosts=["elasticsearch:9200"]  
```

啟動時有幾個參數可能會需要設置：

* `--user=root`: 如果有使用 System Module 時，有些 socket 或是 system process 的資訊，會需要有足夠的權限才能存取，這部份的權限管理會需要留意。
* `--volume`: 若是有獨立準備 `metricbeat.yml` 的 config 檔，會需要 mount 到 `/usr/share/metricbeat/metricbeat.yml` 的路徑上。
* `-E coutput.elasticsearch.hosts=`: 如果沒有在 `metricbeat.yml` 特別設定，要使用環境變數指定 Elasticsesarch 的位置時，也要記得加上。
* `--net=`: 如果有要透過 Metricbeat 去收集其他運作在 Docker Container 內服務的 Metrics，要記得 Network 的部份能夠存取得到。

## 在 Docker 環境中，Metricbeat 的設定方式

使用 Metricbeat 運行在 Docker Container 之中時，一般會有三大類要收集的 Metrics：

1. 運行 Container 的實體主機的 System Metrics
2. 每個 Docker Containers 的系統 Metrics
3. 其他 Docker Containers 的服務 Metrics

以下我們分別來說明這些配置上的方式。

### 讓 Metricbeat 在 Docker Container 內取得實體主機的 System Metrics

由於 System Module 針對不同的 Metricset 會從幾個不同的系統位置取得資訊：

* `/proc`: System Module 的許多資訊其實是來自這個 Linux proc filesystem 的位置。
* `/sys/fs/cgroup`: System process 的 metricset 會從這個位置取得 process 的資訊。
* `/proc/net/dev`: System network 的 metricset 會從這個位置取得網路的資訊。

因此我們會需要將這些主機實體位置，mount 到 Docker container 之中，讓運作在 Docker container 內的 Metricbeat 可以存取得到實體主機的這些資訊。

```
ocker run \
	--user root --cap-add sys_ptrace --cap-add dac_read_search \
  --mount type=bind,source=/proc,target=/hostfs/proc,readonly \ 
  --mount type=bind,source=/sys/fs/cgroup,target=/hostfs/sys/fs/cgroup,readonly \ 
  --mount type=bind,source=/,target=/hostfs,readonly \
  docker.elastic.co/beats/metricbeat:7.15.0 -e -system.hostfs=/hostfs
```

上面的例子是把這些路徑都 mount 在 container 內的 `/hostfs` 裡，並且在取後執行 `metricbeat` 時，加上參數指定這個位置 `-system.hostfs=/hostfs`

另外如果有使用 System socket 的 metricset 時，因為需要較高的權限，我們會需要特別加上 `sys_ptrace` 與 `dac_read_search` 的 System capability。

> 這邊要注意，上面提到的 `/proc` 、`/sys`、System capabilies…等設定是針對 Linux 環境，不適用於 Windows 或 MacOS。

### 使用 Metricbeat 取得整體 Docker Containers 的系統 Metrics

要使用 Metricbeat 來取得 Docker Containers 的資訊時，我們要使用的是 Metricbeat 裡的 Docker module。

```
./metricbeat modules enable docker
```

並且在要在 `metricbeat.yml` 裡，設定要收集的 docker metricsets，以及相關的配置。

```
metricbeat.modules:
- module: docker
  metricsets:
    - "container"
    - "cpu"
    - "diskio"
    - "healthcheck"
    - "info"
    #- "image"
    - "memory"
    - "network"
  hosts: ["unix:///var/run/docker.sock"]
  period: 10s
  enabled: true
```

這邊要注意到，由於 docker module 會透過 `/var/run/docker.sock` 與 docker 溝通，所以我們如果是運行在 Docker container 內的 Metricbeat，我們也會需要把實體主機的 `/var/run/docker.sock` mount 到 docker container 內，讓 Metricbeat 可以存取得到。

```
docker run -d \
  --volume="/var/run/docker.sock:/var/run/docker.sock:ro" \
  docker.elastic.co/beats/metricbeat:7.15.0 metricbeat -e \
  -E output.elasticsearch.hosts=["elasticsearch:9200"]  
```

### 使用 Metricbeat 取得其他 Docker Container 身上服務的 Metrics

要使用 Metricbeat 取得其他 Dockre Containre 所運作的服務的 Metrics 時，可以直接指定網路的位置，並且指定在相同的 docker network，讓 Metricbeat 可以存取得到其他服務，或是可以使用 Metricbeat 的 **Autodiscover** 功能。

由於在 Container 的環境之中，機器可能會時常開關，能夠動態的自動監控 Container 會比較實用，因此我們也就會以 Autodiscover 的介紹為主。

#### 設置 Autodiscover 自動找尋需監控的機器 \[2]

Metricbeat 的 Autodiscover 有支援兩種 Provider - `Docker` 與 `Kubernetes` ，這篇會以 Docker 為主要說明。

由於 Docker provider 會監聽 [Docker events](https://docs.docker.com/engine/reference/commandline/events/) \[3]，在 Docker Container `start` 或 `stop` 的時候，去更新需監控機器的列表，因此相關設定上，可以依照 Docker event 的資訊來進行篩選條件的設置，來決定哪些 container 要套用設定。

Docker event 的資訊如下：

```
{
  "host": "10.4.15.9",
  "port": 6379,
  "docker": {
    "container": {
      "id": "382184ecdb385cfd5d1f1a65f78911054c8511ae009635300ac28b4fc357ce51"
      "name": "redis",
      "image": "redis:3.2.11",
      "labels": {
        "io.kubernetes.pod.namespace": "default"
        ...
      }
    }
  }
}
```

這邊可以用來當篩選條件的欄位也就是：

* host
* port
* docker.container.id
* docker.container.image
* docker.container.name
* docker.container.labels

接下來是要在 `metricbeat.yml` 當中，設定 Autodiscover，並且依照上面提到的篩選條件，來設定 `condition`，決定哪些 container 是我們的目標對象。

```
metricbeat.autodiscover:
  providers:
    - type: docker
      labels.dedot: true
      templates:
        - condition:
            contains:
              docker.container.image: redis
          config:
            - module: redis
              metricsets: ["info", "keyspace"]
              hosts: "${data.host}:6379"
```

以上面的例子，目標是只要 Docker Image 是使用 `redis` 的 containers，就會是我們 auto discover 的目標，並且會 Redis module 來收集這些 containers 裡的資訊，相關的 Redis module 的設定，也會在 `config` 裡面去指定。

如此一來，在使用 Docker 動態增加或減少特定服務的 containers 時，我們的 Metricbeat 就會自動的去監控並收集 Metrics 的資訊了。

#### 使用 Docker Autodiscover Provider 的 Hints 讓找機器更容易

Autodiscover 除了上述的使用 Docker event 方式來篩選之外，有提供針對 `Docker Label` 的標示來判定是否要監控的機制。

```
metricbeat.autodiscover:
  providers:
    - type: docker
      hints.enabled: true
```

要啟用這項功能，要把 `hints.enabled: true` 打開。

並且當我們要運作一個 Docker Container 時，就可以指定 Autodiscover 定義好的標籤 `co.elastic.metrics`，讓 Metricbeat 能認得這些 Containers。

以下是 Nginx 的一個例子：

```
  co.elastic.metrics/module: nginx
  co.elastic.metrics/metricsets: stubstatus
  co.elastic.metrics/hosts: '${data.host}:80'
  co.elastic.metrics/period: 10s
```

另外這是 Apache Server 的例子，並且實際用 Docker Run 時，如何加上標籤：

```
docker run \
  --label co.elastic.metrics/module=apache \
  --label co.elastic.metrics/metricsets=status \
  --label co.elastic.metrics/hosts='${data.host}:${data.port}' \
  --detach=true \
  --name my-apache-app \
  -p 8080:80 \
  httpd:2.4
```

## 在 Kibana 的 Metric Monitoring 掌握 Docker 環境的健康狀態

當 Metricbeat 把 Docker Container 相關的資訊開始進行收集後，我們就可以到 Kibana Observability 裡的 Metrics Inventory 頁面，並且選擇 `Docker Containers` 的呈現方式，來檢視 Docker Containers 裡的各項服務的狀態了。

![10-Kibana-Metrics-Inventory-Docker-Container](https://i.imgur.com/czIPkdD.png)

Kibana Metrics 這邊的操作，可以參考前一篇 [09 - Metrics - 觀察系統的健康指標 (3) - 使用 Metricbeat 掌握 Infrastructure 的健康狀態 Host 篇](https://ithelp.ithome.com.tw/articles/10271438) 的介紹，這部份的差異不大，主要是 Docker 會有些特別針對 Docker 環境的數據檢視方式，這部份有興趣的讀者可以再去探索看看。

## 參考資料

1. [官方文件 - Run Metricbeat on Docker](https://www.elastic.co/guide/en/beats/metricbeat/7.14/running-on-docker.html)
2. [官方文件 - Metricbeat Autodiscover](https://www.elastic.co/guide/en/beats/metricbeat/7.14/configuration-autodiscover.html)
3. [Docker events](https://docs.docker.com/engine/reference/commandline/events/)
