基于Prometheus的K8S监控报警设计
- K8S系列
- 2025-10-20
- 367热度
- 1评论
监控数据来源
这里是个通用的问题,不管使用什么监控系统,都要采集这些监控数据,只不过实现的方式不一样
主要有5种:物理节点层面、pod及容器层面,k8s组件(控制面+node面+插件)层面,k8s的资源对象状态,业务层自定义指标
下面详细说明:
- 第1种-物理节点的资源使用情况:例如CPU、内存、硬盘、网络、文件描述符、负载等宿主机资源的健康状态
- 使用node-exporter实现,以daemonset方式运行在每个node上
- 提供方式为/metrics接口
- 提供例如node_cpu_seconds_total、node_memory_MemAvailable_bytes等指标
-
# netstat -unptl | grep -i node_exporte tcp6 0 0 :::9100 :::* LISTEN 105611/node_exporte # curl -s http://127.0.0.1:9100/metrics | grep node_memory_MemAvailable_bytes node_memory_MemAvailable_bytes 8.904409088e+09
- 第2种-pod及其内部容器的资源使用情况:pod是k8s中调度的最小单元,而pod中可以有1个或者多个容器。所以需要可以精确的知晓pod及容器级别的CPU、内存、硬盘、网络、文件描述符、负载等使用情况
- 底层使用cadvisor,目前已经集成到了kubelet中(10250端口),不需要额外部署
- 提供方式为/metrics/cadvisor (需要证书认证,不能直接访问)
- 指标例如container_cpu_usage_seconds_total、container_memory_working_set_bytes等
-
# netstat -unptl | grep kubelet tcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 33949/kubelet tcp6 0 0 :::10250 :::* LISTEN 33949/kubelet # curl -k https://127.0.0.1:10250/metrics/cadvisor Unauthorized
- 第3种- k8s资源对象的状态:注意,这里并不是使用情况,因为k8s的设计理念是设置目标状态,然后各个资源对象去达到这个目标状态。因此,这里更关注的是当前的状态是否正常,例如拿deployment对象举例,会包括这个deployment的目标副本总数,当前副本数,重启次数等等关键信息,帮助我们快速去判断,这个目标对象的状态是否稳定
- 使用kube-state-metrics实现,一般是以deployment的方式部署在集群中
- 提供方式为/metrics接口
- 提供例如kube_deployment_status_replicas_available、kube_pod_container_status_restarts_total等指标
-
# kubectl get deployment -n monitoring NAME READY UP-TO-DATE AVAILABLE AGE prometheus-stack-grafana 1/1 1 1 39d prometheus-stack-kube-prom-operator 1/1 1 1 39d prometheus-stack-kube-state-metrics 1/1 1 1 39d #kubectl get svc -n monitoring | grep kube-state-metrics prometheus-stack-kube-state-metrics ClusterIP 10.100.228.75 <none> 8080/TCP 39d #curl -s http://10.100.228.75:8080/metrics | grep kube_deployment_status_replicas_available kube_deployment_status_replicas_available{namespace="tigera-operator",deployment="tigera-operator"} 1 kube_deployment_status_replicas_available{namespace="monitoring",deployment="prometheus-stack-kube-prom-operator"} 1 kube_deployment_status_replicas_available{namespace="calico-system",deployment="whisker"} 1 kube_deployment_status_replicas_available{namespace="calico-system",deployment="calico-kube-controllers"} 1 kube_deployment_status_replicas_available{namespace="monitoring",deployment="prometheus-stack-kube-state-metrics"} 1 kube_deployment_status_replicas_available{namespace="calico-system",deployment="goldmane"} 1 kube_deployment_status_replicas_available{namespace="calico-apiserver",deployment="calico-apiserver"} 2 kube_deployment_status_replicas_available{namespace="monitoring",deployment="prometheus-stack-grafana"} 1 kube_deployment_status_replicas_available{namespace="kube-system",deployment="coredns"} 2 kube_deployment_status_replicas_available{namespace="kube-system",deployment="metrics-server"} 1 kube_deployment_status_replicas_available{namespace="calico-system",deployment="calico-typha"} 3
- 第4种-k8s自身组件的健康情况:包括控制面的api server、controller manager、scheduler、etcd,以及node层面的kubelet、kube-proxy,以及一些核心插件例如coredns等等
- 实现方式:各个组件自身内置了采集和暴露监控指标数据的功能
- 提供方式也是通用的,为/metrics接口
- 提供例如apiserver_request_total、etcd_server_has_leader等各个组件的健康度,qps,延迟,错误率等状态信息
-
这里以kube-proxy为例,其他的组件需要证书认证 # netstat -unptl | grep kube-proxy tcp6 0 0 :::10256 :::* LISTEN 93982/kube-proxy tcp6 0 0 :::10249 :::* LISTEN 93982/kube-proxy # curl -s http://127.0.0.1:10249/metrics | grep kubeproxy_sync_proxy_rules_service_changes_total 89308
- 第5种-业务应用的自定义指标
- 实现方式:业务自身通过prometheus client库暴露的/metrics接口
- 一般会暴露业务的qps、延迟、订单数据等业务层的核心指标,后续可以利用这些指标进行配置应用层的报警逻辑,或者提供业务容量数据以进行扩容等操作
补充说明-metrics-server
我们在使用kubectl top node|pod 命令的时候,底层依赖的是metrics-server服务
因此,如果在k8s集群安装的过程中没有把这个组件安装上,那么我们执行这些命令的时候,是会报错的
下面是正常的输出
# kubectl top pod -A | tail -5
monitoring prometheus-stack-prometheus-node-exporter-g5n4k 1m 14Mi
monitoring prometheus-stack-prometheus-node-exporter-hn5tn 3m 14Mi
monitoring prometheus-stack-prometheus-node-exporter-tnr7h 2m 14Mi
monitoring prometheus-stack-prometheus-node-exporter-zpn4z 3m 14Mi
tigera-operator tigera-operator-755d956888-k5twc 3m 128Mi
# kubectl top node
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
host1 144m 3% 7746Mi 48%
host2 96m 2% 7659Mi 48%
host3 104m 2% 7398Mi 46%
host4 160m 4% 8050Mi 50%
host5 153m 3% 7960Mi 50%
host6 378m 9% 9320Mi 58%
metrics-server的作用:汇聚所有kubelet的监控数据,进行聚合,然后暴露给kubectl和集群HPA功能进行使用
特点
- 数据来源:kubelet,从所有node节点上的kubelet中获取数据
- 只汇聚和展示CPU、内存 这2个资源类型的实时用量(不存储历史数据,默认只滚动保留最新15分钟的数据)
- 默认60秒采集一次,可以配置metric-resolution进行修改,例如--metric-resolution=15s
- 数据使用方/消费者为:kubectl、HPA、VPA,也就是说只提供集群本身查看并作为扩容的背景数据
- 因为只提供给集群本身使用,因此它不暴露prometheus格式的/metrics接口端点
- prometheus不需要,也无法从metrics-server中采集数据
metrics-server的安装步骤如下
1、下载yaml文件
# wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.8.0/components.yaml
2、修改内容
image: docker.1ms.run/bitnami/metrics-server:0.8.0
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
image: docker.1ms.run/bitnami/metrics-server:0.8.0
说明:
1、添加kubelet-insecure-tls,在采集kubelet数据的时候,跳过证书校验
2、将镜像的来源,修改成国内的加速地址,解决网络限制的问题
3、采集时间从60秒调整为15秒
3、执行apply
# kubectl apply -f components.yaml
kube-prometheus-stack集成方案
这是目前的一个集成方案,其中包含
- Prometheus、Alertmanager等配套组件
- node-exporter、kube-state-metrics等实现k8s集群监控所需要的服务
- 提供了例如ServiceMonitor、PodMonitor等CRD,简化Prometheus监控栈的部署和管理
kube-prometheus-stack的安装
安装步骤
1、安装helm,版本要求3以上
# curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash #helm version version.BuildInfo{Version:"v3.18.6", GitCommit:"b76a950f6835474e0906b96c9ec68a2eff3a6430", GitTreeState:"clean", GoVersion:"go1.24.6"}
2、添加helm仓库
# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
# helm update
上面是使用的官方源,如果不稳定,下面替换成国内的源
先删除上面的
# helm repo remove prometheus-community
# kubectl delete namespace monitoring
然后再
# helm repo add prometheus-community "https://helm-charts.itboon.top/prometheus-community" --force-update
# helm repo update
3、修改镜像源地址
# helm pull prometheus-community/kube-prometheus-stack --untar
# cd /root/kube-prometheus-stack 修改valuse.yaml文件
默认的配置如下:
# grep registry values.yaml | grep -v '#'
registry: docker.io
registry: registry.k8s.io
registry: quay.io
registry: quay.io
registry: registry.k8s.io
registry: quay.io
registry: quay.io
registry: quay.io
registry: quay.io
registry: quay.io
修改命令:
1)替换主 values.yaml 中的镜像源
# sed -i 's|registry.k8s.io|k8s.m.daocloud.io|g' values.yaml
# sed -i 's|quay.io|quay.m.daocloud.io|g' values.yaml
# sed -i 's|docker.io|docker.m.daocloud.io|g' values.yaml
2)替换子 chart 中的镜像源(如 kube-state-metrics)
# sed -i 's|registry.k8s.io|k8s.m.daocloud.io|g' charts/kube-state-metrics/values.yaml
# sed -i 's|quay.io|quay.m.daocloud.io|g' charts/kube-state-metrics/values.yaml
# sed -i 's|registry.k8s.io|k8s.m.daocloud.io|g' ./charts/grafana/values.yaml ./charts/prometheus-node-exporter/values.yaml
# sed -i 's|quay.io|quay.m.daocloud.io|g' ./charts/grafana/values.yaml ./charts/prometheus-node-exporter/values.yaml
# sed -i 's|docker.io|docker.m.daocloud.io|g' ./charts/grafana/values.yaml
# sed -i 's|ghcr.io|ghcr.m.daocloud.io|g' ./kube-prometheus-stack/charts/prometheus-windows-exporter/values.yaml
4、执行安装命令
# helm install prometheus-stack ./ --namespace monitoring --create-namespace
输出如下:
NAME: prometheus-stack
LAST DEPLOYED: Fri Sep 12 17:10:48 2025
NAMESPACE: monitoring
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
kubectl --namespace monitoring get pods -l "release=prometheus-stack"
Get Grafana 'admin' user password by running:
kubectl --namespace monitoring get secrets prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Access Grafana local instance:
export POD_NAME=$(kubectl --namespace monitoring get pod -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=prometheus-stack" -oname)
kubectl --namespace monitoring port-forward $POD_NAME 3000
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.
效果如下:
# kubectl get pods -n monitoring
NAME READY STATUS RESTARTS AGE
alertmanager-prometheus-stack-kube-prom-alertmanager-0 2/2 Running 0 6m12s
prometheus-prometheus-stack-kube-prom-prometheus-0 2/2 Running 0 6m12s
prometheus-stack-grafana-596f485c88-s8qvz 3/3 Running 0 6m14s
prometheus-stack-kube-prom-operator-598bcf855f-szpsj 1/1 Running 0 6m14s
prometheus-stack-kube-state-metrics-678545f4f-gnzhk 1/1 Running 0 6m14s
prometheus-stack-prometheus-node-exporter-4v28g 1/1 Running 0 6m14s
prometheus-stack-prometheus-node-exporter-95md9 1/1 Running 0 6m14s
prometheus-stack-prometheus-node-exporter-g5n4k 1/1 Running 0 6m14s
prometheus-stack-prometheus-node-exporter-hn5tn 1/1 Running 0 6m14s
prometheus-stack-prometheus-node-exporter-tnr7h 1/1 Running 0 6m14s
prometheus-stack-prometheus-node-exporter-zpn4z 1/1 Running 0 6m14s
这些pod的作用是:
- node-exporter 这个作用很清晰,是daemonsets类型,部署在每个node上,收集宿主机的信息使用
- alertmanager-prometheus-stack-kube-prom-alertmanager-0。告警收敛、静默、路由。Prometheus 算出要告警(触发报警规则)后,把消息推给它,它再按配置文件把告警发到 Slack、邮件、钉钉等各种渠道。
- prometheus-prometheus-stack-kube-prom-prometheus-0。核心 Prometheus Server。负责抓取所有 Exporter、Kubernetes 组件、ServiceMonitor 定义的指标,并做 TSDB 存储与查询。
- prometheus-stack-grafana-596f485c88-s8qvz。可视化面板。从 Prometheus 读取指标,提供仪表盘、告警面板、权限管理等。容器里还带了 Grafana-sidecar(自动加载仪表盘)和 init-container(配置数据源)。
- prometheus-stack-kube-prom-operator-598bcf855f-szpsj。监控栈的“大管家”。监听 CRD(ServiceMonitor、PodMonitor、PrometheusRule、AlertmanagerConfig 等),自动生成 Prometheus/Alertmanager 的配置并滚动更新对应的 StatefulSet
- prometheus-stack-kube-state-metrics-678545f4f-gnzhk。把 Kubernetes 对象转成指标,监听 APIServer,然后暴露出可以给prometheus采集的metrics接口。生成 Deployment 副本数、Pod 重启次数、Node 状态等“元数据”指标,供 Prometheus 抓取
流程:Prometheus 抓取数据 → 进行判断→ 触发规则 → 生成 Alert → 推给 Alertmanager → Alertmanager 路由/降噪 → 发告警消息
下面是一张速记表
|
Pod(简写)
|
一句话职责
|
|
alertmanager-0
|
收告警、发通知
|
|
prometheus-0
|
存指标、算告警、发告警
|
|
grafana-xxx
|
画图表、看面板
|
|
operator-xxx
|
管采集配置、管生命周期
|
|
kube-state-metrics-xxx
|
生成K8s对象的状态监控指标
|
|
node-exporter-xxx
|
生成物理节点的资源监控指标
|
kube-prometheus-stack的使用
grafana的账号密码
账号是admin,密码见下方
# kubectl --namespace monitoring get secrets prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
prom-operator
各平台访问地址
要访问的平台有:
- prometheus,默认端口是9090
- alertmanager,默认端口是9093
- grafana,默认端口是3000
在上面的输出中,有一些简单的访问提示和指引
Access Grafana local instance:
# export POD_NAME=$(kubectl --namespace monitoring get pod -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=prometheus-stack" -oname)
# kubectl --namespace monitoring port-forward $POD_NAME 3000
例如
# kubectl --namespace monitoring port-forward $POD_NAME 3000
# kubectl -n monitoring port-forward svc/prometheus-stack-grafana 8080:80
说明
#kubectl -n monitoring port-forward <资源类型/名称> [本地端口]:[Pod端口]
不加资源类型的时候,默认是使用的pod
因为我的一些网络限制,在这里,我使用的方案是,将相关服务的svc设置为nodeport类型,然后在中转机器上配置端口转发
1、svc设置
# kubectl get svc -n monitoring
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
alertmanager-operated ClusterIP None <none> 9093/TCP,9094/TCP,9094/UDP 3d17h
prometheus-operated ClusterIP None <none> 9090/TCP 3d17h
prometheus-stack-grafana NodePort 10.108.188.200 <none> 3000:30424/TCP 3d17h
prometheus-stack-kube-prom-alertmanager NodePort 10.103.216.169 <none> 9093:31441/TCP,8080:31147/TCP 3d17h
prometheus-stack-kube-prom-operator ClusterIP 10.97.11.162 <none> 443/TCP 3d17h
prometheus-stack-kube-prom-prometheus NodePort 10.102.99.12 <none> 9090:32643/TCP,8080:31500/TCP 3d17h
prometheus-stack-kube-state-metrics ClusterIP 10.100.228.75 <none> 8080/TCP 3d17h
prometheus-stack-prometheus-node-exporter ClusterIP 10.110.3.231 <none> 9100/TCP 3d17h
设置之后:
- prometheus 访问端口:32643
- alertmanager,访问端口:31441
- grafana,访问端口:30424
因为网络的问题,我在一台机器上配置了iptables的转发
- prometheus,转发至其中1台节点的32643,外层继续使用:9090
- alertmanager,转发至其中1台节点的31441,外层继续使用:9093
- grafana,转发至其中1台节点的30424,外层继续使用:3000
2、iptables配置
1、安装iptables
2、设置ipv4 forward 转发
3、添加iptables规则
# 1. 启用 IP 转发(如果尚未启用)
echo 1 > /proc/sys/net/ipv4/ip_forward
# 2. 设置 DNAT 规则(目标地址转换)
iptables -t nat -A PREROUTING -p tcp --dport 9090 -j DNAT --to-destination node_ip:32643
# 3. 设置 MASQUERADE 规则(源地址伪装)
iptables -t nat -A POSTROUTING -p tcp -d node_ip --dport 32643 -j MASQUERADE
# 4. 确保转发链允许相关流量(如果防火墙默认拒绝)
iptables -A FORWARD -p tcp -d node_ip --dport 32643 -j ACCEPT
iptables -A FORWARD -p tcp -s node_ip --sport 32643 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp --dport 9093 -j DNAT --to-destination node_ip:31441
iptables -t nat -A POSTROUTING -p tcp -d node_ip --dport 31441 -j MASQUERADE
iptables -A FORWARD -p tcp -d node_ip --dport 31441 -j ACCEPT
iptables -A FORWARD -p tcp -s node_ip --sport 31441 -j ACCEPT
和
iptables -t nat -A PREROUTING -p tcp --dport 3000 -j DNAT --to-destination node_ip:30424
iptables -t nat -A POSTROUTING -p tcp -d node_ip --dport 30424 -j MASQUERADE
iptables -A FORWARD -p tcp -d node_ip --dport 30424 -j ACCEPT
iptables -A FORWARD -p tcp -s node_ip --sport 30424 -j ACCEPT
配置完成之后,访问这台中转机器即可,地址如下:
- grafana:http://host_ip:3000
- prometheus:http://host_ip:9090
- alertmanager:http://host_ip:9093
常见问题处理
【监听127问题】
进入了prometheus的页面中,会看到很多组件,例如controller-manager、etcd、kube-proxy、api server、scheduler等组件的采集页面会出现:
Error scraping target: Get "http://ip:10257/metrics":dial tcp ip:10257: connect: connection refused
问题原因是因为默认情况下,这些组件在启动的时候,监听的是127.0.0.1,无法通过节点的ip进行访问,因此要进行修改
解决方式如下
# pwd
/etc/kubernetes/manifests
# cat etcd.yaml | grep metrics
- --listen-metrics-urls=http://0.0.0.0:2381
# cat kube-apiserver.yaml | grep 'advertise-address='
- --advertise-address=10.128.129.151
# cat kube-controller-manager.yaml | grep bind-address
- --bind-address=0.0.0.0
# cat kube-scheduler.yaml| grep bind-address
- --bind-address=0.0.0.0
# kubectl get cm -n kube-system kube-proxy -o yaml
metricsBindAddress: ""
其中的metrics配置如果不写,那么默认就是127,这里我们设置为: # kubectl edit cm -n kube-system kube-proxy metricsBindAddress: "0.0.0.0:10249"
# kubectl -n kube-system rollout restart ds kube-proxy
【grafana的时区问题】
# kubectl edit cm -n monitoring prometheus-stack-grafana -o yaml
在data的部分
data:
grafana.ini: |
[analytics]
check_for_updates = true
[grafana_net]
url = https://grafana.net
[log]
mode = console
[paths]
data = /var/lib/grafana/
logs = /var/log/grafana
plugins = /var/lib/grafana/plugins
provisioning = /etc/grafana/provisioning
[server]
domain = ''
[date_formats]
default_timezone = Asia/Shanghai
末尾添加这2行
[date_formats]
default_timezone = Asia/Shanghai
然后滚动升级grafana
# kubectl rollout restart deployment prometheus-stack-grafana -n monitoring
deployment.apps/prometheus-stack-grafana restarted
Prometheus的数据查询
PromQL语法介绍
PromQL是Prometheus内置的数据查询语言,全称为Prometheus Query Language。它允许用户实时选择和聚合时间序列数据,从而生成图表、表格和警报。
一个重要的信息:Prometheus中存储的所有指标数据都是时间序列数据。
一个完整的指标数据会包括:指标名称及其标签
- 指标名称: 表示被测量系统的某个特征(如 http_requests_total)。
- 标签: 提供多维度的数据属性(如 method="POST", status="200", endpoint="/api/users")。
例如现在看其中1个node的disk_io情况
查询语句:node_disk_io_now{instance='node_ip:9100'}
输出为:
node_disk_io_now{container="node-exporter", device="vda", endpoint="http-metrics", instance="node_ip:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 0
node_disk_io_now{container="node-exporter", device="vdb", endpoint="http-metrics", instance="node_ip:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 0
通过job="node-exporter"可以知道这个数据的来源是node-exporter
【PromQL的4种使用方式】
PromQL表达式,按照输出的结果类型,有4种使用方式
- 瞬时向量: 在同一时间戳下的一组时间序列及其对应的单个样本值。主要用于当前状态查询。
- 范围向量: 在一段时间范围内的一组时间序列及其对应的一系列样本值。主要用于分析数据变化趋势。
- 标量: 一个简单的数字浮点值。
- 字符串: 一个简单的字符串值(目前未使用)
下面详细说明
瞬时向量查询
直接使用指标名称进行查询,会返回当前时间序列中,所有拥有该指标名称的数据点(这个数据点也叫样本)
例如:
promql:http_requests_total
or
promql:http_requests_total{job="apiserver", status="200"}
通过 {} 来添加标签过滤器,进行更精确的查询。
标签匹配运算符({}中的语句)包括:
=: 等于
!=: 不等于
=~: 正则匹配
!~: 正则不匹配
示例:查询所有 status 以 4xx 开头的时间序列。
promql:http_requests_total{status=~"4.."}
范围向量查询-range和offset
在上面瞬时向量的基础上,在后面添加一个时间范围选择器 [ ],来获取一段时间内的样本。
例如:
promql:http_requests_total{job="apiserver"}[5m]
promql:http_requests_total{job="apiserver"}[2h]
时间单位:
s - 秒
m - 分钟
h - 小时
d - 天
w - 周
y - 年
示例:查询过去 1 小时内的请求总数。
promql:http_requests_total[1h]
上面的是正向的范围数据查询,在此基础之上,还可以查询过去的时间点,也即添加偏移量
偏移量查询:使用 offset 关键字可以查询过去某个时间点的数据,而不是当前时间。
# 查询一天前的瞬时数据
promql:http_requests_total offset 1d
# 查询一天前,过去5分钟内的数据
promql:http_requests_total[5m] offset 1d
标量
只有大小,没有方向的一个数值
它是一个简单的数字浮点值。它没有时间序列的维度信息(即没有指标名称和标签),也没有时间戳。
它就是一个纯粹的数字,比如 3.14、-42、0。
示例如下:对获取到的指标数据进行计算后再输出
promql:node_memory_MemTotal_bytes{instance='node_ip:9100'}
输出:
node_memory_MemTotal_bytes{container="node-exporter", endpoint="http-metrics", instance="node_ip:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 16786505728
promql:node_memory_MemTotal_bytes{instance='node_ip:9100'}/(1024^3)
输出:
{container="node-exporter", endpoint="http-metrics", instance="node_ip:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 15.633651733398438
标量更多的使用场景:
1)在算术运算中作为操作数:
# 将所有请求率乘以一个固定的系数
rate(http_requests_total[5m]) * 0.1
2)在比较运算中作为阈值:
# 找出内存使用率超过 90(这个标量)的节点
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
3)作为函数的参数:
# histogram_quantile 函数的第一个参数就是一个标量(分位数)
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
4)在条件判断中(如 alertmanarer 的配置):
在告警规则中,你经常会看到类似
expr: <vector> > <scalar> (指标数据大于指定标量的报警表达式)。
重要特点:标量在 HTTP API 的响应中,会以一个带时间戳的数组形式返回,这是因为所有表达式都是在某个特定时间点被评估的。例如,一个标量 10 在 API 中的响应可能是 [1234567890, "10"]。(前面的1234567890表示时间戳)
字符串
字符串的“未使用”指的是在PromQL的数值计算、聚合逻辑、核心运算、函数等地方不涉及,字符串处理并不是 PromQL 的主要目标。
- 因为PromQL的设计初衷是处理数值型的监控指标数据,它的强项是计算速率、百分比、分位数、求和等。
- 和字符串相关的label_replace和label_join等函数只是为了辅助重组时间序列的标识(标签),以便于更好地进行数值聚合和筛选,而不是为了处理字符串内容本身。
- 如果你需要对日志内容(本质是字符串)进行查询和分析,你应该使用专门的日志系统,如 Loki(与 Prometheus 同属 Grafana 生态)或 ELK Stack。
因此
- 你不能直接将字符串作为算术运算符(+, -, *, /)的操作数。
- 你不能将字符串用于比较运算符(>, <, == 等),除非使用 bool 修饰符,但即使这样也用处不大。
- 大多数内置函数(如 rate(), sum(), avg_over_time())不接受也不返回字符串。
字符串的使用场景主要是
- 修改标签
- 创建新标签-纯新增
- 创建新标签-基于现有标签进行join连接
示例 1:label_replace 函数(修改标签、创建标签)
这个函数用于修改或创建标签,它的大部分参数都是字符串。
# 为 up 指标添加一个新标签 `service_name`,其值来源于 `job` 标签的值
promql:label_replace(up, "service_name", "$1", "job", "(.*)")
"service_name": 新标签的名称(字符串)。
"$1": 替换值,这里使用了正则匹配的组(字符串)。
"job": 源标签名(字符串)。
"(.*)": 正则表达式(字符串)。
示例 2:label_join 函数
这个函数用于将多个标签的值连接成一个新的标签值。
# 将 instance 和 job 标签的值用冒号连接起来,创建一个新标签 `full_identity`
promql:label_join(up, "full_identity", ":", "instance", "job")
"full_identity": 新标签的名称(字符串)。
":": 连接符(字符串)。
"instance", "job": 要连接的源标签名(字符串)。
注意:label_replace和 label_join 这2个函数,仅对当次查询返回的指标结果产生效果,并不会修改Prometheus底层存储的原始指标,也不会直接影响后续查询或指标生成。
也就是说:这些操作不是持久化的,只是一个临时操作
范围向量查询和子查询
范围向量选择器:[1d] (这里只会产生1个值) 可以理解为是一个瞬时向量
子查询:[1d:] or [1d:1h]
子查询:
- 把前面这个表达式,在过去1天内每X时间计算一次,给我结果序列 (一串值) 可以理解结果是一个范围向量了
- 子查询前面可以再接sum、avg等,去计算产生的这些结果序列的值
案例:
计算所有命名空间在过去24小时的平均CPU使用率
promql:avg( avg_over_time(sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace)[1d:10m]))
如果使用的是[1d]
promql:avg( avg_over_time(sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace)[1d]))
那么输出是:
Error executing query
invalid parameter "query": 1:83: parse error: ranges only allowed for vector selectors
因为avg()函数处理的是一组数据,[1d]只会产生一个值,[1d:]会按照指定的频率去计算N次,得到N个数值。
PromQL中的运算符
其实就是对promql获取到的指标数据(初始情况下数据量往往会很多),做各种运算、逻辑处理,去获取到我们需要的最精确精准的数据
简单来说就是:从一堆数据(注意,不是所有数据,因为有基础的匹配条件)中,通过一些方式,找到我们需要的那些数据
算术运算符:+, -, *, /, % (取模), ^ (幂运算)
# 计算内存使用率 (已用内存 / 总内存)
promql:(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
比较运算符:==, !=, >, <, >=, <=
比较运算符通常用于 bool 修饰符,返回 0 (false) 或 1 (true)。
常规的用法,也叫过滤模式
# 内存使用率超过 80% 的节点
promql:(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 80
如果没有符合的数据,那么输出是
Empty query result
This query returned no data.
我把80改成20之后,输出如下
{container="node-exporter", endpoint="http-metrics", instance="node_ip1:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 38.4250615137907
{container="node-exporter", endpoint="http-metrics", instance="node_ip2:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-hn5tn", service="prometheus-stack-prometheus-node-exporter"} 35.078899145249096
{container="node-exporter", endpoint="http-metrics", instance="node_ip3:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-95md9", service="prometheus-stack-prometheus-node-exporter"} 38.442513548399695
{container="node-exporter", endpoint="http-metrics", instance="node_ip4:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-g5n4k", service="prometheus-stack-prometheus-node-exporter"} 40.47056440888473
{container="node-exporter", endpoint="http-metrics", instance="node_ip5:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-4v28g", service="prometheus-stack-prometheus-node-exporter"} 36.58668169739915
{container="node-exporter", endpoint="http-metrics", instance="node_ip6:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-zpn4z", service="prometheus-stack-prometheus-node-exporter"} 47.07152042203173
下面添加上bool修饰符,也叫布尔模式
promql:(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > bool 80
输出变成
{container="node-exporter", endpoint="http-metrics", instance="node_ip1:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 0
{container="node-exporter", endpoint="http-metrics", instance="node_ip2:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-hn5tn", service="prometheus-stack-prometheus-node-exporter"} 0
{container="node-exporter", endpoint="http-metrics", instance="node_ip3:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-95md9", service="prometheus-stack-prometheus-node-exporter"} 0
{container="node-exporter", endpoint="http-metrics", instance="node_ip4:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-g5n4k", service="prometheus-stack-prometheus-node-exporter"} 0
{container="node-exporter", endpoint="http-metrics", instance="node_ip5:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-4v28g", service="prometheus-stack-prometheus-node-exporter"} 0
{container="node-exporter", endpoint="http-metrics", instance="node_ip6:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-zpn4z", service="prometheus-stack-prometheus-node-exporter"} 0
如果把80改成20,那么结果会从0变成1,对比上面的输出,可以很明显的看到添加了bool之后的变化
默认模式(过滤模式):返回结果为 true 的时间序列,并保持其原始值。结果为false的输出no data.
布尔模式:使用 bool 修饰符时,所有序列都返回,但值变为 1 (true) 或 0 (false)
【2种模式的主要使用场景】
默认/过滤模式:
- 告警规则:只关注超出阈值的具体指标值
- 仪表盘:只显示满足特定条件的实例数据
- 数据分析:筛选出需要进一步处理的时间序列
布尔模式:
- 条件计数:统计满足条件的实例数量
# 统计内存使用率超过 80% 的节点数量 promql:sum ((1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > bool 80) - 多条件逻辑:构建复杂的布尔逻辑
# 内存使用率超过80% 并且 CPU使用率超过90% promql:(node_memory_usage > bool 80) and (node_cpu_usage > bool 90) 这里的node_memory_usage和node_cpu_usages不是真的指标项,这里简化写法,缩小篇幅 - 百分比计算:计算满足条件的实例比例
# 计算内存使用率超过80%的节点比例 promql:avg ((1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > bool 80)
逻辑/集合运算符:and, or, unless (除非)
这些运算符仅在瞬时向量之间使用,用于集合操作。
- vector1 and vector2: 交集
- vector1 or vector2: 并集
- vector1 unless vector2: 补集 (在 vector1 中但不在 vector2 中)
例如:
kube_pod_info{namespace="kube-system"} or kube_pod_info{namespace="default"}
指标的5种数据类型
Prometheus把主动采集的以及客户端上报的所有样本数据(也就是每一个时间序列数据点)都存成一个float64数据
虽然不同指标的输出看起来都是一样的浮点数字,但是实际内部区分了多种类型
这里的类型作用是:
- 不同类型的指标,适用于不同的监控场景
- 例如histogram适合计算延迟,可以精确的看到各个分位的统计情况,例如p95,p99..
- counter只增不减可以计算每秒增量
- gauge可以精确观测当前动态以及趋势预测
因此,在指标暴露端(Exporter 或你的业务代码)必须给每个指标显式声明类型。
具体实现是:
在 Prometheus 文本格式里写
# TYPE <metric_name> <counter|gauge|histogram|summary>
1、协议要求
Prometheus 的 text exposition format 把 # TYPE 行当成“元数据声明”;
如果缺失,Prometheus 在 scrape 时会报
text format parsing error in line xx: invalid metric type ""
整条解析失败,指标被丢弃。
2、客户端库已帮你写死
官方 Go/Java/Python/Rust 等客户端里,你一旦用
prometheus.NewCounterVec(...)库在 /metrics 输出时会自动加
# TYPE http_requests_total counter
不需要你手写,但“显性”这一行一定存在。
3、只有一种情况可以省略
纯推送到 Pushgateway 的临时任务,且使用 protobuf 格式(已废弃)时,类型信息在消息体里二进制编码,不在文本里出现。
现代场景(OpenMetrics、text 0.0.4)仍然要求显式类型。
因此:只要走 Prometheus text 协议(99.99% 的 exporter 都走它),每条指标必须带 # TYPE 行,由客户端库或你自己显式写出;
否则 scrape 直接报错,数据进不到 TSDB。
counter-计数器-只增不减
counter类型的核心:
- 只增不减的“累积量”
- 重启时归零,其余时间只能不断累加
- 一般会带 _total、 _count、_sum等后缀
- 它的当前值表示的是总数,因此不会用它搭配max()、min()、avg()等函数
典型的用途:
- 请求量、下单数、异常条数、错误次数、完成任务数、等可累加的场景。
- 计算每秒增量,也即平均qps
主要搭配的函数:
- rate() / irate() 等函数,把累积量换成每秒增量,也即增长率qps。
- increase()函数,计算具体的增长数值
- sum()函数,计算一个range内的占比
典型 PromQL
# 最近 5 分钟平均 QPS
rate(http_requests_total[5m])
# 看 4xx 占比
sum(rate(http_requests_total{code=~"4.."}[5m]))
/ sum(rate(http_requests_total[5m]))
gauge-测量值-动态变化-双向变化
gauge类型的核心
- 随时间动态变化,可上下双向变化
- 主要体现当下的真实情况
- 当前值就表示的实时动态值,很有意义,因此不用rate()等函数
典型的用途
- 内存使用率、CPU使用率、活跃连接数、队列长度等场景
主要搭配的函数
- avg()、max()、min()、sum()等函数。
- delta()、deriv()等函数
- predict_linear()函数,可以做趋势预测
- 不适用rate()、irate()等函数,因为gauge类型的变化,没有速率的概念
典型 PromQL
# 磁盘剩余空间 <10% 报警
node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.1
# 预测 4 小时后磁盘打满
predict_linear(node_filesystem_free_bytes[2h], 4*3600) < 0
histogram-统计数直方图-分位统计-服务端计算
histogram类型的核心
- 可以理解为是一个复合类型,包含很多个包含多个le标签(也叫做不同的桶)的counter类型指标,并且还搭配了_sum和_count指标
- le标签的含义是分组,按照不同的观测目标,例如延迟、大小级别,进行分组,例如延迟0.1ms,0.5ms,1ms等等
- 因此会产生一堆 {le="yyy"} 的 _bucket 指标对象
- 后缀通常都是_bucket
- 可聚合,多个实例的桶相加后再算分位,这是 Summary 做不到的。
- 注意:桶太少会导致分位误差大,如果桶太多,那么会导致存储膨胀
- <basename>_bucket{le="<upper inclusive bound>"}:落入不同桶的计数器。
- <basename>_sum:所有观测值的总和,即会把每次的真实的数值进行累加
- <basename>_count:所有观测值的总数量。
典型的用途
- 分析请求延迟的分布情况,例如<0.5ms的占比,P99的占比
- 计算平均值:通过 _sum / _count 可以计算出观测事件的平均值。例如,请求的平均延迟、平均响应大小等。
- 监控总量趋势,虽然 _sum 本身是累积值,但通过 rate() 可以观察总量变化趋势:
主要搭配的函数
- histogram_quantile()函数
典型 PromQL
# 过去 5 min 的 P99 延迟
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# 计算平均请求延迟
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])
# 计算平均响应大小(如果有相应histogram)
rate(http_response_size_bytes_sum[5m]) / rate(http_response_size_bytes_count[5m])
# 观察总延迟时间的变化率
rate(http_request_duration_seconds_sum[5m])
这在容量规划和性能分析中很有用。
histogram类型的本质上,其实也是一组counter,例如也可以使用rate()等函数进行计算,但是prometheus知道这些时间序列指标之间的内在联系
例如
promql:rate(apiserver_request_duration_seconds_bucket{instance="10.128.129.151:6443", job="apiserver", namespace="default",subresource="/healthz"}[1m])
histogram类型会被实现为多个counter,桶的每个边界范围是一个counter时间序列,并且还有配套的_sum和_count
但是histogram类型,你可以使用专门的函数如 histogram_quantile() 来计算分位数(例如,99%的请求延迟是多少)。如果你把它当作普通的 Counter 来处理,就无法实现这个关键功能。
summary-分位统计-客户端使用
summary类型的核心
- 也是主要用于计算分位的数据类型,和histogram类似
- 和histogram的区别:
- 在客户端计算分位数,得出结果之后,然后上报结果给prometheus,prometheus只保存最终的结果
- summary指标的输出中,没有le这种标签,而是quantile标签,用来定义分位桶
- 这个包含quantile标签的指标,类型是guage。因为这里是直接显示例如p99的结果,因此是会动态变化的
- 而上面说的histogram中,各个指标的值是累计值,需要使用额外的函数进行计算,才能得出结果
- 也会有_sum和_count,类型是counter
- 也即:<basename>{quantile="<φ>"}(Gauge),<basename>_sum(Counter),<basename>_count(Counter)
- 而histogram是根据promql,在服务端计算(histogram_quantile()函数),然后再输出结果
- 因此,针对summary数据类型,不需要再使用histogram_quantile()函数
- 它的优点是:查询速度极快(因为不用再计算,直接展示结果)
- 它的缺点是:
- 分位等级在代码中写死了,不可以再做一些定制化的promql聚合查询
- 不可以跨实例聚合(例如P99 平均后不再是 P99)
metrics定义示例:
# HELP rpc_dur_seconds RPC latency summary
# TYPE rpc_dur_seconds summary
rpc_dur_seconds{quantile="0.5"} 0.012
rpc_dur_seconds{quantile="0.9"} 0.044
rpc_dur_seconds{quantile="0.99"} 0.097
rpc_dur_seconds_sum 2351.7
rpc_dur_seconds_count 92623
真实的一个输出
promql:apiserver_admission_step_admission_duration_seconds_summary
输出为:
....省略了很多,只摘3行示例
apiserver_admission_step_admission_duration_seconds_summary{endpoint="https", instance="node_ip1:6443", job="apiserver", namespace="default", operation="CREATE", quantile="0.99", rejected="false", service="kubernetes", type="validate"} 0.000082422
apiserver_admission_step_admission_duration_seconds_summary{endpoint="https", instance="node_ip1:6443", job="apiserver", namespace="default", operation="CREATE", quantile="0.5", rejected="true", service="kubernetes", type="admit"} NaN
apiserver_admission_step_admission_duration_seconds_summary{endpoint="https", instance="node_ip1:6443", job="apiserver", namespace="default", operation="CREATE", quantile="0.9", rejected="true", service="kubernetes", type="admit"} NaN
典型的用途
- 但实例就可以代表全貌,例如单主Redis
- 对查询性能敏感,能接受预先固定的这些分位桶
- 计算平均值:和histogram一样,通过 _sum / _count 可以计算出观测事件的平均值。例如,请求的平均延迟、平均响应大小等。
- 监控总量趋势,和histogram一样,虽然 _sum 本身是累积值,但通过 rate() 可以观察总量变化趋势:
主要搭配的函数
- 直接显示
- rate()函数,计算一些平均值以及趋势
- avg_over_time()函数
典型 PromQL
#直接查询分位数,直接获取当前95分位的请求延迟,适用于监控服务水平目标(SLO)。
http_request_duration_seconds{quantile="0.95"}
#计算平均请求延迟,计算过去5分钟内请求的平均延迟。这是分析平均性能的常用方法。
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])
#分析分位数趋势,计算 50分位(中位数)延迟在最近5分钟内的平均值,用于观察延迟的长期趋势。
avg_over_time(http_request_duration_seconds{quantile="0.5"}[5m])
untyped-兼容类型
untyped兼容类型的核心
- 行为类似Gauge(可增可减),但没有明确的类型语义。
- Prometheus不会对其应用特定类型的规则或函数。
典型的用途
- 主要用于处理未知或未明确定义的指标。
- 常见于迁移旧系统、自定义收集器,或通过JMX Exporter等工具转换第三方指标(如Kafka监控指标)而缺乏类型上下文时。
主要搭配的函数
- 由于缺乏明确语义,没有专有函数。
- 通常当作Gauge处理,使用其相关函数(如delta(), deriv(), predict_linear()),或直接使用算术和聚合运算符。
因此,使用时需小心谨慎。理解数据来源和含义至关重要,以避免误用,长期来看,建议明确定义指标类型(如Counter、Gauge等)。
注意事项
- 理解数据源:弄清楚这个指标为什么是Untyped。是因为配置缺失,还是指标本身的行为就不确定?
- 谨慎选择函数:基于你对指标含义的理解,选择合适的PromQL函数。如果你认为它应该是一个Gauge,就使用Gauge的函数;如果它表现得像只增不减的计数器,可以考虑使用rate()等函数,但要意识到这可能不准确。
- 明确定义是上策,从最佳实践的角度,尽量为所有指标显式指定类型。例如,在使用JMX Exporter时,你可以在配置文件中为MBean显式地指定类型(如counter或gauge),这能避免很多潜在问题。
汇总对比
|
类型
|
核心语义
|
值单调性
|
合法运算/函数
|
典型误用后果
|
|
Counter
|
“累积量”
|
只增不减(允许归零)
|
rate()
increase()
resets()
|
用delta()会得出负值
|
|
Gauge
|
“可增可减的瞬时值”
|
任意
|
avg()|max()|min()|sum()
delta()
deriv()
predict_linear()
|
拿rate()算“网速”得荒谬结果
|
|
Histogram
|
“分布”
|
每个桶都是 Counter
|
histogram_quantile()
histogram_avg()
|
直接avg()把桶当样本平均,完全失真
|
|
Summary
|
“客户端预计算分位”
|
仅 count/sum 单调
|
只能
rate()
看总量,
不能再算分位
|
误当 Histogram 用会缺桶
|
|
Untyped
|
“我不知道类型”
|
不假设
|
任何函数都能试,但可能无意义
|
用户自己承担语义错风险 |
常用函数
聚合运算-sum/max/topk/avg等
聚合运算符,主要用于跨维度(标签)对时间序列进行聚合计算,聚合运算符通常与by或without一起使用,以指定如何分组
核心要点
- 跨维度(标签)
- 指的是:可以对不同行,也就是不同Endpoints产生的数据进行聚合,例如计算多个node的内存总和
初始 promql:(node_memory_MemTotal_bytes)/1024^3 输出为: {container="node-exporter", endpoint="http-metrics", instance="node_ip1:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 15.633651733398438 {container="node-exporter", endpoint="http-metrics", instance="node_ip2:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-hn5tn", service="prometheus-stack-prometheus-node-exporter"} 15.633659362792969 {container="node-exporter", endpoint="http-metrics", instance="node_ip3:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-95md9", service="prometheus-stack-prometheus-node-exporter"} 15.633659362792969 {container="node-exporter", endpoint="http-metrics", instance="node_ip4:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-g5n4k", service="prometheus-stack-prometheus-node-exporter"} 15.633659362792969 {container="node-exporter", endpoint="http-metrics", instance="node_ip5:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-4v28g", service="prometheus-stack-prometheus-node-exporter"} 15.633659362792969 {container="node-exporter", endpoint="http-metrics", instance="node_ip6:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-zpn4z", service="prometheus-stack-prometheus-node-exporter"} 15.633659362792969 下面进行聚合 promql:sum(node_memory_MemTotal_bytes)/1024^3 输出为: {} 93.80194854736328
- 指的是:可以对不同行,也就是不同Endpoints产生的数据进行聚合,例如计算多个node的内存总和
- 通常和by或者without组合,根据标签进行分组
- 指的是:在按照目标函数,进行聚合的基础之上,再进行细化的区分,例如显示所有up的enpoint
-
初始 promql:sum(up) 输出为: {} 50 promql:sum by (job) (up) 输出为: {job="prometheus-stack-kube-prom-alertmanager"} 2 {job="node-exporter"} 6 {job="kubelet"} 18 {job="prometheus-stack-kube-prom-prometheus"} 2 {job="kube-scheduler"} 3 {job="kube-etcd"} 3 {job="kube-controller-manager"} 3 {job="kube-state-metrics"} 1 {job="coredns"} 2 {job="apiserver"} 3 {job="prometheus-stack-kube-prom-operator"} 1 {job="kube-proxy"} 6
by 和 without 的对比
- by指定要保留哪些标签进行分,用于当你明确知道要按照哪些标签分组时
- 例如:sum by (job) (up)
- 格式是:函数 by (标签) (指标名称)
- without指定要排除哪些标签进行分组,用于当你明确知道要忽略哪些标签时
- 例如:sum without (instance) (http_requests_total)
- 格式和上面一样:函数 without (标签) (指标名称)
常用的聚合函数
- sum: 求和
- min: 最小值
- max: 最大值
- avg: 平均值
- stddev: 标准差
- stdvar: 标准方差
- count: 计数
- count_values: 对值进行计数
- bottomk: 最小的 k 个元素
- topk: 最大的 k 个元素
- quantile: 分位数
补充:上面的例子中,可以看到kubelet会有18条数据(6台机器,6*3)
原因是因为:
- kubelet本身只对外暴露一个端口(默认 10250),但它在四条不同的子路径上分别提供不同粒度的监控数据。
- Prometheus为了“不遗漏任何指标”,把四条路径都当成独立的 scrape target,于是你在 /targets 页面会看到同一个 kubelet
|
路径
|
内容
|
典型用途
|
是否默认启用
|
|
/metrics
|
kubelet 自身组件的度量:go_
、kubelet_
、volume_*、pod 启动次数、device-plugin 注册状态等
|
监控 kubelet 进程本身
|
是
|
|
/metrics/cadvisor
|
容器级别的度量:container_cpu_
、container_memory_
、container_fs_* 等
|
监控 Pod/容器资源用量
|
是
|
|
/metrics/probes
|
三种探针的结果:prober_probe_total{probe_type=liveness|readiness|startup}
|
耗时、成功/失败次数
监控健康探针的执行情况
|
是
|
|
/metrics/resource
|
仅包含节点和 Pod 的“精简”资源指标(cpu、memory、ephemeral-storage),格式与 metrics-server 保持一致
|
供 HPA/VPA 或 metrics-server 做极简查询
|
需手动打开(--enable-metrics-server-handler)
|
topk()函数示例
初始:
promql:node_memory_MemAvailable_bytes/1024^3
输出为:
{container="node-exporter", endpoint="http-metrics", instance="node_ip1:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 9.570903778076172
{container="node-exporter", endpoint="http-metrics", instance="node_ip2:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-hn5tn", service="prometheus-stack-prometheus-node-exporter"} 10.149948120117188
{container="node-exporter", endpoint="http-metrics", instance="node_ip3:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-95md9", service="prometheus-stack-prometheus-node-exporter"} 9.549674987792969
{container="node-exporter", endpoint="http-metrics", instance="node_ip4:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-g5n4k", service="prometheus-stack-prometheus-node-exporter"} 9.367660522460938
{container="node-exporter", endpoint="http-metrics", instance="node_ip5:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-4v28g", service="prometheus-stack-prometheus-node-exporter"} 9.907245635986328
{container="node-exporter", endpoint="http-metrics", instance="node_ip6:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-zpn4z", service="prometheus-stack-prometheus-node-exporter"} 8.286842346191406
使用topk,输出最大的前3个数据点
promql:topk(3,node_memory_MemAvailable_bytes/1024^3)
输出为:
{container="node-exporter", endpoint="http-metrics", instance="node_ip1:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-hn5tn", service="prometheus-stack-prometheus-node-exporter"} 10.148605346679688
{container="node-exporter", endpoint="http-metrics", instance="node_ip2:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-4v28g", service="prometheus-stack-prometheus-node-exporter"} 9.94540023803711
{container="node-exporter", endpoint="http-metrics", instance="node_ip3:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-95md9", service="prometheus-stack-prometheus-node-exporter"} 9.556514739990234
在此基础之上,还可以基于标签,去添加分组策略
获取每个metrics_path中最高的2个序列数据
promql:topk(2,up)by(metrics_path)
输出为:
up{container="alertmanager", endpoint="http-web", instance="192.168.35.134:9093", job="prometheus-stack-kube-prom-alertmanager", namespace="monitoring", pod="alertmanager-prometheus-stack-kube-prom-alertmanager-0", service="prometheus-stack-kube-prom-alertmanager"} 1
up{container="node-exporter", endpoint="http-metrics", instance="10.128.129.150:9100", job="node-exporter", namespace="monitoring", pod="prometheus-stack-prometheus-node-exporter-tnr7h", service="prometheus-stack-prometheus-node-exporter"} 1
up{endpoint="https-metrics", instance="10.128.129.148:10250", job="kubelet", metrics_path="/metrics/probes", namespace="kube-system", node="urs-virt27-test92.hz.163.org", service="prometheus-stack-kube-prom-kubelet"} 1
up{endpoint="https-metrics", instance="10.128.129.146:10250", job="kubelet", metrics_path="/metrics/probes", namespace="kube-system", node="urs-virt27-test90.hz.163.org", service="prometheus-stack-kube-prom-kubelet"} 1
up{endpoint="https-metrics", instance="10.128.129.151:10250", job="kubelet", metrics_path="/metrics/cadvisor", namespace="kube-system", node="urs-virt27-test95.hz.163.org", service="prometheus-stack-kube-prom-kubelet"} 1
up{endpoint="https-metrics", instance="10.128.129.150:10250", job="kubelet", metrics_path="/metrics/cadvisor", namespace="kube-system", node="urs-virt27-test94.hz.163.org", service="prometheus-stack-kube-prom-kubelet"} 1
up{endpoint="https-metrics", instance="10.128.129.151:10250", job="kubelet", metrics_path="/metrics", namespace="kube-system", node="urs-virt27-test95.hz.163.org", service="prometheus-stack-kube-prom-kubelet"} 1
up{endpoint="https-metrics", instance="10.128.129.146:10250", job="kubelet", metrics_path="/metrics", namespace="kube-system", node="urs-virt27-test90.hz.163.org", service="prometheus-stack-kube-prom-kubelet"} 1
可以很明显的看到,kubelet的job行,每种metrics_path,只输出了数值最大的2行
速率-rate()和irate()
rate() 和 irate()
这里的i不是immediately,是instant,名词或形容词,表示瞬时/瞬间/即刻的/极短的一瞬间。而immediately是副词,表示的是动作发生的快
这是计算增量最常用的2个函数,用于计算计数器在时间范围内的平均增长速率。
- rate(counter[range]): 计算范围向量 v 在时间窗口内的每秒平均增长率。适合用于相对平稳的增长率。
- irate(counter[range]): 计算范围向量 v 中最后两个样本的每秒增长率。对变化更敏感,适合用于快速变化的计数器。
核心要点
- 函数的参数(即入参),指标对象必须是counter类型(单调递增,只会随着时间越来越大,而不是动态浮动的),然后在此基础之上,加上时间范围
- 如果是gauge类型(动态变化),类似load,内存等指标,计算速率时使用delte()和deriv()函数
- 函数的返回值,是瞬时向量,也就是一个经过计算,得出的一个确定的值 --- 每秒增量
- 内部自动处理 counter 重启(即数值回绕)——用 increase() 的逻辑。
- 如果区间内 少于 2 个有效样本,结果直接丢弃(无值)。
【2个函数的内部实现原理】
1、rate()函数
rate(metric_name[time_window]) =
(时间窗口内最后一个值 - 时间窗口内第一个值) / (时间窗口的秒数)
假设我们有一个counter类型的指标 http_requests_total
时间点 值
t0: 1000 (起始点)
t1: 1500 (5秒后)
t2: 1800 (10秒后)
t3: 2200 (15秒后)
t4: 2500 (20秒后)
需求:计算 rate(http_requests_total[10s]) 在 t4 时刻的值:
时间窗口:t2 到 t4 ([range]是从当前往前面推,因此是最近的10秒)
起始值:1800 (t2时刻)
结束值:2500 (t4时刻)
时间差:10秒
rate = (2500 - 1800) / 10 = 700 / 10 = 70 请求/秒
可以看到,rate()的特点是:
- 平滑稳定:考虑了整个时间窗口的所有变化
- 抗抖动:对瞬时波动不敏感
- 适合告警和仪表盘:提供相对稳定的视图
2、irate()函数
irate(metric_name[time_window]) =
(最后一个值 - 倒数第二个值) / (两个样本的时间差秒数)
还是和上面一样,假设我们有一个counter类型的指标 http_requests_total
使用和上面相同的数据
时间点 值
t0: 1000 (起始点)
t1: 1500 (5秒后) (t0的5秒后)
t2: 1800 (10秒后) (t1的5秒后)
t3: 2200 (15秒后) (t2的5秒后)
t4: 2500 (20秒后) (t3的5秒后)
需求:计算 irate(http_requests_total[10s]) 在 t4 时刻的值:
irate()只使用最后两个样本:t3 和 t4 因此虽然这里range定义了10s,但是并不会参考这个
倒数第二个值:2200 (t3时刻)
最后一个值:2500 (t4时刻)
采集时间差:5秒
irate = (2500 - 2200) / 5 = 300 / 5 = 60 请求/秒
可以看到,irate()的特点是:
- 灵敏快速:对变化反应迅速(因为使用的是最新的2个数据点)
- 捕捉峰值:能够反映瞬时流量高峰(原因同上)
- 可能波动大:对样本间隔敏感
对比总结:
|
特性
|
rate()
|
irate()
|
|
计算样本
|
使用时间窗口内的
第一个和最后一个
样本
|
使用时间窗口内的
最后两个
样本
|
|
灵敏度
|
低,平滑
|
高,灵敏
|
|
抗噪性
|
好
|
差
|
|
资源消耗
|
较高(需处理更多数据)
|
较低
|
|
推荐场景
|
告警、仪表盘、长期趋势
|
调试、短期峰值检测
|
|
时间窗口选择
|
通常为抓取间隔的4倍
|
通常为抓取间隔的2-3倍
|
时间窗口的选择至关重要:
- 如果设置的太短,那么会太敏感
- 如果设置的太长,那么会过于平滑,把中间的突增都掩盖掉,掩盖掉了真实变化
- 因此使用rate()时,一般是配置4倍采集间隔,而irate(),因为只用最新的2个数据,因此大于等于2个采集间隔即可
【case】
1、HTTP 请求率监控
# 使用 rate - 适合仪表盘显示
promql:rate(http_requests_total[5m])
# 使用 irate - 适合检测突发流量
irate(http_requests_total[2m])
2、错误率计算
# 计算5分钟内每秒错误率
promql:rate(http_requests_total{status=~"5.."}[5m])
# 计算每秒错误率占比
promql:rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])
3. 网络流量监控
# 计算网络接口的每秒出入流量 (bytes/sec)
promql:rate(node_network_receive_bytes_total[5m]) 入流量
promql:rate(node_network_transmit_bytes_total[5m]) 出流量
increase()-增量函数
作用:计算一个Counter类型指标在指定时间范围内的增长量(纯增量)。它会处理计数器的重置情况(如监控目标重启)
示例:
#计算容器在最近2分钟内的重启次数。
promql:increase(kube_pod_container_status_restarts_total[2m])
#计算过去1小时新增的异常数量,这里error_total是简化写法
promql:increase(error_total[1h])
注意:increase() 实际上是 rate() 函数的语法糖,increase(v [t]) 等价于 rate(v [t]) * t的秒数。在记录规则和告警中,更推荐使用 rate() 函数以保持一致性。
predict_linear()-线性预测函数
predict_linear() 函数核心
- 基于简单线性回归,来预测一个 Gauge 指标在未来的值。
- 它假设指标的变化在短期内是线性的,并基于这个假设进行外推。
- 输出为:一个具体的预测值。可以基于这个添加一些报警逻辑
数学原理:对指定时间窗口内的数据点 (x, y)(其中 x 是时间戳,y 是指标值)拟合出一条最佳的直线 y = a + bx,然后计算在未来某个时间点 t 的值 y_t。
predict_linear()函数的语法
promql:predict_linear(v range-vector, t scalar)
v range-vector:历史数据的时间范围向量(例如 [1h])。
t scalar:从现在开始,预测多少秒后的值,注意基本单位是秒
例如:
#预测在4小时后文件系统的剩余空间
predict_linear(node_filesystem_free_bytes{job="node"}[1h], 4 * 3600)
典型用法:基于当前消耗速度,预测资源耗尽的时间。这是它最经典和有用的场景。
下面是一些例子
#预测磁盘填满时间
#假设我们有一个 Gauge 指标 node_filesystem_free_bytes,表示磁盘剩余空间。
#基于过去 6小时 的磁盘剩余空间数据,使用线性回归预测 1小时(3600秒)后 的磁盘剩余空间。
promql:predict_linear(node_filesystem_free_bytes{device="/dev/sda1"}[6h], 3600)
结果:如果返回 5.12e+08,意味着预测1小时后磁盘还剩大约 512MB 空间。
和
#用于告警 - 预测磁盘将在4小时内耗尽
#预测4小时后的剩余空间是否小于0。如果是,则触发告警,提醒管理员在磁盘被写满之前进行扩容。
promql:predict_linear(node_filesystem_free_bytes{device="/dev/sda1"}[6h], 4 * 3600) < 0
predict_linear()函数的优缺点
- 优点:概念简单,易于理解和解释。对于短期、趋势明显的线性变化预测非常有效。
- 缺点:假设未来会严格按照过去的线性趋势发展,无法捕捉季节性、周期性变化或趋势的突变。在波动较大的场景下预测可能不准。
holt_winters()或-double_exponential_smoothing()-平滑预测函数
- Prometheus 从 2.x → 3.0 的时候把 holt_winters() 改名 + 关进实验特性大门了
- 所以你现在在 UI 里直接写 holt_winters(...) 会收到:Error: unknown function holt_winters
- 该函数,现在已经改名叫 double_exponential_smoothing
- 官方认为“Holt-Winters”通常指 三次指数平滑,而 Prometheus里当前实际上只做了双指数(Holt Linear),为了避免概念混淆,3.0 起把函数重命名为double_exponential_smoothing(v range-vector, sf scalar, tf scalar)
- 新函数默认上锁,必须手动开锁,也就是说即使用了新名字,不额外配置,打开配置也一样报错
1、修改启动参数,加上实验特性开关
--enable-feature=promql-experimental-functions
(如果用 systemd/docker,把这一行放进对应的 ExecStart / command 字段)
2、重启实例
systemctl restart prometheus 或 docker restart <container>
想打开实验函数开关,不需要直接改 Prometheus 二进制启动参数,
只要在 Helm values 里给 prometheus.prometheusSpec.enableFeatures 加一行即可,然后 helm upgrade 触发 Operator 重建 Prometheus Pod。
下面给出完整 3 步操作(零中断):
1、准备 values 片段
新建文件 enable-experimental.yaml(也可直接改原 values):
prometheus:
prometheusSpec:
# 打开实验函数开关,可数组形式放多个
enableFeatures:
- promql-experimental-functions
2、一键升级
# 假设 release 名就叫 kube-prometheus-stack,命名空间同名
helm upgrade -n kube-prometheus-stack \
kube-prometheus-stack \
prometheus-community/kube-prometheus-stack \
--reuse-values \
-f enable-experimental.yaml
- 生成新 Prometheus StatefulSet 配置;
- 滚动重启 Prometheus Pod(先起后停,无中断);
- 启动参数里带上 --enable-feature=promql-experimental-functions。
3、验证
kubectl -n kube-prometheus-stack exec -it sts/prometheus-kube-prometheus-stack-prometheus-0 \
-c prometheus -- \
sh -c 'ps -ef | grep enable-feature'
应看到
--enable-feature=promql-experimental-functions
再进 UI 执行
double_exponential_smoothing(up[1h],0.1,0.3)
不再报错即生效。
常见的坑
- 字段层级别写错,一定是 prometheus.prometheusSpec.enableFeatures(数组)。
- 若你用的是 kube-prometheus-stack < 30.x,字段名可能叫 prometheusSpec.enableFeatures 或 enableFeatures,用helm show values prometheus-community/kube-prometheus-stack确认一下即可。
- 升级后 Pod 名字会变(Revision 增加),但 PVC 复用,历史数据无损。
# 1h 区间,平滑系数 0.1(历史权重高),趋势系数 0.2
double_exponential_smoothing(
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) [1h:], 0.1, 0.2)
因为该函数目前不是稳定状态,线上一般不会使用,这里就不展开了
delta()函数
- 计算Gauge类型指标在指定时间范围内第一个和最后一个样本值的差值。
- 它不具备计数器重置处理能力。
- 总结就是:只看前后的变化值,不关心中间状态
示例:
#计算CPU在2小时内的温度变化。
promql:delta(cpu_temp_celsius{host="zeus"}[2h])
注意:应尽量避免使用delta()函数,因为它可能会受到单个异常值的过度影响。如果需要了解变化趋势,更推荐使用 deriv() 函数。
deriv()函数
deriv()函数:
- 使用最小二乘法回归估算Gauge指标在指定时间范围内的瞬时变化速率(每秒)。
- 这比简单的差值计算更能抵抗异常值的干扰。
示例:
# 根据过去一小时的样本计算每秒驻留内存的变化速度。
promql:deriv(process_resident_memory_bytes[1h])
deriv()函数和rate()函数的区别
它们的设计目标、适用场景和计算方式有本质区别,核心区别一览
|
特性
|
rate()
|
deriv()
|
|
适用指标类型
|
Counter(计数器)
|
Gauge(仪表盘)
|
|
设计目的
|
计算持续增长的指标的平均每秒增长率
|
计算可上下波动的指标的瞬时变化趋势
核心是看整体的趋势
|
|
数学原理
|
(时间窗口内的总增长量) / (时间窗口秒数)
|
线性回归,计算整段时间的趋势线的斜率
|
|
返回值意义
|
始终为正的平均速率
|
可正可负的瞬时变化率(正表示上升,负表示下降)
|
|
对重置的处理
|
自动处理计数器重置(如服务重启)
|
不适用(Gauge 不存在重置)
|
|
典型用例
|
请求速率、错误发生率
|
内存使用量的变化趋势、队列长度的增长速度
|
【详细解析】
1. rate() 函数
设计目标:专门为 Counter(只增不减) 类型设计。
回答的问题是:“这个计数器在指定时间范围内,平均每秒增加多少?”
计算方式:它计算的是时间窗口内的总增长量除以总时间。它会智能地处理计数器重置(比如进程重启后计数器从0开始)。
关键特性:
- 结果永远是非负数。
- 提供的是一个平均、平滑的速率,非常适合用于告警和图形化展示。
示例:
#过去5分钟内,平均每秒的HTTP请求数。例如,结果是 2.5,表示平均每秒处理 2.5 个请求。
promql:rate(http_requests_total[5m])
2. deriv() 函数
设计目标:专门为 Gauge(可增可减) 类型设计。
回答的问题是:“这个仪表盘指标在指定时间范围内,其变化趋势是怎样的?它正在以多快的速度上升或下降?”
计算方式:使用线性回归算法,对时间窗口内的所有数据点拟合出一条最合适的趋势线,然后计算这条线的斜率。
关键特性:
- 结果可正可负。正数表示指标在上升,负数表示指标在下降。
- 对异常值不敏感,更能反映整体趋势,而不是短暂波动。
示例:
#根据过去一小时的可用内存数据,计算内存的瞬时变化速率。
#例如,结果是 -1024,表示内存正在以大约每秒 1024 bytes 的速度被消耗。
#结果是 512,则表示可用内存正在以每秒 512 bytes 的速度增加(可能由于缓存释放)。
promql:deriv(node_memory_MemAvailable_bytes[1h])
【实际的对比】
假设我们有以下数据点:
时间点: t=0 t=10 t=20 t=30 t=40
Counter: 100 150 200 250 300
Gauge: 50 60 55 70 65
对 Counter 使用 rate(...[40s]):
总增长量 = 300 - 100 = 200
总时间 = 40 秒
速率 = 200 / 40 = 5/秒
含义:这个计数器平均每秒增加5。
对 Gauge 使用 deriv(...[40s]):
算法会对 (0,50), (10,60), (20,55), (30,70), (40,65) 这些点进行线性回归。
最终得到一条趋势线,其斜率可能是 0.3/秒。
含义:这个仪表盘指标整体呈现缓慢上升的趋势,大约每秒上升 0.3 个单位。
histogram_quantile()-直方图分位数函数
histogram_quantile() 函数的工作原理
- histogram_quantile() 函数用于从直方图指标(histogram数据类型指标)中计算分位数。
- 其语法为 histogram_quantile(φ, instant vector),其中 φ 是分位数目标值(0 ≤ φ ≤ 1),例如 0.95 表示 P95。
计算过程主要包括以下步骤:
1、确定目标位置:目标数量 = φ × 总计数(总计数通常取自 le="+Inf" 的桶)。
2、定位桶:找到包含目标数量的最小桶(即其计数首次 ≥ 目标数量)。
3、线性插值:假设数据在桶内均匀分布,用线性插值估算分位数值。
值得注意的是,计算的分位数是一个预估值,其准确度取决于 bucket 区间划分的粒度,粒度越大,准确度越低。
一个完整的 PromQL 查询通常如下所示:
promql:histogram_quantile(0.95,sum(rate(kubelet_http_requests_duration_seconds_bucket[5m])) by (le))
在这个例子中,histogram_quantile() 是查询的根,最终结果表示一个近似分位数,0.95 是分位数的目标值,第二个参数是聚合后的直方图。
务必注意:你需要先使用 rate() 处理直方图桶的计数(因为它们是计数器counter数据类型),然后再计算分位数。
现在,我们来看几个 Kubernetes 环境中的具体监控例子。
1、监控 Kubernetes API Server 请求延迟
promql:histogram_quantile(0.99,sum(rate(apiserver_request_duration_seconds_bucket{job="apiserver"}[5m])) by (le, verb))
输出是:
{verb="WATCH"} 60
{verb="DELETE"} 0.0049499999999999995
{verb="POST"} 0.019866666666666716
{verb="PATCH"} 0.024770520231213875
{verb="APPLY"} NaN
{verb="LIST"} 0.00495
{verb="PUT"} 0.024894736842105292
{verb="CONNECT"} NaN
{verb="GET"} 0.02014693877551021
指标说明:计算 API Server 每种请求动词(verb)的 P99 延迟。
关键标签:verb(如 GET、POST、LIST)。
场景:识别处理缓慢的请求类型。
2、监控应用 Pod 的请求延迟
这里假设你的应用暴露了 HTTP 请求延迟的直方图指标。
promql:histogram_quantile(0.95,sum(rate(http_request_duration_seconds_bucket{namespace="my-app", pod=~"web-.*"}[5m])) by (le, pod, path))
指标说明:计算 my-app 命名空间下所有 web- 开头的 Pod,按 Pod 和请求路径统计的 P95 延迟。
关键标签:pod(Pod 名称),path(请求路径)。
场景:定位特定服务或接口的性能瓶颈。
3、监控 Ingress 控制器延迟
promql:histogram_quantile(0.90,sum(rate(nginx_ingress_controller_request_duration_seconds_bucket[5m])) by (le, ingress))
指标说明:计算每个 Ingress 规则的 P90 请求延迟。
关键标签:ingress(Ingress 资源名称)。
场景:评估不同入口流量的延迟表现。
histogram_quantile()函数的核心要点与常见误区:
- 务必先计算速率:直方图的桶是计数器,必须先用 rate() 等函数将其转换为每秒速率,再计算分位数。直接对原始桶计数器使用 histogram_quantile() 会导致错误结果。
- 必要的聚合:在计算分位数前,通常需要先使用 sum() 等聚合操作将相同 le 标签的桶计数相加,否则数据是分化的,无法正确计算全局分位数。
- 理解估算本质:histogram_quantile() 计算的是估算值,其准确性依赖于桶的划分粒度。桶越多越细,结果越精确。
- 指标类型匹配:确保你查询的指标确实是 Histogram 类型,并且包含 _bucket、_sum、_count 这些后缀。
sum_over_time()和avg_over_time()函数
这两个函数都是对单个时间序列在时间维度上的数值进行聚合,与 sum()/avg() 的空间维度聚合形成鲜明对比。
|
特性
|
sum_over_time()
/
avg_over_time()
|
sum()
/
avg()
|
|
操作维度
|
时间维度 - 单个序列在不同时间点【单对象聚合】
|
空间维度 - 多个序列在同一时间点【多个对象聚合】
|
|
输入类型
|
范围向量
|
瞬时向量
|
|
问题回答
|
"这个指标随时间变化的总和/平均值?"
|
"这些指标在当前时刻的总和/平均值?"
|
最最关键的区别就是:xx_over_time()的操作对象是一个指标对象,对这个对象的一段时间进行操作
而不像sum()或者svg()是对查询出来的所有序列,做聚合操作
sum_over_time()函数
定义:sum_over_time(range_vector) 计算范围向量内所有样本值的总和。
计算原理:对于单个时间序列在时间范围 [t₁, t₂, ..., tₙ] 内的值 [v₁, v₂, ..., vₙ]:
示例
假设我们有一个计数器 container_cpu_usage_seconds_total 在5分钟内的值:
时间点 值
t0 (现在-5m): 1000
t1 (现在-4m): 1100
t2 (现在-3m): 1250
t3 (现在-2m): 1400
t4 (现在-1m): 1600
t5 (现在): 1800
promql:sum_over_time(container_cpu_usage_seconds_total[5m])
计算:1000 + 1100 + 1250 + 1400 + 1600 + 1800 = 8150
结果:8150 CPU秒
实际应用场景
场景1:计算总资源消耗
# 计算Pod在过去一小时内消耗的总CPU时间(秒)
promql:sum_over_time(container_cpu_usage_seconds_total{pod="my-app"}[1h])
# 计算节点在过去一天的网络接收总量(字节)
promql:sum_over_time(node_network_receive_bytes_total{instance="node-1"}[1d])
场景2:计算时间段内的总事件数
# 计算过去24小时的总HTTP请求数
promql:sum_over_time(http_requests_total[1d])
# 计算过去一小时的总错误数
promql:sum_over_time(errors_total[1h])
场景3:计费相关 - 资源使用量统计
# 计算命名空间一天内的总CPU使用量(核心×小时)
promql:sum_over_time(sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace)[1d:])
注意这里是[1d:] 子查询,而不是[1d],表示的是按照xx时间间隔就去执行一次前面这个表达式,然后把结果汇总
如果不指定时间,那么默认会使用采集间隔,也就是会把所有的数据都统计进来
avg_over_time()函数
函数定义:avg_over_time(range_vector) 计算范围向量内所有样本值的算术平均值。
计算原理:对于单个时间序列在时间范围 [t₁, t₂, ..., tₙ] 内的值 [v₁, v₂, ..., vₙ]:
示例
使用相同的数据:
时间点 值
t0: 1000
t1: 1100
t2: 1250
t3: 1400
t4: 1600
t5: 1800
promql:avg_over_time(container_cpu_usage_seconds_total[5m])
计算:(1000 + 1100 + 1250 + 1400 + 1600 + 1800) / 6 = 8150 / 6 = 1358.33
结果:1358.33(平均CPU使用秒数)
实际应用场景
场景1:计算平均资源使用率
# 计算过去5分钟的平均内存使用率
promql:avg_over_time(node_memory_usage_percent[5m])
# 计算过去一小时的数据库平均连接数
promql:avg_over_time(database_connections[1h])
场景2:监控指标的平均表现
# 计算过去30分钟的平均队列长度
promql:avg_over_time(message_queue_length[30m])
# 计算过去一小时的缓存平均命中率
promql:avg_over_time(cache_hit_ratio[1h])
其他函数汇总
Prometheus的使用
在kube-prometheus-stack中,我们先看下pod的清单
# kubectl get pods -n monitoring
NAME READY STATUS RESTARTS AGE
alertmanager-prometheus-stack-kube-prom-alertmanager-0 2/2 Running 0 53d
prometheus-prometheus-stack-kube-prom-prometheus-0 2/2 Running 0 53d
prometheus-stack-grafana-5579d96575-p6bp7 3/3 Running 0 49d
prometheus-stack-kube-prom-operator-598bcf855f-szpsj 1/1 Running 0 53d
prometheus-stack-kube-state-metrics-678545f4f-gnzhk 1/1 Running 1 (49d ago) 53d
prometheus-stack-prometheus-node-exporter-4v28g 1/1 Running 0 53d
prometheus-stack-prometheus-node-exporter-95md9 1/1 Running 0 53d
prometheus-stack-prometheus-node-exporter-g5n4k 1/1 Running 0 53d
prometheus-stack-prometheus-node-exporter-hn5tn 1/1 Running 0 53d
prometheus-stack-prometheus-node-exporter-tnr7h 1/1 Running 0 53d
# kubectl get statefulsets -n monitoring
NAME READY AGE
alertmanager-prometheus-stack-kube-prom-alertmanager 1/1 53d
prometheus-prometheus-stack-kube-prom-prometheus 1/1 53d
# kubectl get deployment -n monitoring
NAME READY UP-TO-DATE AVAILABLE AGE
prometheus-stack-grafana 1/1 1 1 53d
prometheus-stack-kube-prom-operator 1/1 1 1 53d
prometheus-stack-kube-state-metrics 1/1 1 1 53d
# kubectl get daemonsets -n monitoring
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
prometheus-stack-prometheus-node-exporter 6 6 6 6 6 kubernetes.io/os=linux 53d
可以看到,kube-prometheus-stack中,主要是由3种控制器进行管理
核心的prometheus、alertmanager是statefulset类型。
prometheus配置文件及查看方法
prometheus的配置文件由--config.file参数指定,一般情况都是叫做prometheus.yaml
方法1:可以进入pod中进行查看
# kubectl exec -it prometheus-prometheus-stack-kube-prom-prometheus-0 -n monitoring -- /bin/sh
进来之后,查看进程的启动参数
/prometheus $ ps -efl | grep prometheus
1 1000 6d20 /bin/prometheus --config.file=/etc/prometheus/config_out/prometheus.env.yaml --web.enable-lifecycle --web.external-url=http://prometheus-stack-kube-prom-prometheus.monitoring:9090 --web.route-prefix=/ --storage.tsdb.retention.time=10d --storage.tsdb.path=/prometheus --storage.tsdb.wal-compression --web.config.file=/etc/prometheus/web_config/web-config.yaml
可以看到,服务使用的配置文件是:/etc/prometheus/config_out/prometheus.env.yaml
然后cat查看该文件内容即可
方法2:web ui上,Status->Server staus->Configuration
可以直接在web界面上查看,方便快捷
prometheus配置文件的内容结构说明
global部分
global设置了全局的默认参数,如数据抓取间隔、规则评估间隔等
例如:
global:
scrape_interval: 30s
scrape_timeout: 10s
scrape_protocols:
- OpenMetricsText1.0.0
- OpenMetricsText0.0.1
- PrometheusText1.0.0
- PrometheusText0.0.4
evaluation_interval: 30s
external_labels:
prometheus: monitoring/prometheus-stack-kube-prom-prometheus
prometheus_replica: prometheus-prometheus-stack-kube-prom-prometheus-0
参数说明:
- scrape_interval,抓取指标的默认时间
- scrape_timeout,抓取指标的默认超时时间
- scrape_protocols,指定 Prometheus 抓取指标时,优先接受的内容格式协议(Content Negotiation)
- 列表顺序决定优先级:从高到低(OpenMetricsText1.0.0 > OpenMetricsText0.0.1 > ...)
- Prometheus 会优先尝试用 OpenMetrics 格式抓取,若目标不支持则回退到 Prometheus 格式。
- 这确保了向后兼容性,同时利用新格式特性。
-
- OpenMetricsText1.0.0 # 最新的OpenMetrics标准格式 - OpenMetricsText0.0.1 # 早期OpenMetrics格式 上面这2个协议,更现代化 - PrometheusText1.0.0 # Prometheus 2.0+ 文本格式 - PrometheusText0.0.4 # 兼容旧版 Prometheus 格式 上面这2个协议,都是原生文本格式
- evaluation_interval,默认每隔多久时间执行一次记录规则(recording rules)和告警规则(alerting rules)
- external_labels,为所有时间序列添加的外部标签,用于区分数据来源,例如多集群环境,添加一些区分信息
scrape_configs部分
在这里定义数据抓取目标,是prometheus配置中最核心、最复杂的部分
有关自动发现、重标签等核心配置,在下面的监控数据采集部分有详细讲述
例如:
scrape_configs:
- job_name: serviceMonitor/monitoring/prometheus-stack-kube-prom-alertmanager/0
honor_timestamps: true
track_timestamps_staleness: false
scrape_interval: 30s
scrape_timeout: 10s
scrape_protocols:
- OpenMetricsText1.0.0
- OpenMetricsText0.0.1
- PrometheusText1.0.0
- PrometheusText0.0.4
always_scrape_classic_histograms: false
convert_classic_histograms_to_nhcb: false
metrics_path: /metrics
scheme: http
enable_compression: true
metric_name_validation_scheme: utf8
metric_name_escaping_scheme: allow-utf-8
follow_redirects: true
enable_http2: true
relabel_configs:
- source_labels: [job]
separator: ;
target_label: __tmp_prometheus_job_name
replacement: $1
action: replace
- source_labels:
...... 这里省略
metric_relabel_configs:
- source_labels: [__name__, le]
separator: ;
regex: (etcd_request|apiserver_request_slo|apiserver_request_sli|apiserver_request)_duration_seconds_bucket;(0\.15|0\.2|0\.3|0\.35|0\.4|0\.45|0\.6|0\.7|0\.8|0\.9|1\.25|1\.5|1\.75|2|3|3\.5|4|4\.5|6|7|8|9|15|20|40|45|50)(\.0)?
replacement: $1
action: drop
- source_labels: [xxxx]
.....这里省略
kubernetes_sd_configs:
- role: endpoints
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
namespaces:
own_namespace: false
names:
- monitoring
参数说明:
- job_name,定义抓取任务的名称。
- 这里的命名格式表明它来自k8s集群的ServiceMonitor这个CRD,具体是monitoring空间下的 prometheus-stack-kube-prom-alertmanager ServiceMonitor
- /0表示是第1个endpoint,命名规范约定,解决同名的问题,因为一个service可能还会有不同的端口,要使用新的任务
- 自动发现kubernetes_sd_configs以及重标签,relabel_configs以及metric_relabel_configs等详细见下面的部分
- honor_timestamps,是否使用目标自带的时间戳
- true表示使用目标endpoint自带的时间戳,也就是指标数据自带的时间
- false表示使用prometheus抓取到这个数据时的时间戳,这个一般没有业务意义,所以通常是设置为true
- track_timestamps_staleness,跟踪时间戳陈旧性,用于检测指标时间戳是否过时,目前是实验性功能,通常保持 false
- always_scrape_classic_histograms, 是否总是抓取经典直方图
- false: 优先使用原生直方图
- true: 同时抓取两种格式
- convert_classic_histograms_to_nhcb,是否将经典直方图转换为新的直方图格式,新格式更高效,但需要客户端支持
- enable_compression,是否启动http压缩,可以减少传输的数据量,但是需要目标endpoint也支持压缩
- metric_name_validation_scheme,指标名称验证方案
- utf8: 允许 UTF-8 字符
- legacy: 只允许 ASCII 字符
- metric_name_escaping_scheme,指标名称转义方案
- allow-utf-8: 允许 UTF-8 字符,不转义
- replace: 将非法字符替换为下划线
- follow_redirects,是否跟随 HTTP 重定向
- true: 自动跟随 3xx 重定向
- false: 不跟随,直接返回重定向响应
- enable_http2,是否启用 HTTP/2 协议
- 好处是多路复用、头部压缩,提高性能,通常保持 true
rule_files部分
注意:规则文件里不一定都是报警规则,还有Recording Rules(记录规则),而不是 Alerting Rules(告警规则)
|
类型
|
关键词
|
作用
|
示例
|
|
Recording Rule
|
record:
|
预先计算并生成新指标
|
record: code_verb:apiserver_request_total:increase30d
|
|
Alerting Rule
|
alert:
|
定义触发告警的条件
|
alert: KubeAPIErrorBudgetBurn
|
Recording Rules主要解决三大问题:
- 查询加速:数据预聚合操作,将复杂、耗时的 30 天计算(avg_over_time(...[30d]))提前算好,存储为新指标。后续查询或告警规则直接用这个新指标,从分钟级降到毫秒级
- 降低负载:避免每次 Grafana dashboard 刷新或告警评估时,重复计算海量历史数据,减轻 Prometheus 压力
- 简化逻辑:把复杂 PromQL 封装成一个易读的指标名,告警规则可以更简洁
例如:
# 不用 Recording Rule 的告警(复杂)
avg_over_time(code_verb:apiserver_request_total:increase1h[30d]) * 24 * 30 > 0.01
# 用 Recording Rule 后的告警(简洁)
code_verb:apiserver_request_total:increase30d > 0.01
场景举例:假设你要监控 "过去 30 天 API Server 写请求的错误率"。
- 原始指标:apiserver_request_total(每秒增量,标签多、基数大)
- 直接查询:每次都要 sum by (...) (increase(...[30d])),扫几十亿条数据,慢且卡死 Prometheus。
- 使用 Recording Rule:每 3 分钟算一次,结果存成新指标 code:apiserver_request_total:increase30d。
- 查询时直接读这个指标,瞬间返回
这两种规则可以混用,也就是说可以配置在一个rule文件中,不需要拆分
例如:
# cat xxx.yaml
groups:
- name: api-aggregation
interval: 3m
rules:
- record: code_verb:apiserver_request_total:increase30d
expr: ...
- alert: KubeAPIErrorBudgetBurn # ← 同一文件里的告警规则
expr: code:apiserver_request_total:increase30d{verb="write"} > 0.01
for: 5m
labels:
severity: critical
下面看一些报警规则的例子
- alert: PrometheusOperatorSyncFailed
annotations:
description: Controller {{ $labels.controller }} in {{ $labels.namespace }}
namespace fails to reconcile {{ $value }} objects.
runbook_url: https://runbooks.prometheus-operator.dev/runbooks/prometheus-operator/prometheusoperatorsyncfailed
summary: Last controller reconciliation failed
expr: min_over_time(prometheus_operator_syncs{status="failed",job="prometheus-stack-kube-prom-operator",namespace="monitoring"}[5m])
> 0
for: 10m
labels:
severity: warning
参数说明:
- expr:报警promql表达式
- for:满足条件之后,等待多久才真正处罚告警
- label:自定义抱歉,附加在告警上
- annotation,直译为注释,这里表示告警的详细描述信息,包含description、runbook_url、summary
alerting部分
alerting部分用于配置Prometheus与Alertmanager的连接
一段配置如下:
alerting:
alert_relabel_configs:
- separator: ;
regex: prometheus_replica
replacement: $1
action: labeldrop
alertmanagers:
- follow_redirects: true
enable_http2: true
scheme: http
path_prefix: /
timeout: 10s
api_version: v2
relabel_configs:
- source_labels: [__meta_kubernetes_service_name]
separator: ;
regex: prometheus-stack-kube-prom-alertmanager
replacement: $1
action: keep
- source_labels: [__meta_kubernetes_endpoint_port_name]
separator: ;
regex: http-web
replacement: $1
action: keep
kubernetes_sd_configs:
- role: endpoints
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
namespaces:
own_namespace: false
names:
- monitoring
在配置文件中,我们可以看到这3个配置很相像
- relabel_configs
- metric_relabel_configs
- alert_relabel_configs
alert_relabel_configs是在alerting配置模块中,其他2个都是在scrape_configs的每个job详细配置中
这个参数的作用是:
- 在告警发送到Alertmanager之前对告警的标签进行重新标记的配置。
- 这里配置了一个labeldrop操作,用于删除标签键匹配prometheus_replica的标签
- 注意这里只是删除这个标签,而不是删除这个指标
下面的alertmanagers部分,用于配置Alertmanager实例的信息
这里使用自动发现机制,然后结合relabel_configs去定位到实例信息
# kubectl get svc -n monitoring | grep prometheus-stack-kube-prom-alertmanager
prometheus-stack-kube-prom-alertmanager NodePort 10.103.216.169 <none> 9093:31441/TCP,8080:31147/TCP 56d
# kubectl get svc -n monitoring prometheus-stack-kube-prom-alertmanager -o jsonpath={.spec.ports}
[{"name":"http-web","nodePort":31441,"port":9093,"protocol":"TCP","targetPort":9093},{"appProtocol":"http","name":"reloader-web","nodePort":31147,"port":8080,"protocol":"TCP","targetPort":"reloader-web"}]
上面配置的是:regex: http-web,因此可以知道这个svc配置了2个端口,altermanager这里关联的是9093端口
alert_relabel_configs配置详解
alert_relabel_configs 是 Prometheus 中用于在告警发送到 Alertmanager 之前对告警标签进行处理的配置。
它允许您在告警路由前修改、添加或删除标签。
|
特性
|
说明
|
|
作用阶段
|
告警规则触发后,发送到 Alertmanager 之前
|
|
主要用途
|
告警标签的统一处理、路由优化、安全清理
|
|
配置位置
|
在 alerting 配置块中
|
【工作流程】
告警规则触发
↓
生成告警(包含所有标签)
↓
alert_relabel_configs 处理
↓ 修改、添加、删除标签
发送到 Alertmanager
↓
Alertmanager 路由处理
【action 选项全集】
以下是 alert_relabel_configs 中所有可用的 action 选项:
1、replace (默认),替换标签值
alert_relabel_configs:
- source_labels: [severity]
regex: critical
target_label: priority
replacement: P0
action: replace
2、keep,只保留匹配的告警,丢弃不匹配的
alert_relabel_configs:
- source_labels: [environment]
regex: production
action: keep
3、drop,丢弃匹配的告警,保留不匹配的
alert_relabel_configs:
- source_labels: [alertname]
regex: TestAlert
action: drop
4、hashmod,计算源标签值的哈希模数
alert_relabel_configs:
- source_labels: [alertname, instance]
modulus: 10
target_label: shard
action: hashmod
5、labelmap,基于正则表达式重命名标签
alert_relabel_configs:
- regex: team_(.+)
replacement: ${1}
action: labelmap
示例:team_infrastructure → infrastructure
6、labeldrop,删除匹配正则表达式的标签
alert_relabel_configs:
- regex: "temp_.*"
action: labeldrop
7、labelkeep,只保留匹配正则表达式的标签,删除不匹配的
alert_relabel_configs:
- regex: "(alertname|severity|instance|job)"
action: labelkeep
【应用场景case】
场景1:统一环境标签
alert_relabel_configs:
# 将所有环境标签统一为小写
- source_labels: [environment]
regex: "(?i)PROD|PRODUCTION"
target_label: environment
replacement: "production"
action: replace
- source_labels: [environment]
regex: "(?i)STAGE|STAGING"
target_label: environment
replacement: "staging"
action: replace
场景2:安全清理敏感信息
alert_relabel_configs:
# 删除可能包含敏感信息的标签
- regex: "(password|token|key|secret)"
action: labeldrop
# 删除高基数标签
- regex: "(trace_id|span_id|user_id)"
action: labeldrop
场景3:基于业务逻辑路由
alert_relabel_configs:
# 为不同团队设置路由标签
- source_labels: [service]
regex: "(api-gateway|user-service|order-service)"
target_label: team
replacement: "backend-team"
action: replace
- source_labels: [service]
regex: "(frontend|mobile-app)"
target_label: team
replacement: "frontend-team"
action: replace
场景4:分片处理
alert_relabel_configs:
# 基于告警名称分片到不同的 Alertmanager
- source_labels: [alertname]
modulus: 2
target_label: __alertmanager_shard
action: hashmod
其他部分
【runtime】
runtime 配置节用于调整 Go 语言运行时(Go Runtime)的参数
作用:
- 控制 Prometheus 进程底层的 Go 语言运行时的行为
- 主要是垃圾回收(Garbage Collection, GC)相关的调优。
使用场景:在高负载或特殊环境下,通过调整这些参数来优化 Prometheus 的性能和资源使用。
配置例如
runtime:
gogc: 75
配置说明:
- gogc: 75 是 runtime 配置中最常见的一个参数。
- 全称:Go Garbage Collection Target Percentage
- 含义:设置了触发Go垃圾回收的内存增长百分比。
- 默认值:100
工作方式:
- 假设当前堆内存大小为 H。
- 当新分配的内存使得堆大小达到 H * (1 + GOGC/100) 时,Go 运行时就会触发一次垃圾回收。
- 对于 gogc: 75,意味着当堆内存增长到当前大小的 1.75 倍 (1 + 75/100 = 1.75) 时,触发 GC。
调优影响:
- 调低 GOGC(如设置为 75):会更频繁地触发垃圾回收。这会导致 CPU 使用率略有增加(因为GC更频繁),但可以显著降低最大内存占用量(RSS)。这在内存资源紧张的环境中非常有用。
- 调高 GOGC(如设置为 200):会减少垃圾回收的频率。这可以降低 CPU 开销,但会让程序占用更多的内存。
另外,prometheus中还有一个和runtime相关的配置,叫做Prometheus 的“运行时重载”
运行时重载功能,它通过 --runtime-config.file 命令行参数指定一个外部文件,而不是在主配置文件的 runtime 节下配置。
核心含义: 运行时重载配置。它允许你在不重启 Prometheus 服务的情况下,动态地修改一些特定的配置。
作用: Prometheus 的主配置文件(prometheus.yml)通常需要在修改后重启才能生效。但 runtime 配置节下的内容可以被“热重载”,这意味着你可以通过 HTTP API 或发送 SIGHUP 信号来让 Prometheus 重新加载这些配置,从而避免服务中断。
可配置项: 通常用于配置一些需要频繁调整的、非核心的参数。
- scrape_interval: 全局的抓取间隔。
- query_timeout: 查询超时时间。
- external_labels: 外部标签。
配置示例:
# 在 prometheus.yml 中
global:
scrape_interval: 15s # 这是初始值
# runtime 配置通常在一个独立的文件中
# 通过 --runtime-config.file 命令行参数指定,如 --runtime-config.file=runtime.yml
然后在 runtime.yml 文件中:
scrape_interval: 30s # 可以在运行时修改为30秒
external_labels:
new_label: new_value # 可以在运行时添加新标签
管理方式: 通过 Prometheus 的 /-/reload HTTP API 端点来触发重载。
【remote_write / remote_read 远程读写】
remote_write:
- url: "http://remote-storage:8080/write" # 远程存储的写入地址
remote_timeout: 30s # 远程写入请求的超时时间
remote_read:
- url: "http://remote-storage:8080/read" # 远程存储的读取地址
remote_write
作用:
- 将 Prometheus 采集到的样本数据(metrics)实时地、近乎同步地发送到一个远程的存储端点。
- 这通常是一个支持 Prometheus 协议的长期存储系统。
使用场景:
- 长期存储: Prometheus 本地 TSDB 默认只保留 15天到1个月的数据。使用 remote_write 可以将数据发送到如 Thanos, Cortex, VictoriaMetrics, M3DB 或云厂商的托管服务中,实现年级别的数据保留。
- 数据备份与高可用: 在多个 Prometheus 实例构成的高可用架构中,它们可以将数据写入同一个远程存储,防止单点故障导致的数据丢失。
- 数据中继: 将数据转发到不支持 Prometheus 拉模型但支持接收数据的系统中,例如 InfluxDB, Kafka, AWS Timestream 等。
配置示例:
remote_write:
- url: "http://thanos-receive:10908/api/v1/receive" # 远程端点的URL
basic_auth: # 认证信息
username: my-user
password: my-password
queue_config: # 队列配置,影响性能和可靠性
capacity: 10000 # 队列容量
max_shards: 200 # 最大分片数(并发数)
min_shards: 1 # 最小分片数
write_relabel_configs: # 在发送前对样本进行重新标记(增删改标签)
- source_labels: [__name__]
regex: ‘expensive_metric.*‘
action: drop # 丢弃匹配规则的成本高的指标
remote_read
作用:
- 允许 Prometheus 从配置的远程存储端点查询历史数据。
- 当你在 Prometheus 的 UI 或通过 API 查询一个时间范围时,如果数据不在本地,Prometheus 会去远程存储中查找。
使用场景:
- 无缝查询体验: 用户无需切换界面,可以直接在 Prometheus 的 Grafana 或 PromQL 查询界面中查询到存储在远端的长期历史数据,就像查询本地数据一样。
- 重要注意点: remote_read 操作是昂贵的,因为它需要从远程存储拉取大量原始数据到本地进行计算。
- 对于聚合查询,更优的方案是直接在远程存储的查询引擎上执行(例如使用 Thanos Query 或 VictoriaMetrics 的 API)。
配置示例:
remote_read:
- url: "http://thanos-query:10908/api/v1/read" # 远程查询端点的URL
read_recent: false # 是否在查询时同时从远程和本地读取最近的数据。设为false可避免重复
basic_auth:
username: my-user
password: my-password
【storage】
核心含义: 配置 Prometheus 本地时间序列数据库(TSDB)的行为,即配置本地TSDB的磁盘和保留策略。
作用: 控制数据在 Prometheus 服务器本地磁盘上的存储方式、位置和保留策略。
关键子项:
- tsdb.path: 指定 TSDB 数据在磁盘上的存储路径。默认为 data/。
- tsdb.retention.time: 数据的保留时间。默认为 15d(15天)。你可以设置为 90d 等更长时间。
- tsdb.retention.size: 数据的最大存储大小。例如 100GB。当数据总量超过此大小时,最旧的数据块会被删除。
- tsdb.wal-compression: 是否压缩预写日志(WAL),可以在牺牲少量CPU的情况下节省磁盘空间。
- tsdb.outofordertimewindow:允许 TSDB 接受一定时间范围内的乱序数据样本
- 0(默认):禁用乱序样本支持,任何时间戳早于最新样本的数据都会被拒绝
- 正时间值(如 1h, 30m):允许接受在这个时间窗口内的乱序样本
- 启用此功能会增加内存和CPU开销,因为TSDB需要维护额外的数据结构来处理乱序数据。
配置示例:
storage:
tsdb:
path: /opt/prometheus/data #指定一个容量大的磁盘路径
outofordertimewindow: 0 #禁用乱序样本支持,任何时间戳早于最新样本的数据都会被拒绝
retention: 30d #数据保留30天
# retention.time 和 retention.size 也可以单独指定
# retention.time: 30d
# retention.size: 500GB
注意:
- remote_write / remote_read 和本地的 storage.tsdb 不是二选一的关系,它们是互补的协作关系。
- 你可以把本地存储看作一个高效缓存和工作区,而远程读写则是数据持久化和扩展能力的桥梁。
|
特性维度
|
本地存储 (storage.tsdb)
|
远程读写 (remote_write/
remote_read)
|
|
核心角色
|
缓存和工作区
|
数据桥梁
|
|
数据流向
|
写入和读取都发生在本地
|
remote_write: 写出到远端
remote_read: 读入从远端
|
|
主要目的
|
提供近期数据的快速查询
|
长期存储、数据备份、高可用、与生态集成
|
|
数据存储
|
存储在Prometheus服务器本地磁盘
|
存储在远程存储系统(如Thanos, Cortex, TDengine等)
|
|
协作关系
|
默认且必需
|
可选,但用于扩展能力
|
在实际运行中,Prometheus 会同时使用这两者:
1、数据写入:
- Prometheus 采集到的监控数据,会同时写入本地 TSDB 和配置的所有 remote_write 目标。
- 这确保了数据在本地有一份副本用于快速查询,同时也在远程有一份用于长期保留。
2、数据查询:当你执行一个查询时(例如在 Prometheus UI 或 Grafana 中):
- Prometheus 会优先从本地 TSDB 查询数据,因为这是最快的路径。
- 如果你查询一个时间范围,其中一部分数据在本地,另一部分(更早的历史数据)只在远程存储中,Prometheus 会通过 remote_read 接口去远程存储获取那部分数据,然后将本地和远程的数据合并后返回给你。这个过程对用户可以是透明的。
是一个简单的共存配置示意,你可以参考修改你的 prometheus.yml:
# 本地存储配置
storage:
tsdb:
# 数据在磁盘上的存储路径
path: data/
# 本地数据保留时间,例如15天
retention: 15d
# 远程写入配置:将数据备份到长期存储系统
remote_write:
- url: "http://your-remote-storage.com/api/v1/write"
# 通常这里还会有认证信息,如 basic_auth 等[citation:5][citation:10]
# 远程读取配置:允许从长期存储系统查询历史数据
remote_read:
- url: "http://your-remote-storage.com/api/v1/read"
# 这个选项设置为true,意味着即使数据可能在本地存在,也强制从远程读取最近的数据。
# 在某些特定配置下,为了优化性能,可能会禁用此功能(设置为false),以避免不必要的远程读取[citation:1][citation:4]。
read_recent: true
【otlp】
核心含义: OpenTelemetry Protocol 的缩写。这是一个配置 Prometheus 如何作为 OpenTelemetry Collector 的一个导出目标。
简单来说就是:prometheus作为接收端,接受来自OpenTelemetry的数据推送
背景: OpenTelemetry (OTel) 是一个云原生时代下,用于可观测性数据(日志、指标、链路)的统一标准。OTLP 是其传输协议。
作用:
- 这个配置项不是让 Prometheus 去拉取 OTel 的数据,而是开启一个接收端
- 允许 OpenTelemetry Collector 将处理好的指标数据 推(Push) 给 Prometheus。
工作流程:
1、应用通过 OTel SDK 发出指标。
2、OpenTelemetry Collector 收集这些指标。
3、Collector 配置一个 otlp 接收器和一个prometheus导出器。
4、Collector 的导出器通过 OTLP 协议,将指标推送到配置了 otlp 的 Prometheus 实例上。
配置示例:
otlp:
# 将特殊字符转换为下划线,为冲突的名称添加后缀以避免重复,这是默认策略,提供最好的兼容性
translation_strategy: UnderscoreEscapingWithSuffixes
# 配置监听端点
http:
endpoint: 0.0.0.0:4318 # OTLP/gRPC 端点
grpc:
endpoint: 0.0.0.0:4317 # OTLP/HTTP 端点
# 全局标签,会添加到所有从OTLP接收的指标上
external_labels:
source: otel
监控数据采集
在传统的prometheus服务配置中,采集配置是维护在prometheus.yml这个文件中
在这一份总配置文件中的scrape_config字段下,包含嵌套了很多采集任务配置,其中会包括
- 每个采集任务的名称,也即job_name
- 每个job去哪里抓取数据
- 每个job的采集间隔
采集配置中的k8s服务发现
kubernetes_sd_configs 是 Prometheus的Kubernetes服务发现机制,注意,它与Kubernetes自身的服务发现是不同层面的概念
在每个job的配置明细中,可以看到以下这种配置项
kubernetes_sd_configs:
- role: endpoints
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
namespaces:
own_namespace: false
names:
- monitoring
kubernetes_sd_configs:
- role: endpoints
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
namespaces:
own_namespace: false
names:
- kube-system
kubernetes_sd_configs:
- role: endpoints
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
namespaces:
own_namespace: false
names:
- default
kubernetes_sd_configs配置
- 功能:让Prometheus自动发现Kubernetes集群中的监控目
- 工作原理:通过Kubernetes API动态获取Pod、Service、Node等资源信息
- 优势:无需手动配置目标IP,适应Pod的动态变化和扩缩容
基本结构
基本结构
kubernetes_sd_configs:
- role: endpoints # 服务发现角色
# 其他配置参数...
关于这个Role,Prometheus支持多种k8s资源类型的服务发现:
|
角色/Role
|
发现目标
|
适用场景
|
|
node
|
集群中的所有Node节点
|
监控节点基础指标(配合node-exporter)
|
|
pod
|
所有Pod实例
|
监控应用自定义指标
|
|
service
|
所有Service
|
发现服务端点
|
|
endpoints
|
Service关联的端点
|
最常用,发现实际的服务后端
|
|
ingress
|
Ingress资源
|
监控HTTP流量
|
拿一段配置进行讲解
kubernetes_sd_configs:
- role: endpoints # 在kube-system空间下,发现所有的endpoints
kubeconfig_file: "" # 留空表示使用pod内的服务账户serviceaccount去访问k8s api
follow_redirects: true # 遵循HTTP重定向
enable_http2: true # 启用HTTP/2协议
namespaces:
own_namespace: false # 是否额外把自己当前的空间(这里是monitoring),也加入names扫描列表中,false表示不加入,只扫码names列表中的空间
names: # 指定自动发现的作用范围,在这个空间下查找
- kube-system # 这里只指定了1个空间,kube-system
注意:如果这里没有namespaces相关配置,默认策略是会扫描所有有权限的namespace
自动发现的工作流程:
1、API查询:Prometheus通过Kubernetes API查询指定命名空间(例如kube-system)的所有Endpoints
2、获取元数据:为每个Endpoint添加Kubernetes元数据标签
3、标签处理:通过relabel_configs添加额外的标签(默认的/metrics输出中缺乏pod_name/pod_ip/namespace等关键信息)
为什么需要标签处理?
为什么需要用relabel_configs、metric_relabel_configs这些配置进行负责的标签处理呢?
因为服务发现提供的是"目标信息",不是"指标数据"
当 Prometheus 通过 Kubernetes 服务发现时:
# 服务发现获取的是这样的信息
- target_ip: "10.244.1.23"
target_port: "8080"
metadata:
service_name: "frontend-service"
namespace: "production"
pod_name: "frontend-7d5b8c9fbc-abcde"
labels:
app.kubernetes.io/name: "frontend"
version: "1.2.3"
但是应用程序暴露的指标往往是这样的:
http_requests_total{method="GET", status="200"} 1500
http_requests_total{method="POST", status="201"} 300
问题:指标数据中没有服务名称、命名空间、Pod 名称等上下文信息!
relabel_configs重标签的作用:重标签配置在两者之间建立桥梁
例如
relabel_configs:
# 将服务发现获取的元数据转换为指标标签
- source_labels: [__meta_kubernetes_service_name]
target_label: service # ← 创建 service 标签
action: replace
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace # ← 创建 namespace 标签
action: replace
最终效果:
prometheus
# 经过重标签处理后,指标变成了:
http_requests_total{method="GET", status="200", service="frontend-service", namespace="production"} 1500
http_requests_total{method="POST", status="201", service="frontend-service", namespace="production"} 300
输出的信息中有关键的信息,方便后续我们进行过滤定位和制作报警
一个简单的工作流程
Kubernetes API Server
↓
服务发现 (从上面获取元数据)
↓ 生成 __meta_* 标签
relabel_configs 处理
↓ 转换为业务标签
指标抓取 (/metrics 端点)
↓ 获取原始指标数据
metric_relabel_configs 处理
↓ 最终清理和优化
存储到 TSDB
除了利用自动发现产生的内部元数据标签,也可以添加一些业务标签
例如:
# 您可以基于业务逻辑添加标签
# 如果namespace值是production,那么修改成prod;如果是其他的值,那么保持不变
- source_labels: [__meta_kubernetes_namespace]
regex: production
target_label: environment
replacement: prod
action: replace
- source_labels: [__meta_kubernetes_namespace]
regex: staging
target_label: environment
replacement: staging_new
action: replace
注意:规则是按顺序执行的,如果某个命名空间同时匹配两个规则(理论上不可能,但配置错误时可能发生),后面的规则会覆盖前面的规则。
数据处理-标签处理等
标签处理涉及2个配置项
- relabel_configs
- metric_relabel_configs
这2个配置项都用于重写标签,但它们在不同的阶段生效,处理的对象和目的也不同
区别如下:
|
特性维度
|
relabel_configs
|
metric_relabel_configs
|
|
作用的阶段
|
在抓取目标指标数据之前
|
在抓取到指标数据之后,存储到数据库之前
|
|
作用对象
|
抓取目标(Target)
|
已抓取的指标(Metric)样本
|
|
主要用途
|
服务发现中目标选择、修改目标标签、定义如何抓取
|
过滤或修改指标,管理存储的样本
|
无论哪种重写配置,它们都共享一套相同的核心配置项。理解这些配置项是编写重写规则的关键:
- action: 指定要执行的重写动作,默认为 replace。
- source_labels: 一个字符串列表,指定要操作的源标签名。
- separator: 用于连接 source_labels 多个值的分隔符,默认为 ;。
- target_label: 执行动作(如 replace)后,要覆盖或写入的目标标签名。
- regex: 用于匹配源标签连接后字符串的正则表达式,默认为 (.*)(匹配所有)。
- replacement: 替换字符串,用于生成目标标签的新值,默认为 $1(使用第一个正则捕获组)。
- modulus: 模数,用于 hashmod 动作,实现目标分片。
action 决定了重写规则的具体行为,以下是几种常见的动作:
- replace(默认):
- 根据regex匹配 source_labels 连接后的字符串,然后用replacement替换 target_label 的值。
- 示例:为所有目标添加一个固定标签。
-
action: replace replacement: production target_label: env
- keep/drop:
- 根据regex是否匹配 source_labels 的连接字符串,来保留或丢弃目标(relabel_configs)或指标(metric_relabel_configs)。
- 示例:只抓取具有特定注解的Kubernetes Pod。
-
action: keep source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] regex: true
- labelmap:
- 根据regex匹配所有现有的标签名,并将匹配的标签值复制到由replacement指定的新标签中。
- 这在服务发现后,将元标签(如 __meta_kubernetes_node_label_*)转换为普通标签时非常有用。
- labeldrop/labelkeep:删除或保留与regex
匹配的标签。常用于清理不需要的标签。
一些案例
1、修改抓取地址和端口(relabel_configs)
- job_name: 'node_exporter'
relabel_configs:
- source_labels: [__address__]
regex: '([^:]+)(?::\d+)?'
replacement: '${1}:9100' # 将端口替换为9100
target_label: __address__
2、丢弃特定指标(metric_relabel_configs)
- job_name: 'some_application'
metric_relabel_configs:
- source_labels: [__name__] # 根据指标名进行过滤
regex: 'expensive_metric_.+'
action: drop # 不存储该指标
3、过滤特定严重程度的警报(alert_relabel_configs,在报警模块中)
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
alert_relabel_configs:
- source_labels: [severity]
regex: 'info'
action: drop # 不发送严重程度为info的警报
【关于__xx标签】
在 Prometheus 的 relabel 规则里,以双下划线 __ 开头的标签名属于“临时/内部标签”。它们只在 抓取发生前 的 relabel 阶段存在;
这用来临时存放中间结果,方便后续步骤继续过滤或拼接;
它不会 随样本最终存进 TSDB,也不会被远程写出去(除非你再显式把它映射成普通标签)。
因此:
- source_labels: [job]
separator: ;
target_label: __tmp_prometheus_job_name
replacement: $1
action: replace
上面这段就是把当前 job 名先 暂存 到一个内部临时标签 __tmp_prometheus_job_name 里,后面别的 relabel 规则可以接着用它做判断、拼接或哈希,而 不会 让 __tmp_prometheus_job_name 最终出现在指标标签里。
自定义的临时标签,通常的规范都是会加上tmp标识,__tmp_xxx
注意:它们的生命周期止于本次 relabel 流程,样本入库前 Prometheus 会把所有 __ 开头的键全部剥掉。
【官方保留(Prometheus 自己生成)的内部标签】
名称已硬编码在代码里,用户 只能读取/修改,不能凭空新建。
常见只有下面这些,背住即可:
__address__ # 将要抓取的 "IP:port"
__scheme__ # http 或 https
__metrics_path__ # 抓取路径,如 /metrics
__param_<name> # URL 查询参数 ?name=xxx
__name__ # 指标名称(在 metric_relabel_configs 里才出现)
__tmp_prometheus_job_name # 新版 operator 会注入,但原理仍是临时
__meta_ 开头的也属于官方保留(由各种服务发现机制注入),但它们在 relabel 完成后就会被自动删除,不会进入抓取请求。
下面是服务发现时,注入的一些标签:
|
标签名称
|
主要来源
|
简要说明
|
|
__meta_kubernetes_service_name
|
Endpoints 或 Service 服务发现
|
来源于Kubernetes中Service对象的名称
|
|
__meta_kubernetes_service_label_
|
Endpoints 或 Service 服务发现
|
来源于Kubernetes中Service的标签
Prometheus会将原始标签中的点
.转换为下划线_
例如,Service的标签
app.kubernetes.io/name
会变成
__meta_kubernetes_service_label_app_kubernetes_io_name
。
|
|
__meta_kubernetes_namespace
|
Endpoints, Pod, Service, Ingress等服务发现
|
来源于Kubernetes中对应资源所在的命名空间
|
|
__meta_kubernetes_pod_name
|
Pod 或 Endpoints(当Endpoint后端是Pod时)服务发现
|
来源于Kubernetes中Pod的名称
|
|
__meta_kubernetes_endpoint_port_name
|
Endpoints 服务发现
|
来源于Kubernetes中Endpoint的端口名称
|
|
__address__
|
Prometheus 内置标签
|
抓取目标的原始地址 (host:port),在重写后通常作为最终抓取地址。
|
|
__scheme__
|
Prometheus 内置标签
|
抓取使用的协议 (http 或 https)。
|
|
__metrics_path__
|
Prometheus 内置标签
|
抓取目标的指标路径
|
|
__param_
|
Prometheus 内置标签
|
抓取URL中传递的参数
|
这些 __meta_ 开头的标签是Prometheus通过查询Kubernetes API Server获取的资源元数据。服务发现过程会为每个发现的Target自动附加这些标签。
关于标签格式的转换:
- 在Kubernetes中,Service的标签(Labels)可能包含点(.)或斜杠(/),例如 app.kubernetes.io/name。
- Prometheus在服务发现过程中,会将这些特殊字符转换为下划线(_)。
- 所以,在配置 relabel_configs 时,你需要使用转换后的标签名,例如__meta_kubernetes_service_label_app_kubernetes_io_name。
简单来说,您配置中 source_labels 里引用的标签:
- __meta_kubernetes_ 开头的:主要来自 Kubernetes服务发现过程,提供了Kubernetes各种资源(如Service、Pod、Endpoints)的元数据信息。
- __ 双下划线开头的其他标签(如 __address__):通常是 Prometheus内置的标签,用于配置抓取行为本身(如地址、路径、协议等)。
【关于正则捕获组】
例如
- source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
separator: ;
regex: Node;(.*)
target_label: node
replacement: ${1}
action: replace
结论:${1} 对应的是 正则里第一个捕获组,也就是 (.*) 匹配到的内容。
拆解:
- 源字符串模板:__meta_kubernetes_endpoint_address_target_kind;__meta_kubernetes_endpoint_address_target_name
- 实际拼出来是:Node;<节点名字> 【这里是上面2个字段的值,kind是Node,name是动态的,所以用正则匹配】
正则 Node;(.*)
- Node; 是字面量,必须原样出现;
- (.*) 是第一捕获组,把“节点名字”这部分抓进来。
因此 ${1} 就是 节点名字,而 不是 Node。
效果:把节点名字写进新生成的 node 标签,例如:node="worker-01"。
标签-relabel_configs
【案例说明】
案例1:
- source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]
separator: ;
regex: (prometheus-stack);true
replacement: $1
action: keep
这是一条 Prometheus relabel_config 规则,作用相当于“过滤器”:
作用:只保留带有标签 release=prometheus-stack 的 Kubernetes Service 所产生的目标targets(例如上面自动发现的endpoint),其余全部扔掉
逐段解释:
source_labels
- 把两条标签值拼在一起:
- __meta_kubernetes_service_label_release → 标签 release 的实际值
- __meta_kubernetes_service_labelpresent_release → 如果标签存在则为 true,不存在则为空
separator: ;
- 把上面两条值用分号连接,形成待匹配字符串。
- 常见两种情况:
- 标签存在且值是 prometheus-stack → 得到 "prometheus-stack;true"
- 标签不存在 → 得到 ";" 或 "something;"
regex: (prometheus-stack);true
- 正则要求:
- 第一段必须是 prometheus-stack(并捕获到 $1)
- 第二段必须是 true
- 只有完全匹配才能通过。
replacement: $1
匹配成功后保留该 target,这里的$1,表示第一个捕获组,也就是()里面的prometheus-stack
action: keep
匹配成功后保留该 target;不匹配就丢弃(action: keep 的含义是“保留匹配行”)。
一句话总结:这条规则让 Prometheus 只扫描那些打了 release=prometheus-stack 的 Service 对应的 Pod endpoint,其余的目标即使端口名对了也会被忽略。
案例2:
- source_labels: [job]
separator: ;
target_label: __tmp_prometheus_job_name
replacement: $1
action: replace
把当前 job 名先 暂存 到一个内部临时标签 __tmp_prometheus_job_name 里,后面别的 relabel 规则可以接着用它做判断、拼接或哈希,而 不会 让 __tmp_prometheus_job_name 最终出现在指标标签里。
可以看到
- target_label 只在“改写类”动作(replace, hashmod, labelmap, lowercase, uppercase…)中才必须使用;
- 上面keep / drop 的案例中,是直接对整行生效,用不着 target_label。
标签-metric_relabel_configs
大致流程如下
服务发现 → relabel_configs → 抓取指标 → metric_relabel_configs → 存储TSDB
↓ ↓
(操作目标) (操作指标数据)
relabel_configs是在抓取指标的/metrics的输出时进行操作,过滤指标、添加标签等等
metric_relabel_configs 的作用是在样本已经抓取回来、即将存入 TSDB 之前,再对“指标行”做一次过滤
经过这一步处理之后,才是生成最终的指标数据,并且存到TSDB,我们在prometheus中所看到的监控指标数据
relabel_configs-抓取前处理
主要使用场景:
- 基于服务发现元数据过滤目标
- 重写抓取地址和路径
- 添加静态标签到目标
- 修改 job 名称
metric_relabel_configs-抓取后处理
主要使用场景:
- 删除敏感或不需要的标签
- 基于指标值过滤指标
- 标签值格式标准化
- 减少存储的指标数量
案例1:
metric_relabel_configs:
- source_labels: [__name__, le]
separator: ;
regex: (csi_operations|storage_operation_duration)_seconds_bucket;(0.25|2.5|15|25|120|600)(\.0)?
replacement: $1
action: drop
配置说明:
- 要进行匹配的对象是__name__, le,这2个字段,分别是指标的名称,以及histogram指标类型的le桶
- 注意,__name__ 这个只会在这里出现,在前面的relabel_configs中是不出现的,它是前置操作,还没有获取到指标数据
- 使用默认的分隔符;
- 正则匹配的是:
- 指标名称是:csi_operations_seconds_bucket 或 storage_operation_duration_seconds_bucket
- le桶标签的值必须是 0.25、2.5、15、25、120、600 这几个(允许再带 .0)
- replacement: $1 因为 action: drop,这行其实不会用到 replacement,写 $1 只是占位,可忽略。
- action: drop 一旦上面正则匹配成功,整条样本就被扔掉;不匹配则保留。
可以看到,这段配置的作用是:把CSI相关直方图那几个太细或者太粗的桶(0.25s、2.5s、15s…)直接丢弃,减少存储量,只保留我们关心的桶
案例2:
metric_relabel_configs:
- source_labels: [__name__]
separator: ;
regex: container_cpu_(cfs_throttled_seconds_total|load_average_10s|system_seconds_total|user_seconds_total)
replacement: $1
action: drop
配置说明:
- 这里匹配的对象只是指标的名称,这一项
- 分隔符默认;
- 正则匹配的是:container_cpu_cfs_throttled_seconds_total、container_cpu_cfs_load_average_10s、container_cpu_system_seconds_total、container_cpu_user_seconds_total
- 搭配上规则drop
- 这个配置的意思是:在/metrics的输出中,这4个指标我都不需要,直接丢弃,减少存储量
案例3:
metric_relabel_configs:
- source_labels: [__name__, le]
separator: ;
regex: (etcd_request|apiserver_request_slo|apiserver_request_sli|apiserver_request)_duration_seconds_bucket;(0\.15|0\.2|0\.3|0\.35|0\.4|0\.45|0\.6|0\.7|0\.8|0\.9|1\.25|1\.5|1\.75|2|3|3\.5|4|4\.5|6|7|8|9|15|20|40|45|50)(\.0)?
replacement: $1
action: drop
配置说明:
- 要进行匹配的对象是__name__, le,这2个字段,分别是指标的名称,以及histogram指标类型的le桶
- 使用默认的分隔符;
- 正则匹配的是:
- 指标名称是:etcd_request_duration_seconds_bucket、apiserver_request_slo_duration_seconds_bucket、apiserver_request_sli_duration_seconds_bucket、apiserver_request_duration_seconds_bucket
- le桶标签的值必须是 (0\.15|0\.2|0\.3|0\.35|0\.4|0\.45|0\.6|0\.7|0\.8|0\.9|1\.25|1\.5|1\.75|2|3|3\.5|4|4\.5|6|7|8|9|15|20|40|45|50)这些(允许再带 .0)
- replacement: $1 因为 action: drop,这行其实不会用到 replacement,写 $1 只是占位,可忽略。
- action: drop 一旦上面正则匹配成功,整条样本就被扔掉;不匹配则保留。
可以看到,这段配置的作用是:在/metrics的输出中,指定的4个xx__duration_seconds_bucket相关直方图指标中,一些指定的桶直接丢弃,减少存储量,只保留我们关心的桶
完整工作流程-自动发现+采集+数据处理
先是服务发现,然后指标抓取
Kubernetes API Server
↓
服务发现 (获取元数据)
↓ 生成 __meta_* 标签
relabel_configs 处理
↓ 转换为业务标签
指标抓取 (/metrics 端点)
↓ 获取原始指标数据(这里输出的指标中,添加了relabel_configs步骤中额外补充的namespace/name/ip等等信息)
metric_relabel_configs 处理(过滤掉不需要关注的指标,减少后端存储压力)
↓ 最终清理和优化
存储到 TSDB
报警部分
Prometheus 的报警部分,主要由2部分组成:
- 定义报警规则(Alerting Rules)
- 定义了“在什么情况下需要触发报警”,它负责计算并发出警报,但不负责处理通知的后续流程。
- 在rule文件中:包含Recording预聚合,以及具体alert规则两个部分
- 定义altermanager。
- 上面的规则出发之后,将告警发送到哪里。
- 但是后续的告警发送(例如告警路由,分级分送,接收人,接收组,告警抑制,静默,告警去重等等)promtheus不负责,由altermanager服务负责
报警规则的配置
配置如下:
alerting:
alert_relabel_configs:
- separator: ;
regex: prometheus_replica
replacement: $1
action: labeldrop
alertmanagers:
- follow_redirects: true
enable_http2: true
scheme: http
path_prefix: /
timeout: 10s
api_version: v2
relabel_configs:
- source_labels: [__meta_kubernetes_service_name]
separator: ;
regex: prometheus-stack-kube-prom-alertmanager
replacement: $1
action: keep
- source_labels: [__meta_kubernetes_endpoint_port_name]
separator: ;
regex: http-web
replacement: $1
action: keep
kubernetes_sd_configs:
- role: endpoints
kubeconfig_file: ""
follow_redirects: true
enable_http2: true
namespaces:
own_namespace: false
names:
- monitoring
rule_files:
- /etc/prometheus/rules/prometheus-prometheus-stack-kube-prom-prometheus-rulefiles-0/*.yaml
关于以下这三部分,在上面的《prometheus配置文件的内容结构说明》章节已经有详细介绍,可跳转查看
- rule_files部分
- alerting部分
- alert_relabel_configs配置详解
prometheus的web-ui各模块功能介绍
概述
Prometheus 的 Web UI 是和Prometheus监控系统交互的主要窗口,深入理解它的各个模块,能让你更高效地进行监控查询、告警管理和系统维护。
下面我将为你详细解析 Query(查询)、Alerts(警报) 和 Status(状态) 这三大核心模块。
为了让你对这三个模块有个快速的了解,我先把它们的主要功能整理成了下面这个表格:
|
模块名称
|
核心功能
|
主要使用者
|
|
Query (Graph)
|
执行 PromQL 查询,以图表或表格形式可视化指标数据。
|
开发者、运维人员、SRE
|
|
Alerts
|
管理和查看 Prometheus 中定义的警报规则的当前状态(如是否触发)。
|
运维人员、SRE、On-call 工程师
|
|
Status
|
查看和维护 Prometheus 服务器的运行状态、配置、服务发现目标等。
|
Prometheus 管理员、运维人员
|
Query模块
Query 模块(通常以 Graph 标签呈现)是你使用 PromQL(Prometheus 查询语言)探索和可视化监控数据的主要工作台。
查询表达式输入:
- 这是最核心的输入框,用于编写 PromQL 查询语句。
- Prometheus 提供了便利功能,点击 "Insert Metric at Cursor" 可以浏览并插入所有可用的指标名称和 PromQL 函数,帮助你构建查询。
执行与交互:
- "Run Queries":执行你输入的查询。
- "Add Query":允许你在同一页面添加多个查询标签页,方便对比不同指标。
- "Enable/Disable query":可以暂时禁用某个查询而不删除它。
- "Hide all series":隐藏此查询在图表中的所有序列,便于在多查询时聚焦特定数据。
结果可视化:
- 交互式图表:查询结果默认会绘制在交互式图表中,X 轴代表时间,Y 轴代表指标值。你可以通过点击拖拽来放大特定时间范围,或使用时间范围选择器调整视图。
- 数据表格:图表下方会以表格形式展示查询结果的当前值,每个指标序列都有一个带颜色的方块,点击可以单独显示或隐藏图表中对应的线条。
- 隐藏图表:对于数据量巨大、可能导致浏览器渲染缓慢的查询,你可以点击 "Hide Graph" 隐藏图表,专注于分析表格数据。
主要用途与使用技巧
- 数据探索与故障排查:通过 PromQL,你可以计算指标的增长率(如 rate(http_requests_total[5m]))、进行聚合运算(如 sum by (job) (rate(http_requests_total[5m]))),从而快速定位性能瓶颈或异常服务。
- 临时验证与仪表板原型设计:在将查询配置到 Grafana 等专业仪表板之前,可以在此模块快速验证查询逻辑的正确性。
- 时间范围选择:利用页面右上角的时间范围选择器,灵活查看不同时间段的数据趋势,这对于分析历史问题尤为关键
Alerts模块
Alerts 模块为你提供了 Prometheus 服务器中所有已配置警报规则的当前状态总览。它显示的是根据当前规则和最新数据计算出的警报状态,而警报通知的实际发送由 Alertmanager 处理。
在这个界面中,你可以清晰地看到:
- 警报名称:对应你在警报规则文件中定义的 alert 字段。
- 状态(State):
- firing:警报已触发,意味着满足触发条件且持续时间已超过 for 子句设定的等待期。Prometheus 正在将警报发送给 Alertmanager。
- pending:警报已激活(触发条件满足),但仍在等待 for 子句所定义的持续时间。这是为了防止短暂波动导致的误报。
- inactive:警报未触发,当前不满足触发条件。
- 触发条件的表达式:即警报规则中的 expr 字段,你可以看到具体是哪个 PromQL 表达式决定了警报的触发。
- 标签(Labels):警报规则附带的标签,这些标签会随警报一同发送给 Alertmanager,用于路由、分组和抑制。
- 活跃时间(Active Since):警报进入 firing 状态的时间。
- 最后一次触发(Last Triggered):该警报规则最后一次被触发的时间。
- 错误信息:如果某条警报规则本身配置有误或执行失败,你可能会在此看到相关的错误信息。
主要用途
- 集中监控警报状态:一目了然地掌握整个系统中有哪些关键问题正在发生(firing),有哪些问题正在酝酿中(pending)。
- 调试警报规则:当预期的警报没有触发时,可以在此检查规则的状态和可能的错误。
- 了解警报系统负载:通过观察 firing 状态警报的数量,可以感知系统当前的稳定性和异常程度。
Status模块
Status 模块是 Prometheus 服务器的"控制面板"和"信息中心",它包含了服务器自身的运行状态、配置信息以及各种管理功能的入口。这个模块对于维护和排查 Prometheus 服务器本身的问题至关重要。
【Runtime & Build Information】
这里展示了服务器的运行时详情,包括 Prometheus 的版本号、Go 语言版本、构建时间戳等。
同时还显示了一些关键的性能指标,如内存使用情况、goroutine 数量、数据目录的存储空间等,帮助你监控服务器自身的健康度。
【Command-Line Flags】
列出了 Prometheus 服务进程启动时使用的所有命令行参数及其取值。这在回顾服务器启动配置或复现环境时非常有用。
【Configuration】
此页面清晰地展示了 Prometheus 当前加载的 prometheus.yml 主配置文件的内容。它是验证配置修改是否正确生效的最直接途径。
【Rules】
这里集中显示了 Prometheus 加载的所有记录规则(Recording Rules)和警报规则(Alerting Rules)。
与 Alerts 模块不同,这里展示的是规则的"代码"本身,而不是其当前状态。你可以在此确认规则的配置内容是否正确。
【Targets】
这是非常重要的一个页面,它显示了 Prometheus 配置要抓取的所有监控目标(Endpoint)及其当前状态。
你可以看到每个目标的 URL、健康状态(UP 或 DOWN)、最后一次抓取是否成功以及最后一次抓取的时间戳。这对于排查 Prometheus 为何无法获取某个服务的指标至关重要。
【Service Discovery】
展示了 Prometheus 通过服务发现机制(如在 Kubernetes 中)自动找到的所有潜在目标。你可以在这里预览服务发现的结果,而无需等待这些目标被正式纳入抓取配置。
Grafana的使用
图表的类型和配置
图表类型和配置
Grafana提供了多种内置面板类型,例如:
- Time series:用于基于时间的折线图、面积图等
- 默认和主要的图形可视化面板
- 可以满足绝大多数场景的需求
- Stat:显示单个较大的统计值
- 突出显示当前时刻的值,输出的是一个具体的数值
- 例如当前的可用率,节点数量等等
- Gauge:仪表图
- 动态浮动,适合展示总体水位,比如带宽是否满了,或者服务稳定性是否跌破阈值。
- 例如磁盘的占比,一个圆圈类型,颜色逐渐变化,绿色,黄色,红色
- Bar chart:条形图,也就是柱形图
- 可以设置是横向,也可以竖向的
- Heatmap:热力图。
- Table:表格
- 可以设置,在右上角输出详细的实例信息并排序
- Pie:饼图,适合表示各项数据的占比:
Grafana 官方提供的所有示例:https://play.grafana.org/dashboards。
完整 Demo:https://play.grafana.org/d/3SWXxreWk/grafana-dashboard?orgId=1
公开网站上直接下载模版json
官方仪表盘仓库:https://grafana.com/grafana/dashboards/
grafana中的explore
Grafana的Explore(探索)模块是一个专为实时数据查询和深度分析设计的强大工具,它能让你脱离固定仪表板的限制,自由地与数据进行交互。
简单来说就是:可以直接去调试数据源,例如去或者prometheus的metrics数据,并且可以把多个数据源结合起来一起排查,去排查分析。
核心价值与应用场景
- 传统仪表板适合监控固定指标,而Explore模式则像给你的数据配了一个“探索模式”,非常适合处理一些临时性和探索性的任务。
- 临时查询与快速迭代:无需预先配置面板,你可以快速构建查询、实时调整参数并立即查看结果,支持高效地试错和探索。
- 多数据源整合与关联分析:支持同时查询和关联来自Prometheus、Loki、Elasticsearch等多种数据源的数据,这在排查复杂问题时尤其有用。
- 问题诊断与根因分析:当监控告警触发后,可以直接跳转到Explore模式,结合日志、指标和追踪数据,像侦探一样层层深入,定位问题根源。
- 数据探索与可视化预览:在将查询结果正式添加到仪表板之前,可以在此进行充分的验证和可视化效果的调试。
界面与核心功能解析
Explore模式的界面设计清晰,核心功能区主要围绕查询构建和结果展示。
- 基本工作流程:其核心工作流程遵循 “查询-分析-可视化” 的闭环。
- 界面核心组件:熟悉界面组件能让你操作起来更得心应手。
- 工具栏:包含数据源选择器、时间范围选择器和查询运行按钮,是控制查询的基础。
- 查询编辑器:这是你与数据对话的主要窗口,它会根据你选择的数据源(如PromQL、LogQL)提供语法提示和自动补全,大大降低编写查询语句的难度。
- 结果可视化区:查询结果会以图表(如折线图)和表格形式实时显示在这里。
- 查询历史与收藏:Explore模式会自动保存你最近两周的查询历史,方便你回溯和复用。对于有价值的查询,你可以将其添加星标收藏,长期保存。
- 查询检查器:这是一个高级调试工具,可以查看查询执行的详细过程、耗时、返回的原始数据等,对于排查复杂查询问题非常有帮助。
- 强大的多数据源查询:你可以在一个Explore界面中配置多个查询,并为每个查询选择不同的数据源,实现真正的跨数据源关联分析。
模板变量-Variables
功能概述
- 变量 (Variables) 是 Grafana 的一项强大功能,可以用于创建动态的、可配置的、模板化的仪表盘。
- 比如创建一个通用大盘,监控多个服务,而无需为每个服务创建单独的看板。
变量设置之后,就可以在每个图表中,去使用,去编写表达式
例如:
sum(alertmanager_alerts{namespace=~"$namespace",service=~"$service"}) by (namespace,service,instance)
这里面的变量就是Variables中设置的,建议使用的方式为${var_name},在外层加一个{}
变量类型及配置
变量可以在“Dashboard Settings - Variables”配置。
Grafana 提供了多种变量类型:
- Query:使用数据源的查询语言,动态获取可选项。
- Custom:手动定义一组可选项,是一组用逗号分隔的值列表,这些值将作为下拉菜单的选项。
- Textbox:添加一个文本框,用户可以输入任意文本。
- Constant:常量,在仪表盘的变量区域不可见。
- Interval:定义一组时间间隔,可以用于更改仪表板上的时间范围或聚合级别。
- Data source:这种类型的变量在有多个数据源时特别有用,允许用户动态切换数据源。、
- Ad-hoc filters:动态添加、修改或删除过滤条件,仅支持某些数据源,如 ES、InfluxDB。
下面将依次介绍使用频率最高的变量类型:Custom、Textbox、Query。
Custom
- 提供几个固定的选项,逗号分隔。
- 默认单选,可以支持多选。
- 当允许多选时,可以有一个“全选”的选项。
- “全选”的默认值是所有值拼起来,如{value1, value2, ...}。 可以自定义一个值,如*。
- Custom 变量的选择框也是输入框,可以临时输入一个不存在于固定选项中的值,如下图。
Textbox
- 简化版的 Custom。就是一个输入框,可以输入任意值。
Query
Custom 类型的变量只能提供固定的值列表,而Query类型变量允许你编写特定的查询语句(例如PromQL),从数据源中获取数据并生成变量的可选值,例如实时查询某个 metrics name 下的某个 标签/tag的取值。
下面的表格汇总了Query类型变量在Prometheus数据源下的几种常见查询模式:
|
应用场景
|
PromQL 查询示例
|
说明
|
|
获取标签所有值
|
label_values(instance)
|
获取 instance 标签的所有值。
|
|
按指标过滤标签值
|
label_values(node_memory_MemTotal_bytes, instance)
|
获取具有
node_memory_MemTotal_bytes
指标的实例名
|
|
链式变量 (依赖)
|
label_values(up{job="$job"}, instance)
|
获取在 $job 变量选定的任务下,up 指标中出现的 instance 标签值
|
|
使用正则过滤
|
label_values(instance)
+ 在Regex栏填 `/.*(server-1|server-2).*/`
|
使用Regex过滤查询结果,只保留包含"server-1"或"server-2"的实例。
|
变量链与多变量依赖
变量的一个强大功能是可以创建变量链(Chained Variables),即一个变量的选项取决于另一个变量的当前值。
实现步骤如下:
- 首先创建一个基础变量,例如 job,用于选择任务。
- Query:label_values(job)
- 然后创建第二个变量,例如 instance,其查询依赖于 job 变量的值。
- Query:label_values(up{job="$job"}, instance)
这样,当用户在仪表盘上选择了特定的 job 后,instance 变量的下拉列表只会更新为属于该 job 的实例。
变量的使用
上面都是在定义和配置变量,那么怎么使用这些变量呢?
创建变量后,你就可以在仪表盘的各个地方使用它们了:
- 在PromQL查询中:
node_cpu_seconds_total{instance=~"$instance", job=~"$job"}
- 对于多值变量,Grafana会自动将选中的多个值转换为类似 value1|value2|value3 的正则表达式形式,因此通常在PromQL中使用 =~(正则匹配)操作符。
- 在面板标题中:
- 你可以在面板的标题中加入变量引用,使面板标题能动态反映当前显示的内容。
- 示例:CPU Usage for $instance
Alertmanager的使用
核心概念
Alertmanager的核心职责是处理Prometheus发送的告警,进行分组、抑制、静默等操作后,路由到正确的接收器
核心
- 数据源:prometheus发送过来的报警
- 处理报警:分组、抑制、静默等等
- 将处理后的报警,发送到指定的接收器(包括接收人和发送方式)
- 接收人:用户或用户组等
- 接收方式:钉钉、邮件、其他webhook,其他渠道等等
配置文件
配置文件的结构如下:
注意:
- global、route、inhibit_rules、receivers、templates 这些是平级的
- 用户和用户组在receivers中的定义没有什么特殊区别,无非是用户组关联了多个用户
global:
resolve_timeout: 5m
http_config:
follow_redirects: true
enable_http2: true
smtp_hello: localhost
smtp_require_tls: true
smtp_tls_config:
insecure_skip_verify: false
pagerduty_url: https://events.pagerduty.com/v2/enqueue
opsgenie_api_url: https://api.opsgenie.com/
wechat_api_url: https://qyapi.weixin.qq.com/cgi-bin/
victorops_api_url: https://alert.victorops.com/integrations/generic/20131114/alert/
telegram_api_url: https://api.telegram.org
webex_api_url: https://webexapis.com/v1/messages
rocketchat_api_url: https://open.rocket.chat/
route:
receiver: "null"
group_by:
- namespace
continue: false
routes:
- receiver: "null"
matchers:
- alertname="Watchdog"
continue: false
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
inhibit_rules:
- source_matchers:
- severity="critical"
target_matchers:
- severity=~"warning|info"
equal:
- namespace
- alertname
- source_matchers:
- severity="warning"
target_matchers:
- severity="info"
equal:
- namespace
- alertname
- source_matchers:
- alertname="InfoInhibitor"
target_matchers:
- severity="info"
equal:
- namespace
- target_matchers:
- alertname="InfoInhibitor"
receivers:
- name: "null"
templates:
- /etc/alertmanager/config/*.tmpl
在k8s环境中,通过kube-prometheus-stack部署时,Alertmanager的配置通常封装在Secret或ConfigMap中
# kubectl -n monitoring get configmap | grep alert
prometheus-stack-kube-prom-alertmanager-overview 1 65d
# kubectl -n monitoring get secret | grep alert
alertmanager-prometheus-stack-kube-prom-alertmanager Opaque 1 65d
alertmanager-prometheus-stack-kube-prom-alertmanager-cluster-tls-config Opaque 1 65d
alertmanager-prometheus-stack-kube-prom-alertmanager-generated Opaque 1 65d
alertmanager-prometheus-stack-kube-prom-alertmanager-tls-assets-0 Opaque 0 65d
alertmanager-prometheus-stack-kube-prom-alertmanager-web-config Opaque 1 65d
通过查看alertmanager pod的yaml,可以看到主要用到的是secret,核心是xxx-generated这个文件
# kubectl get pods -n monitoring alertmanager-prometheus-stack-kube-prom-alertmanager-0 -o yaml
......省略
containers:
- args:
- --config.file=/etc/alertmanager/config_out/alertmanager.env.yaml
- --storage.path=/alertmanager
- --data.retention=120h
- --cluster.listen-address=
- --web.listen-address=:9093
- --web.external-url=http://prometheus-stack-kube-prom-alertmanager.monitoring:9093
- --web.route-prefix=/
- --cluster.label=monitoring/prometheus-stack-kube-prom-alertmanager
- --cluster.peer=alertmanager-prometheus-stack-kube-prom-alertmanager-0.alertmanager-operated:9094
- --cluster.reconnect-timeout=5m
- --web.config.file=/etc/alertmanager/web_config/web-config.yaml
.....省略
volumeMounts:
- mountPath: /etc/alertmanager/config
name: config-volume
- mountPath: /etc/alertmanager/config_out
name: config-out
readOnly: true
- mountPath: /etc/alertmanager/certs
name: tls-assets
readOnly: true
- mountPath: /alertmanager
name: alertmanager-prometheus-stack-kube-prom-alertmanager-db
- mountPath: /etc/alertmanager/web_config/web-config.yaml
name: web-config
readOnly: true
subPath: web-config.yaml
- mountPath: /etc/alertmanager/cluster_tls_config/cluster-tls-config.yaml
name: cluster-tls-config
readOnly: true
subPath: cluster-tls-config.yaml
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-9n45f
readOnly: true
....省略
volumes:
- name: config-volume
secret:
defaultMode: 420
secretName: alertmanager-prometheus-stack-kube-prom-alertmanager-generated
- name: tls-assets
projected:
defaultMode: 420
sources:
- secret:
name: alertmanager-prometheus-stack-kube-prom-alertmanager-tls-assets-0
- emptyDir:
medium: Memory
name: config-out
- name: web-config
secret:
defaultMode: 420
secretName: alertmanager-prometheus-stack-kube-prom-alertmanager-web-config
- name: cluster-tls-config
secret:
defaultMode: 420
secretName: alertmanager-prometheus-stack-kube-prom-alertmanager-cluster-tls-config
- emptyDir: {}
name: alertmanager-prometheus-stack-kube-prom-alertmanager-db
- name: kube-api-access-9n45f
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
在这个pod里面,有2个容器
# kubectl get pods -n monitoring | grep alert
alertmanager-prometheus-stack-kube-prom-alertmanager-0 2/2 Running 0 66d
yaml中2个容器的内容
containers:
- args:
- --config.file=/etc/alertmanager/config_out/alertmanager.env.yaml
- --storage.path=/alertmanager
- --data.retention=120h
- --cluster.listen-address=
- --web.listen-address=:9093
- --web.external-url=http://prometheus-stack-kube-prom-alertmanager.monitoring:9093
- --web.route-prefix=/
- --cluster.label=monitoring/prometheus-stack-kube-prom-alertmanager
- --cluster.peer=alertmanager-prometheus-stack-kube-prom-alertmanager-0.alertmanager-operated:9094
- --cluster.reconnect-timeout=5m
- --web.config.file=/etc/alertmanager/web_config/web-config.yaml
env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
image: quay.m.daocloud.io/prometheus/alertmanager:v0.28.1
.......省略
- args:
- --listen-address=:8080
- --web-config-file=/etc/alertmanager/web_config/web-config.yaml
- --reload-url=http://127.0.0.1:9093/-/reload
- --config-file=/etc/alertmanager/config/alertmanager.yaml.gz
- --config-envsubst-file=/etc/alertmanager/config_out/alertmanager.env.yaml
- --watched-dir=/etc/alertmanager/config
command:
- /bin/prometheus-config-reloader
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: SHARD
value: "-1"
image: quay.m.daocloud.io/prometheus-operator/prometheus-config-reloader:v0.85.0
进入容器里面看下配置和路径
# kubectl exec -it alertmanager-prometheus-stack-kube-prom-alertmanager-0 -n monitoring -- /bin/sh
/alertmanager $ cd /etc/alertmanager/
/etc/alertmanager $
/etc/alertmanager $ ls
alertmanager.yml certs cluster_tls_config config config_out web_config
/etc/alertmanager $ zcat config/alertmanager.yaml.gz | md5sum
cec3423de58950432010edf8a75005e1 -
/etc/alertmanager $ cat config_out/alertmanager.env.yaml | md5sum
cec3423de58950432010edf8a75005e1 -
可以看到,服务内部有处理逻辑,把config下面的文件解压到了config_out下面,文件内容是一致的
用户和发送渠道的配置
用户和用户组
Alertmanager本身不直接管理用户和用户组认证。
在实际应用中,"用户"和"用户组"的概念通常通过路由规则和接收器来体现,如下:
global:
smtp_smarthost: 'smtp.公司.com:587'
smtp_from: 'alert@公司.com'
smtp_auth_username: 'alert@公司.com'
smtp_auth_password: '******'
route: # 根路由
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
receiver: 'default' # 默认兜底
routes:
# 所有 critical 告警发给“sre-组”
- match:
severity: critical
receiver: 'oncall-sre'
continue: true # 继续匹配后续规则
# 也可以再加别的子路由 …
receivers:
- name: 'default'
webhook_configs:
- url: 'http://ops-bot/alert'
# ===== 多人组:oncall-sre =====
- name: 'oncall-sre'
email_configs:
- to: 'zhangsan@公司.com'
send_resolved: true
headers:
Subject: '[{{ .GroupLabels.alertname }}] {{ .CommonAnnotations.summary }}'
- to: 'lisi@公司.com'
send_resolved: true
- to: 'wangwu@公司.com'
send_resolved: true # 可以看到,这里配置了多个接受对象,逻辑上形成组的概念
说明:
- 一个match表示一个报警的匹配规则
- 其中的匹配条件,表示报警中携带的的标签
例如:
route:
receiver: 'default-receiver' # 默认兜底接受者
group_by: ['alertname', 'cluster']
routes:
- match:
team: dba
receiver: 'dba-team'
group_wait: 10s
- match:
team: frontend
receiver: 'frontend-team'
group_wait: 10s
receivers:
- name: 'dba-team' # 这个接受配置中,会发送邮件及slack
email_configs:
- to: 'dba-oncall@example.com'
slack_configs:
- channel: '#dba-alerts'
- name: 'frontend-team'
email_configs:
- to: 'frontend-team@example.com'
slack_configs:
- channel: '#frontend-alerts'
说明:
- 接收到的报警中,包含标签team:dba的发送至一个接受通道;包含标签team:frontend,发送至另外一个通道
- 通道配置会包含:发送方式以及接收人
报警发送通道/渠道
Alertmanager 的配置模型是receiver 即接收者+发送方式”的复合体,没有单独的用户、用户组或发送通道配置项 。
报警的发送通道和用户、用户组的配置都是在同一个地方,都是在receivers中,可以说他们就是同一段配置
接收人和接收方式是绑定在一起的,你不能像在某些系统中那样先定义“用户组”或“通知渠道”,然后再复用它们。
案例如下:
receivers:
- name: 'email-receiver'
email_configs:
- to: 'admin@example.com'
from: 'alertmanager@example.com'
smarthost: 'smtp.example.com:587'
auth_username: 'user'
auth_password: 'password'
headers:
subject: '【{{ .Status | toUpper }】{{ .GroupLabels.alertname }}'
body: |
告警主题: {{ .GroupLabels.alertname }}
告警状态: {{ .Status }}
告警级别: {{ .CommonLabels.severity }}
触发时间: {{ .StartsAt.Format "2006-01-02 15:04:05" }}
故障实例: {{ .CommonLabels.instance }}
告警描述: {{ .CommonAnnotations.summary }}
{{ range .Alerts }}
==========详细信息==========
实例: {{ .Labels.instance }}
描述: {{ .Annotations.description }}
时间: {{ .StartsAt.Format "2006-01-02 15:04:05" }}
{{ end }}
- name: 'slack-receiver'
slack_configs:
- api_url: 'https://hooks.slack.com/services/your/webhook/url'
channel: '#alerts'
title: '{{ .GroupLabels.alertname }}'
text: >
{{ range .Alerts }}
*告警:* {{ .Annotations.title }}
*描述:* {{ .Annotations.description }}
*时间:* {{ .StartsAt.Format "2006-01-02 15:04:05" }}
*链接:* <{{ .GeneratorURL }}|查看详情>
{{ end }}
send_resolved: true
- name: 'webhook-receiver'
webhook_configs:
- url: 'http://webhook-server:8080/alerts'
send_resolved: true
max_alerts: 10
告警收敛+静默+路由等配置
分组控制
group的作用是:把具有相同标签值的告警归为一组,统一发送一条通知,避免告警风暴,也即告警分组。
案例1:
假设你有一条配置:
route:
group_by: ['alertname', 'cluster', 'service']
当以下告警同时触发时:
alertname cluster service
HighCPU prod api
HighCPU prod api
HighCPU test api
HighMemory prod api
Alertmanager 会按 ['alertname', 'cluster', 'service'] 这三个标签的值分组,最终形成 3 个告警组:
HighCPU + prod + api(2 条告警合并为 1 条通知)
HighCPU + test + api(1 条通知)
HighMemory + prod + api(1 条通知)
总结一句话:group_by 是 Alertmanager 控制告警聚合粒度的关键配置,标签值完全相同的告警会被合并为一组通知,从而避免你收到大量重复的告警消息。
案例2:
可以分组之后,再细分一些策略
route:
group_by: ['alertname', 'cluster', 'service'] # 多维分组矩阵
group_wait: 10s # 初始等待
group_interval: 30s # 组内新告警间隔
repeat_interval: 1h # 重复告警间隔
# 根据SLA设置差异化的等待时间[citation:7]
routes:
- match:
service: gold
group_wait: 5s
group_interval: 15s
repeat_interval: 30m
- match:
service: bronze
group_wait: 30s
group_interval: 2m
repeat_interval: 4h
告警收敛/抑制
告警的抑制,主要是配置在inhibit_rules中,说明如下
- source_match:表示要匹配的报警,也就是要抑制的对象,这个报警出现之后,要怎么去抑制接下来的报警
- 抑制策略/条件:
- target_match:根据标签匹配,匹配到的报警就不发送了
- target_match_re:根据正则表达式匹配,匹配到的报警就不发送了
- equal:抑制的前提是报警的这个标签是一致的,这里是cluster标签需要一致
inhibit_rules:
# 集群级故障抑制组件级告警
- source_match:
severity: critical
alertname: ClusterUnavailable
target_match:
severity: warning|critical
equal: ['cluster']
# 服务依赖抑制
- source_match:
severity: critical
alertname: DatabaseDown
target_match_re:
alertname: '.*ServiceUnavailable'
equal: ['cluster']
这个例子的效果是:
源告警:ClusterUnavailable(集群不可用)
目标告警:同一集群里 severity=warning 或 critical 的其他告警
效果:只要集群挂了,里面的 CPU、内存、磁盘、Pod 等告警统统不再发通知
以及
源告警:DatabaseDown
目标告警:同一集群内 alertname 以 ServiceUnavailable 结尾的告警
效果:数据库挂了,所有“服务不可用”告警被静音,避免“一库死,万告警”
【关于抑制的问题】
抑制后后续还发吗?持续多久?
- 抑制是实时动态的
- 只要源告警处于 Active(未 resolved) 状态,目标告警就被抑制;
- 源告警一旦 resolved,抑制立即解除,后续新触发的目标告警可正常发送。
- 没有固定时长(也就是一直抑制),不存在“抑制 30 分钟”这种设置,完全取决于源告警的生命周期。
- 抑制只影响通知,不影响告警本身
- 目标告警仍在 Alertmanager 里存在,可在 Web UI 看到,只是不往外发消息(邮件、Slack 等)。
总结:
- 告警抑制 = “源告警活着/触发,目标告警闭嘴;源告警好了,目标告警继续喊”。
- 它只静默通知,不删除告警,也不按时间长度计算。
告警静默
上面的报警抑制是如果源报警持续,那么是一直生效的,也无法进行时间及其他设置。
而告警静默则更为灵活,可以指定要静默的报警规则,以及设置要静默的时间
静默规则可以通过UI或API临时屏蔽特定告警:
通过Web UI创建静默:
- 访问http://<alertmanager>:9093/#/silences
- 点击"New Silence"
- 设置匹配器,如:alertname=NodeDown、severity=critical
- 设置静默持续时间
- 填写创建者和原因
通过amtool命令行:
# 静默特定告警2小时
amtool silence add alertname=NodeDown severity=critical -d 2h -c "维护窗口" -a "operator@example.com"
# 查看活跃静默
amtool silence query
# 删除静默
amtool silence expire $(amtool silence query -q alertname=NodeDown -o json | jq -r '.[].id')
智能静默实践:
- 维护窗口静默:与CMDB维护日历集成
- 噪声学习:基于历史数据自动生成静默建议
- 精准靶向:使用多维匹配规则
其他配置
分级别发送
分级别的前提是,报警中要带有级别的标签
例如:
route:
receiver: 'default-receiver'
routes:
- match:
severity: warning
receiver: 'warning-receiver'
group_interval: 30m
repeat_interval: 6h
- match:
severity: critical
receiver: 'critical-receiver'
group_interval: 10s
repeat_interval: 5m
routes: # 规则可以进行细分,添加更多的标签,更精确,例如这里是生产环境的critical级别
- match:
severity: critical
cluster: prod
receiver: 'pagerduty-receiver'
receivers:
- name: 'warning-receiver'
email_configs: [...] # 低频邮件通知
- name: 'critical-receiver'
slack_configs: [...] # 即时Slack通知
- name: 'pagerduty-receiver'
pagerduty_configs: [...] # 电话/短信通知
可以看到,所谓的这些分用户、分用户组、分渠道,分级别,配置路径和原理都是一样的,都是在route中,根据报警中的标签匹配规则,去关联相应的receivers,然后针对receivers去做定制化的处理
核心
- match规则:报警的标签匹配
- 发送策略:receivers中自定义
怎么修改alertmanager的配置?
依赖的CR自定义资源对象介绍
上面说了那么的配置案例,那么我们怎么去修改配置呢,入口在哪里呢,怎么生效呢?
整个的流程如下
Prometheus Operator Pod (真正的Operator)
↓ (监听和协调)
Alertmanager 自定义资源 (你查到的这个)
↓ (由Operator根据此资源创建)
StatefulSet (alertmanager-prometheus-stack-kube-prom-alertmanager)
↓ (管理)
Alertmanager Pods (实际运行的进程)
我看再展开看下,在monitoring下有哪些CR/custom resource
# kubectl api-resources |grep -i monitoring
alertmanagerconfigs amcfg monitoring.coreos.com/v1alpha1 true AlertmanagerConfig
alertmanagers am monitoring.coreos.com/v1 true Alertmanager
podmonitors pmon monitoring.coreos.com/v1 true PodMonitor
probes prb monitoring.coreos.com/v1 true Probe
prometheusagents promagent monitoring.coreos.com/v1alpha1 true PrometheusAgent
prometheuses prom monitoring.coreos.com/v1 true Prometheus
prometheusrules promrule monitoring.coreos.com/v1 true PrometheusRule
scrapeconfigs scfg monitoring.coreos.com/v1alpha1 true ScrapeConfig
servicemonitors smon monitoring.coreos.com/v1 true ServiceMonitor
thanosrulers ruler monitoring.coreos.com/v1 true ThanosRuler
和alertmanager相关的有2个
- alertmanagers:这个是控制alertmanager的pod实例的,里面还定义了实例的副本数,operator会监听这个CR,去创建statefulset类型的pod
- alertmanagerconfigs:根据名称也可以看的出来,是定义alertmanager配置的,也就是这里我们要关注的,我们通过定义这个类型的对象,去配置alertmanager的配置
AlertmanagerConfig资源是命名空间级别的,这意味着你
- 可以在不同的命名空间中创建不同的告警配置
- 可以实现多租户告警管理。
Prometheus Operator 通过以下方式决定哪些 AlertmanagerConfig 资源被使用:
- alertmanagerConfigSelector:在 Alertmanager 配置中,用于选择集群中所有可用的 AlertmanagerConfig 资源。
- alertmanagerConfigNamespaceSelector:用于选择哪些命名空间中的 AlertmanagerConfig 资源会被使用。
alertmanagerconfigs的配置案例
首先看这个CR的版本是什么
# kubectl get crd
NAME CREATED AT
adminnetworkpolicies.policy.networking.k8s.io 2025-08-29T07:05:43Z
alertmanagerconfigs.monitoring.coreos.com 2025-09-12T08:38:31Z
alertmanagers.monitoring.coreos.com 2025-09-12T08:38:31Z
apiservers.operator.tigera.io 2025-08-29T07:05:44Z
baselineadminnetworkpolicies.policy.networking.k8s.io 2025-08-29T07:05:43Z
bgpconfigurations.crd.projectcalico.org 2025-08-29T07:05:43Z
bgpfilters.crd.projectcalico.org 2025-08-29T07:05:43Z
bgppeers.crd.projectcalico.org 2025-08-29T07:05:43Z
blockaffinities.crd.projectcalico.org 2025-08-29T07:05:43Z
caliconodestatuses.crd.projectcalico.org 2025-08-29T07:05:43Z
clusterinformations.crd.projectcalico.org 2025-08-29T07:05:43Z
felixconfigurations.crd.projectcalico.org 2025-08-29T07:05:43Z
gatewayapis.operator.tigera.io 2025-08-29T07:05:43Z
globalnetworkpolicies.crd.projectcalico.org 2025-08-29T07:05:43Z
globalnetworksets.crd.projectcalico.org 2025-08-29T07:05:43Z
goldmanes.operator.tigera.io 2025-08-29T07:05:43Z
hostendpoints.crd.projectcalico.org 2025-08-29T07:05:43Z
imagesets.operator.tigera.io 2025-08-29T07:05:43Z
installations.operator.tigera.io 2025-08-29T07:05:43Z
ipamblocks.crd.projectcalico.org 2025-08-29T07:05:43Z
ipamconfigs.crd.projectcalico.org 2025-08-29T07:05:43Z
ipamhandles.crd.projectcalico.org 2025-08-29T07:05:43Z
ippools.crd.projectcalico.org 2025-08-29T07:05:43Z
ipreservations.crd.projectcalico.org 2025-08-29T07:05:43Z
kubecontrollersconfigurations.crd.projectcalico.org 2025-08-29T07:05:43Z
managementclusterconnections.operator.tigera.io 2025-08-29T07:05:44Z
networkpolicies.crd.projectcalico.org 2025-08-29T07:05:43Z
networksets.crd.projectcalico.org 2025-08-29T07:05:43Z
podmonitors.monitoring.coreos.com 2025-09-12T08:38:32Z
probes.monitoring.coreos.com 2025-09-12T08:38:32Z
prometheusagents.monitoring.coreos.com 2025-09-12T08:38:32Z
prometheuses.monitoring.coreos.com 2025-09-12T08:38:32Z
prometheusrules.monitoring.coreos.com 2025-09-12T08:38:33Z
scrapeconfigs.monitoring.coreos.com 2025-09-12T08:38:33Z
servicemonitors.monitoring.coreos.com 2025-09-12T08:38:33Z
stagedglobalnetworkpolicies.crd.projectcalico.org 2025-08-29T07:05:43Z
stagedkubernetesnetworkpolicies.crd.projectcalico.org 2025-08-29T07:05:43Z
stagednetworkpolicies.crd.projectcalico.org 2025-08-29T07:05:43Z
thanosrulers.monitoring.coreos.com 2025-09-12T08:38:34Z
tiers.crd.projectcalico.org 2025-08-29T07:05:43Z
tigerastatuses.operator.tigera.io 2025-08-29T07:05:44Z
whiskers.operator.tigera.io 2025-08-29T07:05:44Z
# kubectl get crd | grep alertmanagerconf
alertmanagerconfigs.monitoring.coreos.com 2025-09-12T08:38:31Z
# kubectl get crd alertmanagerconfigs.monitoring.coreos.com -o jsonpath='{.spec.versions[*].name}'
v1alpha1
可以看到版本是v1alpha1
然后在编辑yaml之前,有一个关键的信息需要去确认
通过上面的流程我们就知道,在整个体系中,是由Prometheus Operator 这个Pod去发现这些CR,然后去创建对应资源的
整个的流程如下
Prometheus Operator Pod (真正的Operator)
↓ (监听和协调)
Alertmanager 自定义资源 (你查到的这个)
↓ (由Operator根据此资源创建)
StatefulSet (alertmanager-prometheus-stack-kube-prom-alertmanager)
↓ (管理)
Alertmanager Pods (实际运行的进程)
在这里,AlertmanagerConfig资源是和Alertmanager强相关的,在Alertmanager中有标签选择器,只有匹配的AlertmanagerConfig,才会被operator创建
alertmanagerConfigSelector 是 Alertmanager 实例用来选择哪些 AlertmanagerConfig 资源应该被使用的标签选择器。它类似于 Kubernetes 中其他资源的 selector 机制。
# kubectl get alertmanagers -n monitoring prometheus-stack-kube-prom-alertmanager -o yaml | less
# kubectl get alertmanager -n monitoring -o jsonpath='{.items[*].spec.alertmanagerConfigSelector}'
{}
可以看到,这里我们没有做限制,只要你定义了AlertmanagerConfig,就可以匹配上
以下是一个详细的AlertmanagerConfig示例,它将接收器、路由和抑制规则整合在一起
注意,在yaml文件里面AlertmanagerConfig要是驼峰的书写格式,而且也没有s,在kubectl输出中有s是表示复数集合
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
name: create-team
namespace: monitoring
labels:
alertmanager: main # 此标签需要与 alertmanagerConfigSelector 匹配
spec:
route:
groupBy: ['namespace', 'alertname']
groupWait: 10s
groupInterval: 30s
repeatInterval: 4h
receiver: 'slack-prod-web' # 默认接收器
routes:
- receiver: 'slack-prod-web-critical'
matchers:
- name: severity
value: "critical"
continue: true # 允许告警继续匹配后续的同级路由[citation:1]
- receiver: 'email-prod-admins'
matchers:
- name: alertname
value: "HighErrorRate"
repeatInterval: 30m # 对于重要告警,提高重复频率
inhibitRules:
- sourceMatch:
- name: severity
value: "critical"
targetMatch:
- name: severity
value: "warning"
equal: ['namespace', 'instance'] # 当同一命名空间和实例的告警同时出现critical和warning时,抑制warning告警
receivers:
- name: 'slack-prod-web'
slackConfigs:
- apiURL:
key: url
name: slack-webhook-secret
channel: '#prod-web-alerts'
sendResolved: true
title: '{{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}*Description*: {{ .Annotations.description }}\n{{ end }}'
- name: 'slack-prod-web-critical'
slackConfigs:
- apiURL:
key: url
name: slack-webhook-secret
channel: '#prod-critical-alerts'
sendResolved: true
- name: 'email-prod-admins'
emailConfigs:
- to: 'prod-admins@company.com'
from: 'alertmanager@company.com'
smarthost: 'smtp.company.com:587'
authUsername: 'alertmanager'
authPassword:
key: password
name: smtp-secret
看是否可正常执行
# kubectl apply -f abc.yaml
alertmanagerconfig.monitoring.coreos.com/create-team created
# kubectl get alertmanagerconfig -n monitoring
NAME AGE
create-team 45s
如果有问题,可以看输出信息
# kubectl describe alertmanagerconfig create-team -n monitoring
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning InvalidConfiguration 2m30s alertmanager-controller AlertmanagerConfig create-team was rejected due to invalid configuration: unable to get secret "slack-webhook-secret": secrets "slack-webhook-secret" not found
修复一下
# kubectl create secret generic slack-webhook-secret \
--namespace=monitoring \
--from-literal=url='https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
secret/slack-webhook-secret created
# kubectl create secret generic smtp-secret \
--namespace=monitoring \
--from-literal=password='your-smtp-password'
secret/smtp-secret created
然后去http://ip:9093/#/status web页面的config部分,可以看到配置已经更新

[…] 基础知识看上一篇文章:基于Prometheus的K8S监控报警设计 […]