Kubernetes 叢集和應用監控方案的設計與實踐

微軟技術棧 發表於 2022-02-14
Kubernetes

Kubernetes 監控

當你的應用部署到 Kubenetes 後,你很難看到容器內部發生了什麼,一旦容器死掉,裡面的資料可能就永遠無法恢復,甚至無法檢視日誌以定位問題所在,何況一個應用可能存在很多個例項,使用者的一個請求不指定被哪個容器處理了,這使得在 Kubernetes 中對應用進行故障排除較為複雜。在應用之外,由於 Kubernetes 作為基礎設施,掌管這整個叢集的生死,Kubernetes 的任何故障,必定影響到應用服務的執行,因此監控 Kubernetes 執行狀況也至關重要。

當你的應用上了雲原生,那你就不得不關注各個伺服器的執行狀態,基礎設施和中介軟體的執行狀態,Kubernetes 中每個元件和資源物件的執行狀態,每個應用的執行狀態。當然,這個執行狀態是一個模糊的概念,取決於我們的關注點,每個被監控的物件要表達的 "執行狀態" 是不一樣的。為了可以監控我們關注的物件,物件需要做出一些配合,提供合適的執行狀態的表達資訊,以供我們收集和分析,這可以稱為可觀測性。

在雲原生中,一般對可觀測性分為三大作用域:

Kubernetes 叢集和應用監控方案的設計與實踐

你可以在 Kubernetes 文件中瞭解如何監控、除錯,以及瞭解如何對日誌進行處理:

https://v1-20.docs.kubernetes.io/docs/tasks/debug-application-cluster/

在本文中,所提到的監控,只包括 Metrics 。

Metrics、Tracing、Logging 不是完全獨立的,在上圖中,Metrics 也會可能包含 Logging 和 Tracing 的資訊。

監控物件

要採集的監控資料,來源於被監控物件,而在 Kubernetes 叢集中,我們可以將要監控的物件分為三大部分:

  • 機器:叢集中的所有節點機器,指標有 CPU 記憶體使用率、網路和硬碟 IO 速率等;
  • Kubernetes 物件狀態:Deployments, Pods, Daemonsets, Statefulset 等物件的狀態和某些指標資訊;
  • 應用:Pod 中每個容器的狀態或指標,以及容器本身可能提供的 /metrics 端點。

Prometheus

在基礎環境中,一個完整的監控應包括採集資料、儲存資料、分析儲存資料、展示資料、告警等多個部分,而每個部分都有相關的工具或技術解決雲原生中環境的多樣需求和複雜性問題。

既然要做監控,那麼就需要監控工具。監控工具可以獲取所有重要的指標和日誌(Metrics也可以包含一些日誌),並將它們儲存在一個安全、集中的位置,以便可以隨時訪問它們來制定方案解決問題。由於在雲原生中,應用在 Kubernetes 叢集中部署,因此,監控 Kubernetes 可以讓你深入瞭解叢集的執行狀況和效能指標、資源計數以及叢集內部情況的頂級概覽。發生錯誤時,監控工具會提醒你(告警功能),以便你快速推出修復程式。

Prometheus 是一個 CNCF 專案,可以原生監控 Kubernetes、節點和 Prometheus 本身,目前 Kubernetes 官方文件主要推薦使用 Prometheus 本身,它為 Kubernetes 容器編排平臺提供開箱即用的監控能力。因此在本文中,對監控方案的設計是圍繞 Prometheus 展開的。

下面是 Prometheus 的一些元件介紹:

  1. Metric Collection: Prometheus 使用拉模型通過 HTTP 檢索度量。在 Prometheus 無法獲取指標的情況下,可以選擇利用 Pushgateway 將指標推給 Prometheus 。
  2. Metric Endpoint: 希望使用 Prometheus 監視的系統應該公開某個/度量端點的度量, Prometheus 利用這個端點以固定的間隔提取指標。
  3. PromQL: Prometheus 附帶了 PromQL,這是一種非常靈活的查詢語言,可用於查詢 Prometheus 儀表板中的指標。此外,Prometheus UI 和 Grafana 將使用 PromQL 查詢來視覺化指標。
  4. Prometheus Exporters: 有許多庫和伺服器可以幫助將第三方系統中的現有指標匯出為 Prometheus 指標。這對於無法直接使用 Prometheus 指標檢測給定系統的情況。
  5. TSDB (time-series database): Prometheus 使用 TSDB 高效地儲存所有資料。預設情況下,所有資料都儲存在本地。然而,為了避免單點故障,prometheustsdb 可以選擇整合遠端儲存。

Prometheus 在 Kubernetes 中的監控方案結構如下:

img

【圖源:https://devopscube.com/setup-prometheus-monitoring-on-kubernetes/】

指標

要監控的物件種類很多,我們把相同型別的物件稱為一個實體,而每個實體執行時的物件產生的資料有各種各樣的,為了歸納收集這些資料, Prometheus 將實體中的各種屬性值分為 Counter (計數器)、Gauge (儀表盤)、Histogram(累積直方圖)、Summary(摘要)四種型別,實體中的每個屬性,稱為指標,例如 容器已累計使用 CPU 量,使用指標名稱 container_cpu_usage_seconds_total 記錄。

每個指標一般格式為:

指標名稱{後設資料=值} 指標值

每個物件都在無時無刻產生資料,為了區分當前指標值屬於哪個物件,可以給指標除了指標值外,附加大量的後設資料資訊,示例如下表示。

container_cpu_usage_seconds_total{
	beta_kubernetes_io_arch = "amd64",
	  beta_kubernetes_io_os = "linux", 
	  container = "POD", 
	  cpu = "total", 
	  id = "...", 
	  image = "k8s.gcr.io/pause:3.5", 
	  instance = "slave1", 
	  job = "kubernetes-cadvisor", 
	  kubernetes_io_arch = "amd64", 
	  kubernetes_io_hostname = "slave1",
	  kubernetes_io_os = "linux", 
	  name = "k8s_POD_pvcpod_default_02ed547b-6279-4346-8918-551b87877e91_0", 
	  namespace = "default", 
	  pod = "pvcpod"
}

物件生成類似這種結構的文字後,可以暴露 metrics 端點,讓 Prometheus 自動採集,或通過 Pushgateway 推送到 Prometheus 中。

接下來,我們將在 Kubernetes 中搭建一個完整的 Prometheus 監控體系。

實踐

節點監控

本章參考資料:https://devopscube.com/node-exporter-kubernetes/

node exporter 是用 Golang 編寫的,用於在 Linux 系統上,收集核心公開的所有硬體和作業系統級別的指標,包括 CPU 、資訊、網路卡流量、系統負載、socket 、機器配置等。

讀者可參考中列舉出的 https://github.com/prometheus/node_exporter 中列舉出的所有預設開啟或預設關閉的指標。

既然要監控叢集中的每個節點,那麼就要做到每個節點都執行這一個 node exporter 例項,並且在叢集新增節點的時候自動排程一個 node exporter 到這個節點中執行,因此需要採用 node exporter 的部署需要 DaemontSet 模式。

檢視叢集中的所有節點:

[email protected]:~# kubectl get nodes
NAME     STATUS                     ROLES                  AGE     VERSION
master   Ready,SchedulingDisabled   control-plane,master   98d     v1.22.2
salve2   Ready                      <none>                 3h50m   v1.23.3
slave1   Ready                      <none>                 98d     v1.22.2

Bibin Wilson 大佬已經封裝好了用於 Kubernetes 的 node exporter 的 YAML 檔案,我們直接下載即可:

git clone https://github.com/bibinwilson/kubernetes-node-exporter

開啟倉庫中的 daemonset.yaml 檔案,大概瞭解其中的資訊。

在 YAML 檔案中,可以看到 node exporter 會被部署到名稱空間 monitoring 中執行,它有兩個 label:

   labels:
     app.kubernetes.io/component: exporter
     app.kubernetes.io/name: node-exporter

為了 node exporter 能夠被排程到 master 節點中執行,我們需要為 Pod 新增容忍度屬性:

  template:
    metadata:
      labels:
        app.kubernetes.io/component: exporter
        app.kubernetes.io/name: node-exporter
    spec:
    # 複製下面這部分到對應的位置
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "node.kubernetes.io/unschedulable"
        operator: "Exists"
        effect: "NoSchedule"

為了部署 node exporter ,我們先建立名稱空間:

kubectl create namespace monitoring

執行命令部署 node exporter:

kubectl create -f daemonset.yaml

檢視 node exporter 例項

[email protected]:~# kubectl get daemonset -n monitoring
NAME            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
node-exporter   3         3         3       3            3           <none>          22h

由於 node exporter Pod 分散在各個節點,為了便於 Prometheus 收集這些 node exporter 的 Pod IP,需要建立 Endpoint 統一收集,這裡通過建立 Service 自動生成 Endpoint 來達到目的。

檢視倉庫下的 service.yaml 檔案,其定義如下:

kind: Service
apiVersion: v1
metadata:
  name: node-exporter
  namespace: monitoring
  annotations:
      prometheus.io/scrape: 'true'
      prometheus.io/port:   '9100'
spec:
  selector:
      app.kubernetes.io/component: exporter
      app.kubernetes.io/name: node-exporter
  ports:
  - name: node-exporter
    protocol: TCP
    port: 9100
    targetPort: 9100

此 Service 的選擇器如下:

selector:
   app.kubernetes.io/component: exporter
   app.kubernetes.io/name: node-exporter

建立 Service:

kubectl create -f service.yaml

檢視 Endpoint 收集的 node exporter 的 Pod IP:

[email protected]:~# kubectl get endpoints -n monitoring 
NAME                    ENDPOINTS                                       AGE
node-exporter           10.32.0.27:9100,10.36.0.4:9100,10.44.0.3:9100   22h

node exporter 除了收集各種指標資料外,不會再幹什麼。

部署 Prometheus

本節參考https://devopscube.com/setup-prometheus-monitoring-on-kubernetes/

現在有了 node exporter ,可以收集節點各類指標,接下來便是對 Kubernetes 基礎設施的 metrics 資料收集。

Kubernetes 自身提供的很多 metrics 資料,有三大端點 /metrics/cadvisor, /metrics/resource and /metrics/probes

/metrics/cadvisor 為例,cAdvisor 分析在給定節點上執行的所有容器的記憶體、CPU、檔案和網路使用情況的指標,你可以參考 https://github.com/google/cadvisor/blob/master/docs/storage/prometheus.md 瞭解 cAdvisor 的所有指標。

其它資料:

原始碼位置:https://github.com/kubernetes/metrics/blob/master/pkg/apis/metrics/v1beta1/types.go

Kubernetes 監控架構設計:https://github.com/kubernetes/design-proposals-archive

在本節中,部署的 Prometheus 將會對 kubenetes 進行以下動作以便收集 metrics 資料:

  1. Kubernetes-apiservers: 從 API 伺服器獲得所有的指標;
  2. Kubernetes 節點: 它收集所有的 kubernetes 節點指標;
  3. kubernetes-pods: pod 後設資料上加上 prometheus.io/scrape 和 prometheus.io/port 註釋,所有的 pod 指標都會被發現;
  4. kubernetes-cadvisor: 收集所有 cAdvisor 指標,與容器相關;
  5. Kubernetes-Service-endpoints: 如果服務後設資料使用 prometheus.io/scrape 註釋和 prometheus.io/port 註釋,那麼所有的服務端點都將被廢棄;

Bibin Wilson 大佬已經封裝好了相關的部署定義檔案,我們直接下載即可:

git clone https://github.com/bibinwilson/kubernetes-prometheus

Prometheus 通過使用 Kubernetes API Server ,獲取 各節點、Pod、Deployment 等所有可用的指標。因此,我們需要建立具有對所需 API 組的只有讀訪問許可權的 RBAC 策略,並將策略繫結到監視名稱空間,以限制 Prometheus Pod 只能對 API 進行讀操作。

檢視 clusterRole.yaml 檔案,可以其要監控的資源物件列表:

- apiGroups: [""]
  resources:
  - nodes
  - nodes/proxy
  - services
  - endpoints
  - pods
  verbs: ["get", "list", "watch"]
- apiGroups:
  - extensions
  resources:
  - ingresses

在叢集中建立角色和角色繫結:

kubectl create -f clusterRole.yaml

可以通過 通過命令列標誌和配置檔案 對 Prometheus 進行配置。雖然命令列標誌配置了不可變的系統引數(例如儲存位置、要儲存在磁碟和記憶體中的資料量等),但配置檔案定義了與抓取作業及其例項相關的所有內容,以及載入哪些規則檔案,因此部署 Permetheus 少不了做檔案配置。

Permetheus 的配置檔案以 YAML 格式編寫,具體規則可以參考:https://prometheus.io/docs/prometheus/latest/configuration/configuration/

為了便於將配置檔案對映到 Permetheus Pod 中,我們需要將配置放到 configmap ,然後掛載到 Pod,配置內容可以檢視 config-map.yaml 。config-map.yaml 中定義了很多采集資料來源的規則,例如收集 Kubernetes 叢集和 node exporter ,配置可參考:

    scrape_configs:
      - job_name: 'node-exporter'
        kubernetes_sd_configs:
          - role: endpoints
        relabel_configs:
        - source_labels: [__meta_kubernetes_endpoints_name]
          regex: 'node-exporter'
          action: keep

你可以開啟 https://raw.githubusercontent.com/bibinwilson/kubernetes-prometheus/master/config-map.yaml 線上預覽這個檔案。

建立 configmap:

kubectl create -f config-map.yaml

這個配置很重要,需要根據實際情況配置,一般由運維處理,這裡就不再討論。

接下來將要部署 Prometeus ,由於示例檔案中使用 emtpy 卷儲存 Prometheus 資料,因此一旦 Pod 重啟等,資料將會丟失,因此這裡可以改成 hostpath 卷。

開啟 prometheus-deployment.yaml 檔案:

          emptyDir: {}

改成

          hostPath:
            path: /data/prometheus
            type: Directory  

可改可不改。

如果改的話,需要在被排程此 Pod 對應的節點上建立 /data/prometheus 目錄。

部署 Prometeus :

kubectl create  -f prometheus-deployment.yaml 

檢視部署狀態:

[email protected]:~# kubectl get deployments --namespace=monitoring
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
prometheus-deployment   1/1     1            1           23h

為了在外界訪問 Prometeus ,需要建立 Service:

apiVersion: v1
kind: Service
metadata:
  name: prometheus-service
  namespace: monitoring
  annotations:
      prometheus.io/scrape: 'true'
      prometheus.io/port:   '9090'
  
spec:
  selector: 
    app: prometheus-server
  type: NodePort  
  ports:
    - port: 8080
      targetPort: 9090 
      nodePort: 30000
kubectl create -f prometheus-service.yaml

接下來可以訪問 Prometeus UI 皮膚。

點選 Graph,點選🌏圖示,選擇需要顯示的指標值,再點選 Execute 查詢顯示。

image-20220210205949823

image-20220210205829297

你還可以在 Service Discobery 中,檢視 Prometheus 採集的 metrics 資料來源。

image-20220210210215530

image-20220210210331859

如果你的叢集沒有安裝過 kube-state-metrics,那麼這個資料來源會顯示紅色標記,在下一節中,我們繼續部署這個元件。

至此,我們的監控結構如下所示:

image-20220210214630990

部署 Kube State Metrics

本節參考資料:https://devopscube.com/setup-kube-state-metrics/

Kube State metrics 是一個服務,它與 Kubernetes API Server 通訊,以獲取所有 API 物件的詳細資訊,如 Deployment、Pod 等。

Kube State metrics 提供了無法直接從本地 Kubernetes 監視元件獲得的 Kubernetes 物件和資源 度量,因為 Kubenetes Metrics 本身提供的指標並不是很全面,因此需要 Kube State Metrics 以獲得與 kubernetes 物件相關的所有度量。

以下是可以從 Kube State metrics 中獲得的一些重要度量:

  1. Node status, node capacity (CPU and memory)
  2. Replica-set compliance (desired/available/unavailable/updated status of replicas per deployment)
  3. Pod status (waiting, running, ready, etc)
  4. Ingress metrics
  5. PV, PVC metrics
  6. Daemonset & Statefulset metrics.
  7. Resource requests and limits.
  8. Job & Cronjob metrics

可以在這裡的文件中檢視受支援的詳細指標:https://github.com/kubernetes/kube-state-metrics/tree/master/docs

image-20220210210642889

Bibin Wilson 大佬已經封裝好了相關的部署定義檔案,我們直接下載即可:

git clone https://github.com/devopscube/kube-state-metrics-configs.git

直接應用所有 YAML 建立對應的資源:

kubectl apply -f kube-state-metrics-configs/
├── cluster-role-binding.yaml
├── cluster-role.yaml
├── deployment.yaml
├── service-account.yaml
└── service.yaml

上面建立的資源,包含以下部分,這一小節,就不展開講解。

  1. Service Account
  2. Cluster Role
  3. Cluster Role Binding
  4. Kube State Metrics Deployment
  5. Service

使用以下命令檢查部署狀態:

kubectl get deployments kube-state-metrics -n kube-system

隨後,重新整理 Prometheus Service Discobery ,可以看到紅色變成了藍色,點選此資料來源,可以看到以下資訊:

image-20220210211101241

- job_name: 'kube-state-metrics'
  static_configs:
    - targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080']

此配置為 kube-state-metrics 的訪問地址。

在此,我們部署的 Prometeus 結構如下:

image-20220210214807996

部署 Grafana

本節參考資料:https://devopscube.com/setup-grafana-kubernetes/

經過前面幾個小節的部署,已經搞好資料來源的採集以及資料儲存,接下來我們將部署 Grafana,利用 Grafana 對指標資料進行分析以及視覺化。

Bibin Wilson 大佬已經封裝好了相關的部署定義檔案,我們直接下載即可:

git clone https://github.com/bibinwilson/kubernetes-grafana.git

首先檢視 grafana-datasource-config.yaml 檔案,此配置是為了 Grafana 自動配置好 Prometheus 資料來源。

image-20220210212258986

裡面還有一個很重要的地址:

                "url": "http://prometheus-service.monitoring.svc:8080",

這裡要確認你的 CoreDNS 是否正常,你可以參考 https://kubernetes.io/zh/docs/tasks/administer-cluster/dns-debugging-resolution/ 中列舉的 DNS 除錯方法,確認你的叢集中是否可以通過 DNS 訪問 Pod。
最簡單的方法是啟動一個 Pod,然後使用命令測試 curl http://prometheus-service.monitoring.svc:8080,看看能不能獲取到響應資料,如果出現:

[email protected]:~/jk/kubernetes-prometheus# curl http://prometheus-deployment.monitoring.svc:8080
curl: (6) Could not resolve host: prometheus-deployment.monitoring.svc
[email protected]:~/jk/kubernetes-prometheus# curl http://prometheus-deployment.monitoring.svc.cluster.local:8080
curl: (6) Could not resolve host: prometheus-deployment.monitoring.svc.cluster.local

可能是你 coredns 沒有安裝或者別的原因,導致無法通過此地址訪問 Prometheus ,為了為了避免過多操作,可以改為使用 IP,而不是域名。

檢視 Prometheus 的 Service IP:

[email protected]:~/jk/kubernetes-prometheus# kubectl get svc -n monitoring
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
prometheus-deployment   NodePort    10.105.95.8     <none>        9090:32330/TCP   23h

測試通過 Service IP 訪問是否正常

[email protected]:~/jk/kubernetes-prometheus# curl 10.105.95.8:9090
<a href="/graph">Found</a>.

grafana-datasource-config.yaml 中的 prometheus-deployment.monitoring.svc.cluster.local:8080 改成對應的 Service IP,並且埠改成 9090。

建立配置

kubectl create -f grafana-datasource-config.yaml

開啟 deployment.yaml 檢視定義,模板中 grafana 的資料儲存也是使用 empty 卷,有資料丟失風險,因此可以改成用 hospath 或其他型別的卷儲存。可參考筆者的配置:

      volumes:
        - name: grafana-storage
          hostPath:
            path: /data/grafana
            type: Directory   

部署 Grafana:

kubectl create -f deployment.yaml

然後建立 Service:

kubectl create -f service.yaml

接著可以通過 32000 埠訪問 Grafana。

賬號密碼都是 admin

至此,我們部署的 Prometheus 監控結構如下:

image-20220210214953273

剛剛進去的時候空空如也,我們需要利用圖表模板製作視覺化介面,才能顯示出漂亮的資料。

在 Grafana 官方網站中,有很多社群製作的免費的模板 https://grafana.com/grafana/dashboards/?search=kubernetes

首先開啟 https://grafana.com/grafana/dashboards/8588 下載這個模板,然後上傳模板檔案,並繫結對應的 Prometheus 資料來源。

image-20220210212926323

image-20220210212952395

image-20220210213043764

接下來就可以看到對應的監控介面了。

image-20220210213221217

你可以開啟 Browse ,繼續匯入更多的模板,然後檢視要顯示的模板監控介面。

image-20220210213337031

應用如何接入 Prometheus 和 Grafana

前面已經提及對基礎設施的監控,我們還可以對中介軟體如 TIDB、Mysql 等生成、收集指標資料,還可以在程式中自定義指標資料,然後自行製作 Grafana 模板。如果你是 .NET 開發,還可以參考筆者的另一篇文章來一步步瞭解這些過程:https://www.cnblogs.com/whuanle/p/14969982.html

告警

在監控體系中,告警是重中之重,一般需要根據公司的實際情況自研告警處理和推送通知元件

我們建議您閱讀 基於 Rob Ewaschuk 在 Google 的觀察的我的警報哲學https://docs.google.com/a/boxever.com/document/d/199PqyG3UsyXlwieHaqbGiWVa8eMWi8zzAn0YfcApr8Q/edit

在前面部署 Prometheus 時,config-map.yaml 便已經定義了一個告警規則。

  prometheus.rules: |-
    groups:
    - name: devopscube demo alert
      rules:
      - alert: High Pod Memory
        expr: sum(container_memory_usage_bytes) > 1
        for: 1m
        labels:
          severity: slack
        annotations:
          summary: High Memory Usage

一條告警規則主要由以下幾部分組成:

  • alert:告警規則的名稱。
  • expr:基於 PromQL 表示式告警觸發條件,用於計算是否有時間序列滿足該條件。
  • for:評估等待時間,可選引數。用於表示只有當觸發條件持續一段時間後才傳送告警。在等待期間新產生告警的狀態為 pending。
  • labels:自定義標籤,允許使用者指定要附加到告警上的一組附加標籤。
  • annotations:用於指定一組附加資訊,比如用於描述告警詳細資訊的文字等,annotations 的內容在告警產生時會一同作為引數傳送到 Alertmanager。

可參考:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/prometheus-alert-rule

image-20220212215935688

在 Grafana 中也可以看到這條規則。

image-20220212220457886

下面我們將來配置告警通知。

首先建立一個告警聯絡方式,筆者使用了釘釘 Webhook。

image-20220212221559333

image-20220212222438650

然後找 Alert Rules,新增一個新的告警規則。

image-20220212225334405

告警規則請參考:https://grafana.com/docs/grafana/latest/alerting/unified-alerting/alerting-rules/create-grafana-managed-rule/

接著開啟 Notification policies,為告警規則和聯絡方式做繫結,符合條件的告警資訊將會被推送到指定的聯絡方式中。

image-20220212223912995

image-20220212225530864

在 Alert Rules 中可以看到告警資訊的推送記錄。由於筆者的伺服器在國外,可能導致伺服器無法使用釘釘的 Webhook 功能,因此這裡一直在 Pending,因此筆者這裡就不再做過多的嘗試了,讀者瞭解大概步驟即可。

image-20220212225643237

image-20220212225835855