使用 Prometheus Pushgateway 推送監(jiān)控指標(biāo)
我們知道 Prometheus 采用的 pull 模式,但是某些網(wǎng)絡(luò)場景下面(比如不在一個(gè)子網(wǎng)或者防火墻),Prometheus 無法直接拉取監(jiān)控指標(biāo)數(shù)據(jù),這個(gè)時(shí)候我們可能就需要一種能夠主動(dòng) push 的模式了。而 Pushgateway 就是 Prometheus 生態(tài)中來解決這個(gè)問題的一個(gè)工具。
但是 Pushgateway 也不是萬能的,其本身也存在一些弊端:
將多個(gè)節(jié)點(diǎn)數(shù)據(jù)匯總到 pushgateway, 如果 pushgateway 掛了,受影響范圍更大 Prometheus 拉取狀態(tài) up 只針對(duì) pushgateway, 無法做到對(duì)每個(gè)目標(biāo)有效
由于 Pushgateway 可以持久化推送給它的所有監(jiān)控?cái)?shù)據(jù),所以即使你的監(jiān)控已經(jīng)下線,Prometheus 還會(huì)拉取到舊的監(jiān)控?cái)?shù)據(jù),需要手動(dòng)清理 Pushgateway 不要的數(shù)據(jù)。

Pushgateway 的存在是為了允許臨時(shí)和批處理作業(yè)向 Prometheus 暴露其指標(biāo)。由于這些類型的任務(wù)可能存在的時(shí)間不夠長而無法被抓取,因此他們可以將指標(biāo)推送到 Pushgateway,然后 Pushgateway 將這些指標(biāo)暴露給 Prometheus。有一點(diǎn)我們需要明白的是 Pushgateway 不是將指標(biāo)主動(dòng) push 給 Prometheus,而是通過腳本將指標(biāo)數(shù)據(jù)主動(dòng) push 給 Pushgateway 后,Prometheus 仍然通過 pull 模式去抓取指標(biāo)。
前面我們也介紹過 node-exporter 中的 textfile 收集器也可以用來采集指標(biāo),似乎和 Pushgateway 比較相似,這二者有什么區(qū)別嗎?textfile 通常是用于節(jié)點(diǎn)級(jí)別的指標(biāo),而 Pushgateway 是用于服務(wù)級(jí)別的指標(biāo)。
安裝
同樣要安裝 Pushgateway 也非常簡單,直接從 Release 頁面 下載適用于你平臺(tái)的二進(jìn)制版本并解壓即可使用。如果你想從源碼自行編譯,可以在代碼根目錄下面直接執(zhí)行 make 命令即可編譯。
直接執(zhí)行 Pushgateway 二進(jìn)制文件即可啟動(dòng)了,要更改監(jiān)聽的地址,可以通過 --web.listen-address 標(biāo)志(例如0.0.0.0:9091或:9091)指定。默認(rèn)情況下 Pushgateway 不保留指標(biāo)。但是 --persistence.file 標(biāo)志允許我們指定一個(gè)文件,將推送的指標(biāo)保存在其中,這樣當(dāng) Pushgateway 重新啟動(dòng)后指標(biāo)仍然存在。
此外當(dāng)然我們也可以直接使用 Docker 鏡像來進(jìn)行啟動(dòng):
docker run -d -p 9091:9091 prom/pushgateway
同樣我們這里還是將 Pushgateway 部署在 Kubernetes 集群中,對(duì)應(yīng)的資源清單文件如下所示:
# pushgateway.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pushgateway-data
namespace: kube-mon
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: pushgateway
namespace: kube-mon
labels:
app: pushgateway
spec:
selector:
matchLabels:
app: pushgateway
template:
metadata:
labels:
app: pushgateway
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: pushgateway-data
containers:
- name: pushgateway
image: prom/pushgateway:v1.4.3
imagePullPolicy: IfNotPresent
args:
- "--persistence.file=/data"
ports:
- containerPort: 9091
name: http
volumeMounts:
- mountPath: "/data"
name: data
resources:
requests:
cpu: 100m
memory: 500Mi
limits:
cpu: 100m
memory: 500Mi
---
apiVersion: v1
kind: Service
metadata:
name: pushgateway
namespace: kube-mon
labels:
app: pushgateway
spec:
selector:
app: pushgateway
type: NodePort
ports:
- name: http
port: 9091
targetPort: http
這里我們 --persistence.file 指定了持久化的文件,然后通過一個(gè) Service 來暴露了服務(wù),直接應(yīng)用上面的資源清單文件即可:
? ? kubectl apply -f https://p8s.io/docs/pushgateway/manifests/pushgateway.yaml
? ? kubectl get pods -n kube-mon -l app=pushgateway
NAME READY STATUS RESTARTS AGE
pushgateway-7684cbb67d-6mbjn 1/1 Running 0 99s
? ? kubectl get svc -n kube-mon -l app=pushgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
pushgateway NodePort 10.106.136.207 <none> 9091:30893/TCP 107s
默認(rèn)情況下 Pushgateway 會(huì)提供一個(gè)簡單的 Web 頁面,可以查看當(dāng)前具有哪些指標(biāo)。

基本使用
Pushgateway 的數(shù)據(jù)推送支持兩種方式,Prometheus Client SDK 推送和 API 推送。
Client SDK 推送
Prometheus 本身提供了支持多種語言的 SDK,可通過 SDK 的方式,生成相關(guān)的數(shù)據(jù),并推送到 Pushgateway,當(dāng)然這種方式需要客戶端代碼支持,這也是官方推薦的方案。目前的 SDK 覆蓋語言有官方的:
Go Java or Scala Python Ruby
也有許多第三方的庫,詳情可參見此鏈接:https://prometheus.io/docs/instrumenting/clientlibs/
這里我們以 Python 為例進(jìn)行說明。
首先安裝 Prometheus 的 Python SDK:
? ? pip install prometheus-client
然后創(chuàng)建一個(gè)名為 app.py 的文件,內(nèi)容如下所示:
from prometheus_client import CollectorRegistry, Counter, Gauge, push_to_gateway
registry = CollectorRegistry()
g = Gauge('container_memory', 'Container memory data', ['node'], registry=registry)
g.labels(node='node1').inc() # +1
g.labels(node='node2').dec(10) # -10
g.labels(node='node3').set(4.2) # 4.2
c = Counter('my_requests_total', 'HTTP requests total', ['method', 'endpoint'], registry=registry)
c.labels(method='get', endpoint='/').inc()
c.labels(method='post', endpoint='/submit').inc()
push_to_gateway('192.168.0.106:30893', job='batchA', registry=registry)
首先引入了 Prometheus 的 Python SDK,然后創(chuàng)建了一個(gè) CollectorRegistry 實(shí)例,分別創(chuàng)建了一個(gè) Gauge 和 Counter 類型的指標(biāo),其中第一個(gè)參數(shù)為指標(biāo)名稱,第二個(gè)參數(shù)為指標(biāo)的注釋信息,第三個(gè)參數(shù)為相關(guān)的標(biāo)簽,然后為不同的標(biāo)簽值設(shè)置不同的指標(biāo)值,最后通過 push_to_gateway 函數(shù)將指標(biāo)數(shù)據(jù)發(fā)送到指定的 Pushgateway 服務(wù)上去。
直接執(zhí)行上面的 Python 文件即可將數(shù)據(jù)推送到 Pushgateway:
? ? python app.py

API 推送
此外使用 Prometheus 文本協(xié)議,推送指標(biāo)也是非常簡單的,無需提供單獨(dú)的 CLI。只需使用 curl 之類的命令行工具即可。
不過需要注意,在文本協(xié)議中,每一行都必須以換行符結(jié)尾('LF' 或 '\n'),以其他方式結(jié)束一行,例如使用 'CR'('\r')、'CRLF'('\r\n')或只是數(shù)據(jù)包的結(jié)尾,將導(dǎo)致協(xié)議錯(cuò)誤。
推送的指標(biāo)按組進(jìn)行管理,由任意數(shù)量的標(biāo)簽組成的分組鍵識(shí)別,其中第一個(gè)必須是 job 標(biāo)簽。
比如我們現(xiàn)在將單個(gè)樣本推送到由 {job="some_job"} 標(biāo)識(shí)的組中:
? ? echo "some_metric 3.14" | curl --data-binary @- http://192.168.0.106:30893/metrics/job/some_job
需要注意由于沒有提供類型信息, 所以這里的 some_metric 將是 UNTYPED 無類型的。
上面的命令執(zhí)行完后我們可以重新前往 Pushgateway 的 Web 頁面查看。

可以看到頁面中就出現(xiàn)了上面我們推送的 some_metric 這個(gè)指標(biāo),而且位于 job="some_job" 這個(gè)分組下面。
接下來我們?cè)偻扑鸵恍┫鄬?duì)復(fù)雜的指標(biāo),推到 {job="some_job",instance="some_instance"} 標(biāo)識(shí)的分組中去,繼續(xù)之前記得將上面的分組刪除,然后執(zhí)行下面的命令:
? ? cat <<EOF | curl --data-binary @- http://192.168.0.106:30893/metrics/job/some_job/instance/some_instance
# TYPE some_metric counter
some_metric{label="val1"} 42
# TYPE another_metric gauge
# HELP another_metric Just an example.
another_metric 2398.283
EOF
該命令會(huì)將指標(biāo)推送到 {job="some_job",instance="some_instance"} 標(biāo)識(shí)的分組中去,要注意指標(biāo)內(nèi)容中是如何提供類型信息和幫助信息的,這里我們的指標(biāo)是都具有類型了。

在 Web 界面中就可以看到上面推送的兩個(gè)指標(biāo)了,一個(gè)是 COUNTER 類型,一個(gè)是 GAUGE 類型。
如果你想刪除某個(gè)分組下面的所有指標(biāo),我們也可以通過 curl 命令來實(shí)現(xiàn)。比如刪除由 {job="some_job",instance="some_instance"} 標(biāo)識(shí)的分組中的所有指標(biāo),可以通過下面的命令來實(shí)現(xiàn):
? ? curl -X DELETE http://192.168.0.106:30893/metrics/job/some_job/instance/some_instance
這里需要注意的是刪除是根據(jù)分組標(biāo)識(shí)進(jìn)行刪除的,比如我們要?jiǎng)h除由 {job="some_job"} 標(biāo)識(shí)的分組中的所有指標(biāo),那么可以用下面的命令來實(shí)現(xiàn):
? ? curl -X DELETE http://192.168.0.106:30893/metrics/job/some_job
但是需要注意這里是不包括上面示例 {job="some_job",instance="some_instance"} 分組中的指標(biāo)的,即使這些指標(biāo)具有相同的 job 標(biāo)簽,這點(diǎn)非常重要。
如果你想刪除所有組中的所有指標(biāo),可以使用下面的命令來實(shí)現(xiàn):
? ? curl -X PUT http://192.168.0.106:30893/api/v1/admin/wipe
但是需要注意需要通過 Pushgateway 的命令行標(biāo)志 --web.enable-admin-api 來啟用管理 API。
抓取指標(biāo)
現(xiàn)在我們需要將 Pushgateway 的指標(biāo)配置到 Prometheus 中去,讓 Prometheus 去主動(dòng)抓取 Pushgateway 的指標(biāo)數(shù)據(jù),我們當(dāng)然也可以使用服務(wù)發(fā)現(xiàn)的方式,這里我們單獨(dú)為 Pushgateway 創(chuàng)建一個(gè)抓取任務(wù),在 Prometheus 中添加如下所示的抓取配置:
scrape_configs:
- job_name: "pushgateway"
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels:
[__meta_kubernetes_namespace, __meta_kubernetes_service_name]
action: keep
regex: kube-mon;pushgateway
# 省略其他配置......
我們這里同樣采用基于 endpoints 的自動(dòng)發(fā)現(xiàn)的方式來進(jìn)行配置,去匹配 kube-mon 這個(gè)命名空間下名為 pushgateway 的服務(wù)即可。正常更新上面的配置后 Prometheus 就會(huì)自動(dòng)去發(fā)現(xiàn) Pushgateway 這個(gè)服務(wù)了。

現(xiàn)在我們重新推送下面的指標(biāo):
? ? cat <<EOF | curl --data-binary @- http://192.168.0.106:30893/metrics/job/some_job/instance/some_instance
# TYPE some_metric counter
some_metric{label="val1"} 42
# TYPE another_metric gauge
# HELP another_metric Just an example.
another_metric 2398.283
EOF
推送完成后我們就可以在 Prometheus 中去查詢 some_metric 和 another_metric 這兩個(gè)指標(biāo)了。

Prometheus 會(huì)給每個(gè)抓取的指標(biāo)附加一個(gè) job 和 instance 的標(biāo)簽,job 標(biāo)簽來自 scrape 配置,我們這里抓取 Pushgateway 的 job 標(biāo)簽為 job="pushgateway",instance 標(biāo)簽的值會(huì)自動(dòng)設(shè)置為抓取目標(biāo)的主機(jī)和端口,所以所有從 Pushgateway 抓取的指標(biāo)都會(huì)有 Pushgateway 的主機(jī)和端口作為 instance 標(biāo)簽,但是這可能會(huì)和你附加推送到 Pushgateway 指標(biāo)上的 job 和 instance 標(biāo)簽沖突,這個(gè)時(shí)候 Prometheus 會(huì)將這些標(biāo)簽重命名為 exported_job 和 exported_instance。
但是,在抓取 Pushgateway 時(shí),通常不希望出現(xiàn)這種行為。更多的時(shí)候你可能更希望保留推送到 Pushgateway 的指標(biāo)的 job 和 instance 標(biāo)簽,這個(gè)時(shí)候我們只需要在 Pushgateway 的抓取配置中設(shè)置 honor_labels: true 即可,我們重新更新 Prometheus 的配置:
scrape_configs:
- job_name: "pushgateway"
honor_labels: true
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels:
[__meta_kubernetes_namespace, __meta_kubernetes_service_name]
action: keep
regex: kube-mon;pushgateway
# 省略其他配置......
上面抓取 Pushgateway 的任務(wù)中我們新增了一個(gè) honor_labels: true 的配置,更新后我們重新去查詢推送到 Pushgateway 中的兩個(gè)指標(biāo)。

可以看到 job 和 instance 這兩個(gè)標(biāo)簽變成了我們推送到 Pushgateway 中的標(biāo)簽值了,這個(gè)可能更符合我們的預(yù)期。
另外需要注意的是 Pushgateway 不提供任何強(qiáng)一致性保證,沒有高可用的方案,它可以做的最好的事情就是將每個(gè)指定時(shí)間段的指標(biāo)保存到磁盤了,總之不到萬不得已的情況下最好別用 Pushgateway。
對(duì) Prometheus 感興趣的朋友也可以關(guān)注下我們的《Prometheus入門到實(shí)戰(zhàn)》的課程,現(xiàn)在屬于上新優(yōu)惠價(jià)格,已經(jīng)更新了80小節(jié)內(nèi)容,還在持續(xù)更新中......
