概述
已經有了cadvisor、heapster(metric server),幾乎容器運行的所有指標都能拿到,但是下面這種情況卻無能為力:
- 我調度了多少個replicas?現在可用的有幾個?
- 多少個Pod是running/stopped/terminated狀態?
- Pod重啟了多少次?
- 我有多少job在運行中
而這些則是kube-state-metrics提供的內容,它基於client-go開發,輪詢Kubernetes API,並將Kubernetes的結構化信息轉換為metrics。
功能
kube-state-metrics提供的指標,按照階段分為三種類別:
- 1.實驗性質的:k8s api中alpha階段的或者spec的字段。
- 2.穩定版本的:k8s中不向后兼容的主要版本的更新
- 3.被廢棄的:已經不在維護的。
指標類別包括:
- CronJob Metrics
- DaemonSet Metrics
- Deployment Metrics
- Job Metrics
- LimitRange Metrics
- Node Metrics
- PersistentVolume Metrics
- PersistentVolumeClaim Metrics
- Pod Metrics
- Pod Disruption Budget Metrics
- Horizontal Pod Autoscaler Metrics
- Endpoint Metrics
- Secret Metrics
- ConfigMap Metrics
- …..
以pod為例:
- kube_pod_info
- kube_pod_owner
- kube_pod_status_phase
- kube_pod_status_ready
- kube_pod_status_scheduled
- kube_pod_container_status_waiting
- kube_pod_container_status_terminated_reason
- …
使用
部署清單:
kube-state-metrics/ ├── kube-state-metrics-cluster-role-binding.yaml ├── kube-state-metrics-cluster-role.yaml ├── kube-state-metrics-deployment.yaml ├── kube-state-metrics-role-binding.yaml ├── kube-state-metrics-role.yaml ├── kube-state-metrics-service-account.yaml ├── kube-state-metrics-service.yaml
主要鏡像有:
image: quay.io/coreos/kube-state-metrics:v1.5.0
image: k8s.gcr.io/addon-resizer:1.8.3(參考metric server文章,用於擴縮容)
對於pod的資源限制,一般情況下:
200MiB memory 0.1 cores
超過100節點的集群:
2MiB memory per node 0.001 cores per node
kube-state-metrics做過一次性能優化,具體內容參考下文
部署成功后,prometheus的target會出現如下標志
因為kube-state-metrics-service.yaml中有prometheus.io/scrape: ‘true’標識,因此會將metric暴露給prometheus,而Prometheus會在kubernetes-service-endpoints這個job下自動發現kube-state-metrics,並開始拉取metrics,無需其他配置。
使用kube-state-metrics后的常用場景有:
- 存在執行失敗的Job: kube_job_status_failed{job=”kubernetes-service-endpoints”,k8s_app=”kube-state-metrics”}==1
- 集群節點狀態錯誤: kube_node_status_condition{condition=”Ready”,status!=”true”}==1
- 集群中存在啟動失敗的Pod:kube_pod_status_phase{phase=~”Failed|Unknown”}==1
- 最近30分鍾內有Pod容器重啟: changes(kube_pod_container_status_restarts[30m])>0
配合報警可以更好地監控集群的運行
與metric-server的對比
- metric-server(或heapster)是從api-server中獲取cpu、內存使用率這種監控指標,並把他們發送給存儲后端,如influxdb或雲廠商,他當前的核心作用是:為HPA等組件提供決策指標支持。
- kube-state-metrics關注於獲取k8s各種資源的最新狀態,如deployment或者daemonset,之所以沒有把kube-state-metrics納入到metric-server的能力中,是因為他們的關注點本質上是不一樣的。metric-server僅僅是獲取、格式化現有數據,寫入特定的存儲,實質上是一個監控系統。而kube-state-metrics是將k8s的運行狀況在內存中做了個快照,並且獲取新的指標,但他沒有能力導出這些指標
- 換個角度講,kube-state-metrics本身是metric-server的一種數據來源,雖然現在沒有這么做。
- 另外,像Prometheus這種監控系統,並不會去用metric-server中的數據,他都是自己做指標收集、集成的(Prometheus包含了metric-server的能力),但Prometheus可以監控metric-server本身組件的監控狀態並適時報警,這里的監控就可以通過kube-state-metrics來實現,如metric-serverpod的運行狀態。
深入解析
kube-state-metrics本質上是不斷輪詢api-server,代碼結構也很簡單
主要代碼目錄
. ├── collectors │ ├── builder.go │ ├── collectors.go │ ├── configmap.go │ ...... │ ├── testutils.go │ ├── testutils_test.go │ └── utils.go ├── constant │ └── resource_unit.go ├── metrics │ ├── metrics.go │ └── metrics_test.go ├── metrics_store │ ├── metrics_store.go │ └── metrics_store_test.go ├── options │ ├── collector.go │ ├── options.go │ ├── options_test.go │ ├── types.go │ └── types_test.go ├── version │ └── version.go └── whiteblacklist ├── whiteblacklist.go └── whiteblacklist_test.go
所有類型:
var ( DefaultNamespaces = NamespaceList{metav1.NamespaceAll} DefaultCollectors = CollectorSet{ "daemonsets": struct{}{}, "deployments": struct{}{}, "limitranges": struct{}{}, "nodes": struct{}{}, "pods": struct{}{}, "poddisruptionbudgets": struct{}{}, "replicasets": struct{}{}, "replicationcontrollers": struct{}{}, "resourcequotas": struct{}{}, "services": struct{}{}, "jobs": struct{}{}, "cronjobs": struct{}{}, "statefulsets": struct{}{}, "persistentvolumes": struct{}{}, "persistentvolumeclaims": struct{}{}, "namespaces": struct{}{}, "horizontalpodautoscalers": struct{}{}, "endpoints": struct{}{}, "secrets": struct{}{}, "configmaps": struct{}{}, } )
構建對應的收集器
Family即一個類型的資源集合,如job下的kube_job_info、kube_job_created,都是一個FamilyGenerator實例
metrics.FamilyGenerator{ Name: "kube_job_info", Type: metrics.MetricTypeGauge, Help: "Information about job.", GenerateFunc: wrapJobFunc(func(j *v1batch.Job) metrics.Family { return metrics.Family{&metrics.Metric{ Name: "kube_job_info", Value: 1, }} }), },
func (b *Builder) buildCronJobCollector() *Collector { // 過濾傳入的白名單 filteredMetricFamilies := filterMetricFamilies(b.whiteBlackList, cronJobMetricFamilies) composedMetricGenFuncs := composeMetricGenFuncs(filteredMetricFamilies) // 將參數寫到header中 familyHeaders := extractMetricFamilyHeaders(filteredMetricFamilies) // NewMetricsStore實現了client-go的cache.Store接口,實現本地緩存。 store := metricsstore.NewMetricsStore( familyHeaders, composedMetricGenFuncs, ) // 按namespace構建Reflector,監聽變化 reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1beta1.CronJob{}, store, b.namespaces, createCronJobListWatch) return NewCollector(store) }
性能優化:
kube-state-metrics在之前的版本中暴露出兩個問題:
- metrics接口響應慢(10-20s)
- 內存消耗太大,導致超出limit被殺掉
問題一的方案就是基於client-go的cache tool實現本地緩存,具體結構為:
var cache = map[uuid][]byte{}
問題二的的方案是:對於時間序列的字符串,是存在很多重復字符的(如namespace等前綴篩選),可以用指針或者結構化這些重復字符。
優化點和問題
- 1.因為kube-state-metrics是監聽資源的add、delete、update事件,那么在kube-state-metrics部署之前已經運行的資源,豈不是拿不到數據?kube-state-metric利用client-go可以初始化所有已經存在的資源對象,確保沒有任何遺漏
- 2.kube-state-metrics當前不會輸出metadata信息(如help和description)
- 3.緩存實現是基於golang的map,解決並發讀問題當期是用了一個簡單的互斥鎖,應該可以解決問題,后續會考慮golang的sync.Map安全map。
- 4.kube-state-metrics通過比較resource version來保證event的順序
- 5.kube-state-metrics並不保證包含所有資源
監控數據展示
基於kube-state-metrics的監控數據,可以組裝一些常用的監控面板,如下面的grafana面板