使用 vmagent 代替 Prometheus 采集監(jiān)控指標
vmagent 可以幫助我們從各種來源收集指標并將它們存儲在?VM 或者任何其他支持 remote write 協(xié)議的 Prometheus 兼容的存儲系統(tǒng)中。
特性
vmagent 相比于 Prometheus 抓取指標來說具有更多的靈活性,比如除了拉取(pull)指標還可以推送(push)指標,此外還有很多其他特性:
可以替換 prometheus 的 scraping target 支持從 Kafka 讀寫數(shù)據(jù) 支持基于 prometheus relabeling 的模式添加、移除、修改 labels,可以在數(shù)據(jù)發(fā)送到遠端存儲之前進行數(shù)據(jù)的過濾 支持多種數(shù)據(jù)協(xié)議,influx line 協(xié)議,graphite 文本協(xié)議,opentsdb 協(xié)議,prometheus remote write 協(xié)議,json lines 協(xié)議,csv 數(shù)據(jù)等 支持收集數(shù)據(jù)的同時,并復制到多種遠端存儲系統(tǒng) 支持不可靠遠端存儲,如果遠程存儲不可用,收集的指標會在 -remoteWrite.tmpDataPath緩沖,一旦與遠程存儲的連接被修復,緩沖的指標就會被發(fā)送到遠程存儲,緩沖區(qū)的最大磁盤用量可以用-remoteWrite.maxDiskUsagePerURL來限制。相比 prometheus 使用更少的內(nèi)存、cpu、磁盤 io 以及網(wǎng)絡帶寬 當需要抓取大量目標時,抓取目標可以分散到多個 vmagent 實例中 可以通過在抓取時間和將其發(fā)送到遠程存儲系統(tǒng)之前限制唯一時間序列的數(shù)量來處理高基數(shù)和高流失率問題 可以從多個文件中加載 scrape 配置

部署
接下來我們以抓取 Kubernetes 集群指標為例說明如何使用 vmagent,我們這里使用自動發(fā)現(xiàn)的方式來進行配置。vmagent 是兼容 prometheus 中的 kubernetes_sd_configs 配置的,所以我們同樣可以使用。
要讓 vmagent 自動發(fā)現(xiàn)監(jiān)控的資源對象,需要訪問 APIServer 獲取資源對象,所以首先需要配置 rbac 權(quán)限,創(chuàng)建如下所示的資源清單。
#?vmagent-rbac.yaml
apiVersion:?v1
kind:?ServiceAccount
metadata:
??name:?vmagent
??namespace:?kube-vm
---
apiVersion:?rbac.authorization.k8s.io/v1
kind:?ClusterRole
metadata:
??name:?vmagent
rules:
??-?apiGroups:?["",?"networking.k8s.io",?"extensions"]
????resources:
??????-?nodes
??????-?nodes/metrics
??????-?services
??????-?endpoints
??????-?endpointslices
??????-?pods
??????-?app
??????-?ingresses
????verbs:?["get",?"list",?"watch"]
??-?apiGroups:?[""]
????resources:
??????-?namespaces
??????-?configmaps
????verbs:?["get"]
??-?nonResourceURLs:?["/metrics",?"/metrics/resources"]
????verbs:?["get"]
---
apiVersion:?rbac.authorization.k8s.io/v1
kind:?ClusterRoleBinding
metadata:
??name:?vmagent
roleRef:
??apiGroup:?rbac.authorization.k8s.io
??kind:?ClusterRole
??name:?vmagent
subjects:
??-?kind:?ServiceAccount
????name:?vmagent
????namespace:?kube-vm
然后添加 vmagent 配置,我們先只配置自動發(fā)現(xiàn) Kubernetes 節(jié)點的任務,創(chuàng)建如下所示的 ConfigMap 對象:
#?vmagent-config.yaml
apiVersion:?v1
kind:?ConfigMap
metadata:
??name:?vmagent-config
??namespace:?kube-vm
data:
??scrape.yml:?|
????global:
??????scrape_interval:?15s
??????scrape_timeout:?15s
????scrape_configs:
????-?job_name:?nodes
??????kubernetes_sd_configs:
????????-?role:?node
??????relabel_configs:
??????-?source_labels:?[__address__]
????????regex:?"(.*):10250"
????????replacement:?"${1}:9111"
????????target_label:?__address__
????????action:?replace
??????-?action:?labelmap
????????regex:?__meta_kubernetes_node_label_(.+)
這里我們通過自動發(fā)現(xiàn) Kubernetes 節(jié)點獲取節(jié)點監(jiān)控指標,需要注意 node 這種 role 的自動發(fā)現(xiàn)默認獲取的是節(jié)點的 10250 端口,這里我們需要通過 relabel 將其 replace 為 9111。
然后添加 vmagent 部署資源清單,如下所示:
#?vmagent-deploy.yaml
apiVersion:?v1
kind:?PersistentVolumeClaim
metadata:
??name:?vmagent-pvc
??namespace:?kube-vm
spec:
??accessModes:
????-?ReadWriteOnce
??resources:
????requests:
??????storage:?1Gi
??storageClassName:?nfs-client
---
apiVersion:?apps/v1
kind:?Deployment
metadata:
??name:?vmagent
??namespace:?kube-vm
??labels:
????app:?vmagent
spec:
??selector:
????matchLabels:
??????app:?vmagent
??template:
????metadata:
??????labels:
????????app:?vmagent
????spec:
??????serviceAccountName:?vmagent
??????containers:
????????-?name:?agent
??????????image:?"victoriametrics/vmagent:v1.77.0"
??????????imagePullPolicy:?IfNotPresent
??????????args:
????????????-?-promscrape.config=/config/scrape.yml
????????????-?-remoteWrite.tmpDataPath=/tmpData
????????????-?-remoteWrite.url=http://vminsert:8480/insert/0/prometheus
????????????-?-envflag.enable=true
????????????-?-envflag.prefix=VM_
????????????-?-loggerFormat=json
??????????ports:
????????????-?name:?http
??????????????containerPort:?8429
??????????volumeMounts:
????????????-?name:?tmpdata
??????????????mountPath:?/tmpData
????????????-?name:?config
??????????????mountPath:?/config
??????volumes:
????????-?name:?tmpdata
??????????persistentVolumeClaim:
????????????claimName:?vmagent-pvc
????????-?name:?config
??????????configMap:
????????????name:?vmagent-config
我們將 vmagent 配置通過 ConfigMap 掛載到容器 /config/scrape.yml,另外通過 -remoteWrite.url=http://vminsert:8480/insert/0/prometheus 指定遠程寫入的地址,這里我們寫入前面的 vminsert 服務,另外有一個參數(shù) -remoteWrite.tmpDataPath,該路徑會在遠程存儲不可用的時候用來緩存收集的指標,當遠程存儲修復后,緩存的指標就會被正常發(fā)送到遠程寫入,所以最好持久化該目錄。
集群模式
單個 vmagent 實例可以抓取數(shù)萬個抓取目標,但是有時由于 CPU、網(wǎng)絡、內(nèi)存等方面的限制,這還不夠。在這種情況下,抓取目標可以在多個 vmagent 實例之間進行拆分。集群中的每個 vmagent 實例必須使用具有不同 -promscrape.cluster.memberNum 值的相同 -promscrape.config 配置文件,該參數(shù)值必須在 0 ... N-1 范圍內(nèi),其中 N 是集群中 vmagent 實例的數(shù)量。集群中 vmagent 實例的數(shù)量必須傳遞給 -promscrape.cluster.membersCount 命令行標志。例如,以下命令可以在兩個 vmagent 實例的集群中傳播抓取目標:
vmagent?-promscrape.cluster.membersCount=2?-promscrape.cluster.memberNum=0?-promscrape.config=/path/config.yml?...
vmagent?-promscrape.cluster.membersCount=2?-promscrape.cluster.memberNum=1?-promscrape.config=/path/config.yml?...
當 vmagent 在 Kubernetes 中運行時,可以將 -promscrape.cluster.memberNum 設置為 StatefulSet pod 名稱,pod 名稱必須以 0 ... promscrape.cluster.memberNum-1 范圍內(nèi)的數(shù)字結(jié)尾,例如,-promscrape.cluster.memberNum=vmagent-0。
默認情況下,每個抓取目標僅由集群中的單個 vmagent 實例抓取。如果需要在多個 vmagent 實例之間復制抓取目標,則可以通過 -promscrape.cluster.replicationFactor 參數(shù)設置為所需的副本數(shù)。例如,以下命令啟動一個包含三個 vmagent 實例的集群,其中每個目標由兩個 vmagent 實例抓取:
vmagent?-promscrape.cluster.membersCount=3?-promscrape.cluster.replicationFactor=2?-promscrape.cluster.memberNum=0?-promscrape.config=/path/to/config.yml?...
vmagent?-promscrape.cluster.membersCount=3?-promscrape.cluster.replicationFactor=2?-promscrape.cluster.memberNum=1?-promscrape.config=/path/to/config.yml?...
vmagent?-promscrape.cluster.membersCount=3?-promscrape.cluster.replicationFactor=2?-promscrape.cluster.memberNum=2?-promscrape.config=/path/to/config.yml?...
需要注意的是如果每個目標被多個 vmagent 實例抓取,則必須在 -remoteWrite.url 指向的遠程存儲上啟用重復數(shù)據(jù)刪除。
所以如果你抓取的監(jiān)控目標非常大,那么我們建議使用 vmagent 集群模式,那么可以使用 StatefulSet 方式進行部署
#?vmagent-sts.yaml
apiVersion:?v1
kind:?Service
metadata:
??name:?vmagent
??namespace:?kube-vm
??annotations:
????prometheus.io/scrape:?"true"
????prometheus.io/port:?"8429"
spec:
??selector:
????app:?vmagent
??clusterIP:?None
??ports:
????-?name:?http
??????port:?8429
??????targetPort:?http
---
apiVersion:?apps/v1
kind:?StatefulSet
metadata:
??name:?vmagent
??namespace:?kube-vm
??labels:
????app:?vmagent
spec:
??replicas:?2
??serviceName:?vmagent
??selector:
????matchLabels:
??????app:?vmagent
??template:
????metadata:
??????labels:
????????app:?vmagent
????spec:
??????serviceAccountName:?vmagent
??????containers:
????????-?name:?agent
??????????image:?victoriametrics/vmagent:v1.77.0
??????????imagePullPolicy:?IfNotPresent
??????????args:
????????????-?-promscrape.config=/config/scrape.yml
????????????-?-remoteWrite.tmpDataPath=/tmpData
????????????-?-promscrape.cluster.membersCount=2
????????????#?-?-promscrape.cluster.replicationFactor=2?#?可以配置副本數(shù)
????????????-?-promscrape.cluster.memberNum=$(POD_NAME)
????????????-?-remoteWrite.url=http://vminsert:8480/insert/0/prometheus
????????????-?-envflag.enable=true
????????????-?-envflag.prefix=VM_
????????????-?-loggerFormat=json
??????????ports:
????????????-?name:?http
??????????????containerPort:?8429
??????????env:
????????????-?name:?POD_NAME
??????????????valueFrom:
????????????????fieldRef:
??????????????????fieldPath:?metadata.name
??????????volumeMounts:
????????????-?name:?tmpdata
??????????????mountPath:?/tmpData
????????????-?name:?config
??????????????mountPath:?/config
??????volumes:
????????-?name:?config
??????????configMap:
????????????name:?vmagent-config
??volumeClaimTemplates:
????-?metadata:
????????name:?tmpdata
??????spec:
????????accessModes:
??????????-?ReadWriteOnce
????????storageClassName:?nfs-client
????????resources:
??????????requests:
????????????storage:?1Gi
我們這里就使用 StatefulSet 的形式來管理 vmagent,直接應用上面的資源即可:
#?先將前面示例中的?prometheus?停掉
????kubectl?scale?deploy?prometheus?--replicas=0?-n?kube-vm
????kubectl?apply?-f?vmagent-rbac.yaml
????kubectl?apply?-f?vmagent-config.yaml
????kubectl?apply?-f?vmagent-sts.yaml
????kubectl?get?pods?-n?kube-vm?-l?app=vmagent
NAME????????READY???STATUS????RESTARTS???AGE
vmagent-0???1/1?????Running???0??????????3m43s
vmagent-1???1/1?????Running???0??????????2m9s
這里我們部署了兩個 vmagent 實例來抓取監(jiān)控指標,我們這里一共 3 個節(jié)點。
????kubectl?get?nodes
NAME??????STATUS???ROLES??????????????????AGE???VERSION
master1???Ready????control-plane,master???44d???v1.22.8
node1?????Ready?????????????????????44d???v1.22.8
node2?????Ready?????????????????????44d???v1.22.8
所以兩個 vmagent 實例會分別采集部分指標,我們可以通過查看日志來進行驗證:
????kubectl?logs?-f?vmagent-0?-n?kube-vm
#?......
{"ts":"2022-05-10T04:44:44.004Z","level":"info","caller":"VictoriaMetrics/lib/promscrape/scraper.go:393","msg":"static_configs:?added?targets:?1,?removed?targets:?0;?total?targets:?1"}
{"ts":"2022-05-10T04:44:44.006Z","level":"info","caller":"VictoriaMetrics/lib/promscrape/scraper.go:393","msg":"kubernetes_sd_configs:?added?targets:?2,?removed?targets:?0;?total?targets:?2"}
????kubectl?logs?-f?vmagent-1?-n?kube-vm
#?......
{"ts":"2022-05-10T04:46:17.893Z","level":"info","caller":"VictoriaMetrics/lib/promscrape/scraper.go:393","msg":"kubernetes_sd_configs:?added?targets:?1,?removed?targets:?0;?total?targets:?1"}
從日志可以看出 vmagent-0 實例發(fā)現(xiàn)了 2 個 targets,vmagent-1 實例發(fā)現(xiàn)了 1 個 targets,這也符合我們預期的。
接下來我們再新增其他內(nèi)容的監(jiān)控,比如 APIServer、容器等等,配置如下所示:
#?vmagent-config2.yaml
apiVersion:?v1
kind:?ConfigMap
metadata:
??name:?vmagent-config
??namespace:?kube-vm
data:
??scrape.yml:?|
????global:
??????scrape_interval:?15s
??????scrape_timeout:?15s
????scrape_configs:
????-?job_name:?nodes
??????kubernetes_sd_configs:
????????-?role:?node
??????relabel_configs:
??????-?source_labels:?[__address__]
????????regex:?"(.*):10250"
????????replacement:?"${1}:9111"
????????target_label:?__address__
????????action:?replace
??????-?action:?labelmap
????????regex:?__meta_kubernetes_node_label_(.+)
????-?job_name:?apiserver
??????scheme:?https
??????bearer_token_file:?/var/run/secrets/kubernetes.io/serviceaccount/token
??????tls_config:
????????ca_file:?/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
????????insecure_skip_verify:?true
??????kubernetes_sd_configs:
??????-?role:?endpoints
??????relabel_configs:
??????-?action:?keep
????????regex:?default;kubernetes;https
????????source_labels:
????????-?__meta_kubernetes_namespace
????????-?__meta_kubernetes_service_name
????????-?__meta_kubernetes_endpoint_port_name
????-?job_name:?cadvisor
??????bearer_token_file:?/var/run/secrets/kubernetes.io/serviceaccount/token
??????scheme:?https
??????tls_config:
????????ca_file:?/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
????????insecure_skip_verify:?true
??????kubernetes_sd_configs:
??????-?role:?node
??????relabel_configs:
??????-?action:?labelmap
????????regex:?__meta_kubernetes_node_label_(.+)
??????-?replacement:?/metrics/cadvisor
????????target_label:?__metrics_path__
????-?job_name:?endpoints
??????kubernetes_sd_configs:
??????-?role:?endpoints
??????relabel_configs:
??????-?action:?drop
????????regex:?true
????????source_labels:
????????-?__meta_kubernetes_pod_container_init
??????-?action:?keep_if_equal
????????source_labels:
????????-?__meta_kubernetes_service_annotation_prometheus_io_port
????????-?__meta_kubernetes_pod_container_port_number
??????-?action:?keep
????????regex:?true
????????source_labels:
????????-?__meta_kubernetes_service_annotation_prometheus_io_scrape
??????-?action:?replace
????????regex:?(https?)
????????source_labels:
????????-?__meta_kubernetes_service_annotation_prometheus_io_scheme
????????target_label:?__scheme__
??????-?action:?replace
????????regex:?(.+)
????????source_labels:
????????-?__meta_kubernetes_service_annotation_prometheus_io_path
????????target_label:?__metrics_path__
??????-?action:?replace
????????regex:?([^:]+)(?::\d+)?;(\d+)
????????replacement:?$1:$2
????????source_labels:
????????-?__address__
????????-?__meta_kubernetes_service_annotation_prometheus_io_port
????????target_label:?__address__
??????-?action:?labelmap
????????regex:?__meta_kubernetes_service_label_(.+)
??????-?source_labels:
????????-?__meta_kubernetes_pod_name
????????target_label:?pod
??????-?source_labels:
????????-?__meta_kubernetes_namespace
????????target_label:?namespace
??????-?source_labels:
????????-?__meta_kubernetes_service_name
????????target_label:?service
??????-?replacement:?${1}
????????source_labels:
????????-?__meta_kubernetes_service_name
????????target_label:?job
??????-?action:?replace
????????source_labels:
????????-?__meta_kubernetes_pod_node_name
????????target_label:?node
大部分的配置在前面 Prometheus 章節(jié)都介紹過了,核心就是通過 relabel_configs 來控制抓取的任務,vmagent 是兼容傳統(tǒng)的 prometheus 重新標記規(guī)則的,但也有一些獨特的 action,比如上面配置中我們使用了一個 keep_if_equal 的操作,該操作的意思是如果指定的標簽值相等則將該條數(shù)據(jù)保留下來。
有時,如果某個指標包含兩個具有相同值的標簽,則需要刪除它。這可以通過 vmagent 支持的 drop_if_equal 操作來完成。例如,如果以下 relabel 規(guī)則包含 real_port 和 required_port 的相同標簽值,則它會刪除指標:
-?action:?drop_if_equal
??source_labels:?[real_port,?needed_port]
該規(guī)則將刪除以下指標:foo{real_port="123",needed_port="123"},但會保留以下指標:foo{real_port="123",needed_port="456"}。
有時可能需要只對指標子集應用 relabel,在這種情況下,可以將 if 選項添加到 relabel_configs 規(guī)則中,例如以下規(guī)則僅將 {foo="bar"} 標簽添加到與 metric{label=~"x|y"} 序列選擇器匹配的指標:
-?if:?'metric{label=~"x|y"}'
??target_label:?"foo"
??replacement:?"bar"
if 選項可以簡化傳統(tǒng)的 relabel_configs 規(guī)則,例如,以下規(guī)則可以刪除與 foo{bar="baz"} 序列選擇器匹配的指標:
-?if:?'foo{bar="baz"}'
??action:?drop
這相當于以下傳統(tǒng)的規(guī)則:
-?action:?drop
??source_labels:?[__name__,?bar]
??regex:?"foo;baz"
不過需要注意的是 Prometheus 還不支持 if 選項,現(xiàn)在只支持 VictoriaMetrics。
現(xiàn)在更新 vmagent 的配置。
????kubectl?apply?-f?vmagent-config2.yaml
配置刷新有兩種方式:
發(fā)送 SUGHUP 信號給 vmagent 進程 向 http://vmagent:8429/-/reload發(fā)送一個 http 請求
刷新后就可以開始采集上面的指標了,同樣我們也可以通過 http://vmselect/select/0/vmui/ 來訪問 vmui,比如現(xiàn)在我們來查詢 pod 的內(nèi)存使用率,可以使用如下的查詢語句:
sum(container_memory_working_set_bytes{image!=""}) by(namespace, pod) / sum(container_spec_memory_limit_bytes{image!=""}) by(namespace, pod) * 100 != +inf

vmagent 作為采集指標重要的一環(huán),當然對它的監(jiān)控也不可少。vmagent 通過 http://vmagent:8429/metrics 暴露了很多指標,如 vmagent_remotewrite_conns 遠程存儲連接,vm_allowed_memory_bytes 可使用的內(nèi)存大小,我們把一些重要的指標收集起來,通過 Grafana 進行展示,能夠更好的幫助我們分析 vmagent 的狀態(tài)。
我們可以使用 https://grafana.com/grafana/dashboards/12683 來展示 vmagent 的狀態(tài)。

