Kubernetes 集群和應用監控方案的設計與實踐
- 2022 年 2 月 14 日
- 筆記
- Kubernetes與Docker
Kubernetes 監控
當你的應用部署到 Kubenetes 後,你很難看到容器內部發生了什麼,一旦容器死掉,裡面的數據可能就永遠無法恢復,甚至無法查看日誌以定位問題所在,何況一個應用可能存在很多個實例,用戶的一個請求不指定被哪個容器處理了,這使得在 Kubernetes 中對應用進行故障排除較為複雜。在應用之外,由於 Kubernetes 作為基礎設施,掌管這整個集群的生死,Kubernetes 的任何故障,必定影響到應用服務的運行,因此監控 Kubernetes 運行狀況也至關重要。
當你的應用上了雲原生,那你就不得不關注各個伺服器的運行狀態,基礎設施和中間件的運行狀態,Kubernetes 中每個組件和資源對象的運行狀態,每個應用的運行狀態。當然,這個運行狀態是一個模糊的概念,取決於我們的關注點,每個被監控的對象要表達的 “運行狀態” 是不一樣的。為了可以監控我們關注的對象,對象需要做出一些配合,提供合適的運行狀態的表達資訊,以供我們收集和分析,這可以稱為可觀測性。
在雲原生中,一般對可觀測性分為三大作用域:
你可以在 Kubernetes 文檔中了解如何監控、調試,以及了解如何對日誌進行處理:
//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 的一些組件介紹:
- Metric Collection: Prometheus 使用拉模型通過 HTTP 檢索度量。在 Prometheus 無法獲取指標的情況下,可以選擇利用 Pushgateway 將指標推給 Prometheus 。
- Metric Endpoint: 希望使用 Prometheus 監視的系統應該公開某個/度量端點的度量, Prometheus 利用這個端點以固定的間隔提取指標。
- PromQL: Prometheus 附帶了 PromQL,這是一種非常靈活的查詢語言,可用於查詢 Prometheus 儀錶板中的指標。此外,Prometheus UI 和 Grafana 將使用 PromQL 查詢來可視化指標。
- Prometheus Exporters: 有許多庫和伺服器可以幫助將第三方系統中的現有指標導出為 Prometheus 指標。這對於無法直接使用 Prometheus 指標檢測給定系統的情況。
- TSDB (time-series database): Prometheus 使用 TSDB 高效地存儲所有數據。默認情況下,所有數據都存儲在本地。然而,為了避免單點故障,prometheustsdb 可以選擇集成遠程存儲。
Prometheus 在 Kubernetes 中的監控方案結構如下:
【圖源://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 監控體系。
實踐
節點監控
本章參考資料://devopscube.com/node-exporter-kubernetes/
node exporter 是用 Golang 編寫的,用於在 Linux 系統上,收集內核公開的所有硬體和作業系統級別的指標,包括 CPU 、資訊、網卡流量、系統負載、socket 、機器配置等。
讀者可參考中列舉出的 //github.com/prometheus/node_exporter 中列舉出的所有默認開啟或默認關閉的指標。
既然要監控集群中的每個節點,那麼就要做到每個節點都運行這一個 node exporter 實例,並且在集群新增節點的時候自動調度一個 node exporter 到這個節點中運行,因此需要採用 node exporter 的部署需要 DaemontSet 模式。
查看集群中的所有節點:
root@master:~# 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 //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 實例
root@master:~# 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:
root@master:~# 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
本節參考//devopscube.com/setup-prometheus-monitoring-on-kubernetes/
現在有了 node exporter ,可以收集節點各類指標,接下來便是對 Kubernetes 基礎設施的 metrics 數據收集。
Kubernetes 自身提供的很多 metrics 數據,有三大端點 /metrics/cadvisor
, /metrics/resource
and /metrics/probes
。
以 /metrics/cadvisor
為例,cAdvisor 分析在給定節點上運行的所有容器的記憶體、CPU、文件和網路使用情況的指標,你可以參考 //github.com/google/cadvisor/blob/master/docs/storage/prometheus.md 了解 cAdvisor 的所有指標。
其它資料:
源碼位置://github.com/kubernetes/metrics/blob/master/pkg/apis/metrics/v1beta1/types.go
Kubernetes 監控架構設計://github.com/kubernetes/design-proposals-archive
在本節中,部署的 Prometheus 將會對 kubenetes 進行以下動作以便收集 metrics 數據:
- Kubernetes-apiservers: 從 API 伺服器獲得所有的指標;
- Kubernetes 節點: 它收集所有的 kubernetes 節點指標;
kubernetes-pods
: pod 元數據上加上 prometheus.io/scrape 和 prometheus.io/port 注釋,所有的 pod 指標都會被發現;kubernetes-cadvisor
: 收集所有 cAdvisor 指標,與容器相關;- Kubernetes-Service-endpoints: 如果服務元數據使用 prometheus.io/scrape 注釋和 prometheus.io/port 注釋,那麼所有的服務端點都將被廢棄;
Bibin Wilson 大佬已經封裝好了相關的部署定義文件,我們直接下載即可:
git clone //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 格式編寫,具體規則可以參考://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
你可以打開 //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
查看部署狀態:
root@master:~# 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 查詢顯示。
你還可以在 Service Discobery 中,查看 Prometheus 採集的 metrics 數據源。
如果你的集群沒有安裝過 kube-state-metrics,那麼這個數據源會顯示紅色標記,在下一節中,我們繼續部署這個組件。
至此,我們的監控結構如下所示:
部署 Kube State Metrics
本節參考資料://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 中獲得的一些重要度量:
- Node status, node capacity (CPU and memory)
- Replica-set compliance (desired/available/unavailable/updated status of replicas per deployment)
- Pod status (waiting, running, ready, etc)
- Ingress metrics
- PV, PVC metrics
- Daemonset & Statefulset metrics.
- Resource requests and limits.
- Job & Cronjob metrics
可以在這裡的文檔中查看受支援的詳細指標://github.com/kubernetes/kube-state-metrics/tree/master/docs
Bibin Wilson 大佬已經封裝好了相關的部署定義文件,我們直接下載即可:
git clone //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
上面創建的資源,包含以下部分,這一小節,就不展開講解。
- Service Account
- Cluster Role
- Cluster Role Binding
- Kube State Metrics Deployment
- Service
使用以下命令檢查部署狀態:
kubectl get deployments kube-state-metrics -n kube-system
隨後,刷新 Prometheus Service Discobery ,可以看到紅色變成了藍色,點擊此數據源,可以看到以下資訊:
- job_name: 'kube-state-metrics'
static_configs:
- targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080']
此配置為 kube-state-metrics 的訪問地址。
在此,我們部署的 Prometeus 結構如下:
部署 Grafana
本節參考資料://devopscube.com/setup-grafana-kubernetes/
經過前面幾個小節的部署,已經搞好數據源的採集以及數據存儲,接下來我們將部署 Grafana,利用 Grafana 對指標數據進行分析以及可視化。
Bibin Wilson 大佬已經封裝好了相關的部署定義文件,我們直接下載即可:
git clone //github.com/bibinwilson/kubernetes-grafana.git
首先查看 grafana-datasource-config.yaml
文件,此配置是為了 Grafana 自動配置好 Prometheus 數據源。
裡面還有一個很重要的地址:
"url": "//prometheus-service.monitoring.svc:8080",
首先你要使用命令測試 curl //prometheus-service.monitoring.svc:8080
,看看能不能獲取到響應數據,如果出現:
root@master:~/jk/kubernetes-prometheus# curl //prometheus-deployment.monitoring.svc:8080
curl: (6) Could not resolve host: prometheus-deployment.monitoring.svc
root@master:~/jk/kubernetes-prometheus# curl //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:
root@master:~/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 訪問是否正常
root@master:~/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 監控結構如下:
剛剛進去的時候空空如也,我們需要利用圖表模板製作可視化介面,才能顯示出漂亮的數據。
在 Grafana 官方網站中,有很多社區製作的免費的模板 //grafana.com/grafana/dashboards/?search=kubernetes
首先打開 //grafana.com/grafana/dashboards/8588 下載這個模板,然後上傳模板文件,並綁定對應的 Prometheus 數據源。
接下來就可以看到對應的監控介面了。
你可以打開 Browse ,繼續導入更多的模板,然後查看要顯示的模板監控介面。
應用如何接入 Prometheus 和 Grafana
前面已經提及對基礎設施的監控,我們還可以對中間件如 TIDB、Mysql 等生成、收集指標數據,還可以在程式中自定義指標數據,然後自行製作 Grafana 模板。如果你是 .NET 開發,還可以參考筆者的另一篇文章來一步步了解這些過程://www.cnblogs.com/whuanle/p/14969982.html
告警
在監控體系中,告警是重中之重,一般需要根據公司的實際情況自研告警處理和推送通知組件。
我們建議您閱讀 基於 Rob Ewaschuk 在 Google 的觀察的我的警報哲學//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。
可參考://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/prometheus-alert-rule
在 Grafana 中也可以看到這條規則。
下面我們將來配置告警通知。
首先創建一個告警聯繫方式,筆者使用了釘釘 Webhook。
然後找 Alert Rules,添加一個新的告警規則。
接著打開 Notification policies,為告警規則和聯繫方式做綁定,符合條件的告警資訊將會被推送到指定的聯繫方式中。
在 Alert Rules 中可以看到告警資訊的推送記錄。由於筆者的伺服器在國外,可能導致伺服器無法使用釘釘的 Webhook 功能,因此這裡一直在 Pending,因此筆者這裡就不再做過多的嘗試了,讀者了解大概步驟即可。