> For the complete documentation index, see [llms.txt](https://training.onedoggo.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](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-kubernetes-pian.md).

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

### 本篇學習重點

* Metricbeat 能夠收集 Kubernetes 哪些 Metrics
* 如何使用 Metricbeat 來收集 Kubernetes 的 Metrics
* 在 Kibana 上有提供哪些方式，能查看 Metricbeats 所收集的 Metrics

***

前一篇文章介紹了如何使用 Metricbeat 來掌握 Docker 環境 Infrastructure 的健康狀態，這一篇將來說明 Kubernetes 的環境，我們要如何使用 Metricbeat 來掌握各種健康狀態的 Metrics。

## Metricbeat 在 Kubernetes 環境中，能收集哪些 Metrics

這邊我們分成以下三個部份來說明：

* **System Metrics**: 我們在 Kubernetes 環境中的每個 Node，我們都應該要收集這些 Nodes 的系統 Metrics，所以這邊就會要使用 Metricbeat 的 `system module` 來收集。
* **Kubernetes Metrics**: 在 Kubernetes 的環境之中，Kubernetes 本身有許多運作的功能與機制，這部份 Metricbeat 有特別針對 Kubernetes 開發 `kubernetes module` 裡面有收集非常豐富的 Metrics，下面會針對這個 module 獨立介紹。
* **其他運作在 Kubernetes 上的服務的 Metrics**: 這部份就會使用 Metricbeat 所支援的各種服務的 modules，細節可以參考 [官方文件 Metricbeat Modules](https://www.elastic.co/guide/en/beats/metricbeat/7.14/metricbeat-modules.html) \[1]。

### Metricbeat Kubernetes Module

這邊我們來說明 Metricbeat 針對 Kubernetes 所開發的 Module 有哪些功能。

Kubernetes module 有收集以下 Kubernetes 元件的 Metrics：

* [kubelet](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)
* [kube-state-metrics](https://github.com/kubernetes/kube-state-metrics)
* [apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/)
* [controller-manager](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/)
* [scheduler](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/)
* [proxy](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/)

由於這些元件之中， `kubelet` 和 `proxy` 是運作在 Cluster 當中的每個 Node 身上，所以會是 Node 層級，而其他的元件是運作在 Cluster 層級，因此不同層級也會有不同的佈署與收集方式，以下我整理一個表格，將 Kubernetes module 所支援的 metricset 進行分類說明。

| Metricset 名稱                                                                                                                                                                                                                                                                  | 收集的方式                                                                                                 | 層級      | 適用的佈署方式                  |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------- | ------------------------ |
| `container`, `node`, `pod`, `system`, `volume`                                                                                                                                                                                                                                | kubelet endpoint (Default HTTP port: `10250`)                                                         | Node    | DaemonSet                |
| `state_node`, `state_daemonset`, `state_deployment`, `state_replicaset`, `state_statefulset`, `state_pod`, `state_container`, `state_cronjob`, `state_resourcequota`, `state_service`, `state_persistentvolume`, `state_persistentvolumeclaim`, `state_storageclass`, `event` | kibe-state-metrics 需另外安裝的 Service (Standard Example HTTP port: `8080`)                                | Cluster | DaemonSet 之中的 Leader Pod |
| `apiserver`                                                                                                                                                                                                                                                                   | Kubernetes `/metrics` API                                                                             | Cluster | DaemonSet 之中的 Leader Pod |
| `proxy`                                                                                                                                                                                                                                                                       | proxy endpoint (Default HTTP port: `10249`)                                                           | Node    | DaemonSet                |
| `scheduler`                                                                                                                                                                                                                                                                   | scheduler endpoint 需另外 create `kube-scheduler` service (Default HTTP port: `10259`)                   | Cluster | DaemonSet 之中的 Leader Pod |
| `controller-manager`                                                                                                                                                                                                                                                          | controller-manager endpoint 需另外 create `kube-controller-manager` service (Default HTTP port: `10257`) | Cluster | DaemonSet 之中的 Leader Pod |

## 使用 Metricbeat 監控 Kubernetes 的 Metrics

接下來，我們要來實際說明佈署的方式，在進行實作之前，先介紹在 Kubernetes 上部署 Metricbeat 的方法。

### 在 Kubernetes 上部署 Metricbeat 的方法

首先，要在 Kubernetes 裡佈署 Metricbeat 的方式有兩種，這邊先簡介這兩種的概念，接下來的實例，將帶大家了解如何實作。

#### DaemonSet

由於 Kubernetes DaemonSet 的設計，讓我們能夠輕易的確保 Metricbeat 能運作且常駐在 Kubernetes Cluster 中的每一個 Node 身上，並且可以在這個 Node 上只啟動一個 Instance (一個 Pod)，讓這個 Metricbeat 負責收集這個 Node 的 System Metrics 以及運作在這個 Node 身上其他 Pods 的各種服務的 Metrics。

因此在使用 Metricbeat 來監控 Kubernetes Cluster 的基本配置方式，就是將 Metricbeat 佈署成 DaemonSet。

另外 Metricbeat 有實作 [Leader Election](https://github.com/kubernetes/client-go/tree/master/tools/leaderelection) \[2]，在 Cluster DaemonSet 中只有一個 Metricbeat Pod，會被選為 leader，我們就可以使用這個 leader pod 來負責收集 Cluster 層級的 Metrics，避免 Cluster 中若是有多個 Metricbeat 都在收集 Cluster 層集的 Metrics，會收集到重覆的資料。

#### Deployment

除了使用 DaemonSet 的方式來佈署，我們當然也可以使用 Deployment 的方式來佈署 Metricbeat，但是什麼時候會用到這種方法呢？

主要是當 Cluster 規模愈大時，上面所提到 leader pod 很可能會因為資源不足，無法處理這樣大量級的資料，這時就應該考慮使用獨立的 Instance 來佈署 Metricbeat，以專門收集 Cluster 層級的 Metrics。

### 實際的佈署

我們這邊使用官方提供的一個 [Metricbeat-Kubernetes 範例](https://raw.githubusercontent.com/elastic/beats/7.14/deploy/kubernetes/metricbeat-kubernetes.yaml) \[3] 來進行拆解說明。

#### ConfigMap `metricbeat-deamonset-config`

在這個 ConfigMap 當中，定義了主要運作成為 DaemonSet 的 Metricbeat 的主要設定，也就是 `metricbeat.yml` 的配置。

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: metricbeat-daemonset-config
  namespace: kube-system
  labels:
    k8s-app: metricbeat
data:
  metricbeat.yml: |-
    metricbeat.config.modules:
      # Mounted `metricbeat-daemonset-modules` configmap:
      path: ${path.config}/modules.d/*.yml
      # Reload module configs as they change:
      reload.enabled: false

    metricbeat.autodiscover:
      providers:
        - type: kubernetes
          scope: cluster
          node: ${NODE_NAME}
          unique: true
          templates:
            - config:
                - module: kubernetes
                  hosts: ["kube-state-metrics:8080"]
                  period: 10s
                  add_metadata: true
                  metricsets:
                    - state_node
                    - state_deployment
                    - state_daemonset
                    - state_replicaset
                    - state_pod
                    - state_container
                    - state_cronjob
                    - state_resourcequota
                    - state_statefulset
                    - state_service
                - module: kubernetes
                  metricsets:
                    - apiserver
                  hosts: ["https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"]
                  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
                  ssl.certificate_authorities:
                    - /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
                  period: 30s
                # Uncomment this to get k8s events:
                #- module: kubernetes
                #  metricsets:
                #    - event
        # To enable hints based autodiscover uncomment this:
        #- type: kubernetes
        #  node: ${NODE_NAME}
        #  hints.enabled: true

    processors:
      - add_cloud_metadata:

    cloud.id: ${ELASTIC_CLOUD_ID}
    cloud.auth: ${ELASTIC_CLOUD_AUTH}

    output.elasticsearch:
      hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
      username: ${ELASTICSEARCH_USERNAME}
      password: ${ELASTICSEARCH_PASSWORD}
```

這裡面的配置我們分成以下來說明：

* 有定義下面另一組 ConfigMap `metricbeat-daemonset-modules` 要載入到 `modules.d` 目錄底的設定。
* 定義 `autodiscover` 的設定，這裡指定到 `scope: cluster` 以及 `unique: true`，代表這是在整個 DaemonSet 當中，只有 leader 這個唯一的 Pod 會運行，也因此裡面所描述的 Metricset 會是 Cluster 等級的，像是 `state_*` 以及 `event` 這樣的 Metricsets。

#### ConfigMap `metricbeat-daemonset-modules`

在這個 ConfigMap 裡，定義了主要的 DaemonSet 要執行的 Metricbeat 的模組有哪些

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: metricbeat-daemonset-modules
  namespace: kube-system
  labels:
    k8s-app: metricbeat
data:
  system.yml: |-
    - module: system
      period: 10s
      metricsets:
        - cpu
        - load
        - memory
        - network
        - process
        - process_summary
        #- core
        #- diskio
        #- socket
      processes: ['.*']
      process.include_top_n:
        by_cpu: 5      # include top 5 processes by CPU
        by_memory: 5   # include top 5 processes by memory

    - module: system
      period: 1m
      metricsets:
        - filesystem
        - fsstat
      processors:
      - drop_event.when.regexp:
          system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib|snap)($|/)'
  kubernetes.yml: |-
    - module: kubernetes
      metricsets:
        - node
        - system
        - pod
        - container
        - volume
      period: 10s
      host: ${NODE_NAME}
      hosts: ["https://${NODE_NAME}:10250"]
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      ssl.verification_mode: "none"
      # If there is a CA bundle that contains the issuer of the certificate used in the Kubelet API,
      # remove ssl.verification_mode entry and use the CA, for instance:
      #ssl.certificate_authorities:
        #- /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt
    # Currently `proxy` metricset is not supported on Openshift, comment out section
    - module: kubernetes
      metricsets:
        - proxy
      period: 10s
      host: ${NODE_NAME}
      hosts: ["localhost:10249"]
```

這裡面的配置我們分成以下來說明：

* 由於這會是每個 Node 都會運行一組的 DaemonSet，所以會要收集系統的 Metrics，這邊有定義了 `system.yml` 的 System module。
* 同樣的每個 Node 也都要收集 Node 層級的 Kubernetes Metrics，所以這邊有定義 `kuebernetes.yml` 使用 Kubernetes module，並且宣告 `node`、`system`、`pod`、`container`、`volume`、`proxy` 這些 Metricsets。
* 另外針對不同的 metricset 的來源，會從 `kubelet` 的 port `10250` 及 `proxy` 的 port `10249` 取得 Metrics。

#### DaemoneSet `metricbeat`

接下來就是主要的 DaemonSet - `metricbeat`

```
# Deploy a Metricbeat instance per node for node metrics retrieval
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: metricbeat
  namespace: kube-system
  labels:
    k8s-app: metricbeat
spec:
  selector:
    matchLabels:
      k8s-app: metricbeat
  template:
    metadata:
      labels:
        k8s-app: metricbeat
    spec:
      serviceAccountName: metricbeat
      terminationGracePeriodSeconds: 30
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - name: metricbeat
        image: docker.elastic.co/beats/metricbeat:7.14.2
        args: [
          "-c", "/etc/metricbeat.yml",
          "-e",
          "-system.hostfs=/hostfs",
        ]
        env:
        - name: ELASTICSEARCH_HOST
          value: elasticsearch
        - name: ELASTICSEARCH_PORT
          value: "9200"
        - name: ELASTICSEARCH_USERNAME
          value: elastic
        - name: ELASTICSEARCH_PASSWORD
          value: changeme
        - name: ELASTIC_CLOUD_ID
          value:
        - name: ELASTIC_CLOUD_AUTH
          value:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        securityContext:
          runAsUser: 0
          # If using Red Hat OpenShift uncomment this:
          #privileged: true
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 100Mi
        volumeMounts:
        - name: config
          mountPath: /etc/metricbeat.yml
          readOnly: true
          subPath: metricbeat.yml
        - name: data
          mountPath: /usr/share/metricbeat/data
        - name: modules
          mountPath: /usr/share/metricbeat/modules.d
          readOnly: true
        - name: proc
          mountPath: /hostfs/proc
          readOnly: true
        - name: cgroup
          mountPath: /hostfs/sys/fs/cgroup
          readOnly: true
      volumes:
      - name: proc
        hostPath:
          path: /proc
      - name: cgroup
        hostPath:
          path: /sys/fs/cgroup
      - name: config
        configMap:
          defaultMode: 0640
          name: metricbeat-daemonset-config
      - name: modules
        configMap:
          defaultMode: 0640
          name: metricbeat-daemonset-modules
      - name: data
        hostPath:
          # When metricbeat runs as non-root user, this directory needs to be writable by group (g+w)
          path: /var/lib/metricbeat-data
          type: DirectoryOrCreate
```

這裡面的配置我們分成以下來說明：

* 這邊有使用到 DaemonSet 的 `selector` 與 `template` ，並指定 Label `k8s-app: metricbeat`。
* 使用 `serviceAccountName: metricbeat`。
* 將 `/proc`、`/sys/fs/cgroup` 給 mount 到 `/hostfs` 底下，並且在啟動時，指定給 metricbeat: `-system.hostfs=/hostfs`。
* 將 Elasticsearch 相關的環境變數指定預設定。
* mount 前面指定好的 ConfigMap `metricbeat-daemonset-config` 與 `metricbeat-daemonset-modules`。

#### ServiceAccount 與相關的權限設定

這部份就不多解釋，請大家自己從設定來參考。

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: metricbeat
subjects:
- kind: ServiceAccount
  name: metricbeat
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: metricbeat
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: metricbeat
  namespace: kube-system
subjects:
  - kind: ServiceAccount
    name: metricbeat
    namespace: kube-system
roleRef:
  kind: Role
  name: metricbeat
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: metricbeat-kubeadm-config
  namespace: kube-system
subjects:
  - kind: ServiceAccount
    name: metricbeat
    namespace: kube-system
roleRef:
  kind: Role
  name: metricbeat-kubeadm-config
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: metricbeat
  labels:
    k8s-app: metricbeat
rules:
- apiGroups: [""]
  resources:
  - nodes
  - namespaces
  - events
  - pods
  - services
  verbs: ["get", "list", "watch"]
# Enable this rule only if planing to use Kubernetes keystore
#- apiGroups: [""]
#  resources:
#  - secrets
#  verbs: ["get"]
- apiGroups: ["extensions"]
  resources:
  - replicasets
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources:
  - statefulsets
  - deployments
  - replicasets
  verbs: ["get", "list", "watch"]
- apiGroups:
  - ""
  resources:
  - nodes/stats
  verbs:
  - get
- nonResourceURLs:
  - "/metrics"
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: metricbeat
  # should be the namespace where metricbeat is running
  namespace: kube-system
  labels:
    k8s-app: metricbeat
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs: ["get", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: metricbeat-kubeadm-config
  namespace: kube-system
  labels:
    k8s-app: metricbeat
rules:
  - apiGroups: [""]
    resources:
      - configmaps
    resourceNames:
      - kubeadm-config
    verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: metricbeat
  namespace: kube-system
  labels:
    k8s-app: metricbeat
```

### 安裝 `Kube-state-metrics` 以提供 Metricbeat 所需要的 Metrics

由於前面所提到的 `kube-state-metrics` 並非是 Kubernetes Cluster 預設啟用的功能，這部份會需要透過另外安裝才能啟用這個服務。

以下使用最基本的簡易安裝方式，細節請參考 [GitHub kube-state-metrics](https://github.com/kubernetes/kube-state-metrics#kubernetes-deployment) \[4]。

先抓下 GitHub 上專案中建立好的 examples 配置：

```
git clone https://github.com/kubernetes/kube-state-metrics.git
```

使用 examples 中的 `standard` 配置來建立服務：

```
cd kube-state-metrics
kubectl apply -f examples/standard
```

成功建立之後，就可以在 `kube-system` 的 namespace 中看到 `kube-state-metrics` 的 `pod` 以及對外開啟 `8080` port 的 `service`。

![11-k9s-kube-state-metrics](https://i.imgur.com/NNkYnS3.png)

> 這邊使用的工具是 [k9s](https://github.com/derailed/k9s)，蠻好用的 Kubernetes 視覺化管理工具。

在圖中我們可以看到，由於 `kube-state-metrics` 是指定給 leader 才會執行，所以我們在 leader 這台 pod 的 logs 才會看到這資訊。

### 使用 Autodiscovery 透過 Annotation 自動找尋需監控的 Pods

其他運作在 Kubernetes 上的 Service，若是要使用 Metricbeat 的其他 modules 來取得 Metrics，建議可以使用 Autodiscovery 針對 Kubernetes Annotation 所支援的自動監控的功能。

```
metricbeat.autodiscover:
  providers:
    - type: kubernetes
      templates:
        - condition:
            contains:
              kubernetes.labels.app: "redis"
          config:
            - module: redis
              metricsets: ["info", "keyspace"]
              hosts: "${data.host}:6379"
              password: "${REDIS_PASSWORD}"
```

例如我們可以針對 `kubernetes.labels.app` 為 `redis` 的 pod，設定使用 redis module 來取得 Redis 的 Metrics , 並且套用上面所定義的 config 配置。

## 在 Kibana 檢視 Kubernetes 的 Metrics

在以上設定安裝完成之後，我們可以透過 Kibana 來查看 Metricbeat 所針對 Kubernetes 收集到的 Metrics。

### Kibana Observability Metrics

在 Kibana > Observability > Metrics 裡的 Inventory 中，我們可以針對 Show 指定為 `Kubernetes Pods` ，並且可以透過 Group by 的方式進行分類，結果如下圖。

![11-kibana-observability-metrics-k8s](https://i.imgur.com/rBT17Ct.png)

其他 Inventory 的使用方式，可以參考先前的文章 [09 - Metrics - 觀察系統的健康指標 (3) - 使用 Metricbeat 掌握 Infrastructure 的健康狀態 Host 篇](https://ithelp.ithome.com.tw/articles/10271438) 的介紹。

### Metricbeat 內建的 Dashboard

由於 Inventory 的檢視方式較為是 Infrastructure 整體的健康度，而 Metricbeat 也有為 Kubernetes 建立了四種 Dashboard，可以讓我們直接使用。

(由於我的測試環境有些資訊沒有呈現出來，所以有幾張圖我直接以[官方網站](https://www.elastic.co/guide/en/beats/metricbeat/7.14/metricbeat-module-kubernetes.html#metricbeat-module-kubernetes)的圖來呈現)

### Cluster Overview

![11-kibana-dashboard-k8s-overview](https://i.imgur.com/lu1pyx1.png)

### Controller Manager

![11-metricbeat-kubernetes-controllermanager](https://i.imgur.com/DY74GiU.png)

### Scheduler

![11-metricbeat\_kubernetes\_scheduler](https://i.imgur.com/ZpNW7k6.png)

### Proxy

![11-metricbeat-kubernetes-proxy](https://i.imgur.com/73DjI4S.png)

***

Metricbeat 針對 Kubernetes 所收集的資訊相當詳細，相信可以對於我們監控管理 Kubernetes Cluster 有很大的幫助，特別是這些資料進入 Elasticsearch 之後，還能和其他 Obersevability 的資訊進行整合使用，應用的空間還很大呢！

## 參考資訊

1. [官方文件 - Metricbeat Modules](https://www.elastic.co/guide/en/beats/metricbeat/7.14/metricbeat-modules.html)
2. [Kubernetes client-go - Leader Election](https://github.com/kubernetes/client-go/tree/master/tools/leaderelection)
3. [Metricbeat-Kubernetes 範例](https://raw.githubusercontent.com/elastic/beats/7.14/deploy/kubernetes/metricbeat-kubernetes.yaml)
4. [GitHub kube-state-metrics](https://github.com/kubernetes/kube-state-metrics#kubernetes-deployment)
