# 使用 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/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://training.onedoggo.com/tech-sharing/uncle-joe-teach-es-elastc-observability/metrics-guan-cha-xi-tong-de-jian-kang-zhi-biao/shi-yong-metricbeat-zhang-wo-infrastructure-de-jian-kang-zhuang-tai-docker-pian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
