基于Prometheus的K8S监控报警设计

监控数据来源

这里是个通用的问题,不管使用什么监控系统,都要采集这些监控数据,只不过实现的方式不一样

主要有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
然后接下来进行alertmanager和grafana服务的配置
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进行访问,因此要进行修改

解决方式如下

目录:/etc/kubernetes/manifests
在这个目录下,修改etcd、kube-scheduler、kube-controller-manager等的配置
# 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
然后kube-proxy也可能会出现这个问题,kube-proxy是daemonset的类型,并不是上面那种静态pod启动方式,因此要修改它的configmap
# 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"
然后滚动升级这些pod
# 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
  • 通常和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)
  • 新函数默认上锁,必须手动开锁,也就是说即使用了新名字,不额外配置,打开配置也一样报错
如何解锁(必须重启 Prometheus)
1、修改启动参数,加上实验特性开关

--enable-feature=promql-experimental-functions
(如果用 systemd/docker,把这一行放进对应的 ExecStart / command 字段)

2、重启实例
systemctl restart prometheus 或 docker restart <container>

上面这2个步骤是传统单独部署prometheus的方式,但是我们是使用kube-prometheus-stack方式部署的,操作方式有所不一样
kube-prometheus-stack 把 Prometheus 当成 CRD(Prometheus object) 来管理,
想打开实验函数开关,不需要直接改 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
 Operator 会自动执行:
  • 生成新 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 复用,历史数据无损。
一句话总结:kube-prometheus-stack 下开实验函数,只需在 values 里给 prometheus.prometheusSpec.enableFeatures 塞 promql-experimental-functions,然后 helm upgrade;Operator 会滚动重启 Prometheus,无需手动改二进制启动参数。
例子
# 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()函数

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()函数的区别

这2个都是用于计算速率的函数,它们之间的区别是什么呢?

它们的设计目标、适用场景和计算方式有本质区别,核心区别一览

特性
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 个单位。
总结:
  • 当你有一个只增不减的指标(如请求数、错误数、网络流量字节数),并想知道它的活动速率时,使用 rate()。
  • 当你有一个可升可降的指标(如内存使用量、CPU温度、活跃连接数),并想知道它的变化趋势和速度时,使用 deriv()。
简单记忆口诀:rate() 用于 Counter 看吞吐,deriv() 用于 Gauge 看趋势。

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])

 

其他函数汇总

sum(irate(apiserver_request_total{client!=""}[$interval])) by (client)
histogram_quantile($quantile, sum(irate(apiserver_request_duration_seconds_bucket{verb="LIST"}[$interval])) by (pod_name, verb, resource, scope, le))
这些用法是什么?

 

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  远程读写】

这些配置用于将数据发送到远程存储(如长期存储系统),或从远程存储读取数据。这是 Prometheus 实现高可用、长期存储和与生态系统集成的重要特性。
例如:
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部分,可以看到配置已经更新