Prometheus监控全讲解 Prometheus监控理论 黑盒监控和白盒监控 黑盒监控指的是监控外部用户可见的系统行为,白盒监控指的是监控内部暴露出来的指标信息。它们一个对外,一个对内。二者在功能上有 2 点区别。
监控角度不同:黑盒更偏向于外侧,你可以理解为是通过某个功能或者具体的某个接口来观察,它并不清楚内部是如何实现的;而白盒则更倾向于从内侧监控,它是代码层面的,从内部的视角来解读整个系统。
面向对象不同:黑盒更倾向于看到这个问题的现象,比如某个端口挂掉了无法再提供服务,它面向的是当下所发生的故障;白盒则更加倾向于面向产生问题的原因,比如我们在日志中可以通过堆栈信息分析出故障的根源。
黑盒监控
黑盒中的监控一般可以细分为如下的 4 类内容。
端口状态:通过程序检测具体业务的端口是否存活。可以简单确定程序是否有在提供服务,如果端口都无法连接,那么肯定是系统出现了问题。通常我们也会结合进程检测使用,如果进程存活,但是端口不存在,则说明可能程序存在某些问题,没有将服务暴露出来。
证书检测:通过检测证书是否有效,确认用户是否可以正常访问。现在的网站服务基本都是使用的 HTTPS,如果证书出现了问题,则可能是浏览器认定为不安全,阻止了用户访问。
探活:通过心跳检测来判定服务是否存活,比如定期通过具体的某个接口检测我们的服务是否运行正常。如果在这里出现异常,则说明我们的系统存在问题。
端到端功能检测:这个就相对复杂一些。通常是通过定期进行端到端的测试,结合业务流程感知业务是否在执行正常,比如我们可以通过 UI 或者接口自动化测试工具,来确认页面中返回的数据或者数据是否是正确的。
白盒监控
白盒监控中重要的数据维度:
日志:通过日志记录可以了解到程序的运行状态,程序中是否存在异常。
指标:数值形式的指标可以帮助我们了解到系统中的数据走向、流量情况等。
链路追踪:我们可以细粒度到程序的代码行级别来将链路可视化,这可以帮助我们了解程序的执行流程。
黄金指标 黄金指标是 Google 针对大量分布式监控的经验总结,它可以在服务级别帮助衡量终端用户体验、服务中断、业务影响等层面的问题,有 4 类指标信息,分为错误、延迟、流量和饱和度 。无论你监控的数据再复杂、再令人眼花缭乱,都可以套用在这 4 类上。
错误
错误指的是当前系统所有发生过的错误请求,我们可以通过错误请求个数计算出相应的错误率。
在基础层中,我们可以把系统宕机、进程或者端口挂掉、网络丢包这样的情况认定为是故障。在业务层中,监控的内容也就相对比较多了,比如http状态码500、日志中出现的错误,都是可以被认定为“错误”的指标。
延迟
延迟指的是服务在处理请求时花费的时间。 我们经常说的接口耗时、响应时长指的就是延迟。在这里需要注意一点:一般在统计延迟时,并不会把成功或者错误的信息分开统计。这样的统计方式会使我们更难了解到真实的情况,所以在统计时常常需要把它们区分开。以一个 HTTP 接口为例,响应状态码正确的(200)和错误的(500)请求,它们的耗时一定会有差别,因为正确的请求走完了全流程,而错误的可能只进行了某一部分流程,我们就需要把这两个请求的耗时在统计时分别记录 。
在基础层中,我们可以监控 I/O 等待、网络延迟等信息。在业务层中,则可以监控接口调用的耗时、MySQL 查询的耗时等信息。
延迟在系统中是一个十分关键的指标,很多时候我们的服务并不会产生错误,但很可能会有延迟,延迟在 HTTP 层会影响用户体验,在数据库中出现高延迟会导致请求错误,这是我们需要着重关注的。
流量
流量是表现系统负载情况的数据,比如我们常见的 QPS、UV。通过这个指标我们能确切了解到服务当前承受了多大的压力,请求量和处理量有多大。我们常说的容量规划,其实是流量规划。通过流量,我们可以得知当前系统运行的状况,是否到达了它的负荷上限。
在基础层中,常见的监控指标有磁盘的读写速度、网络 I/O 情况等。在业务层中则有请求量、MySQL 查询次数等等。
通过观察流量相关的指标,可以了解到是否存在突增突降的情况,从而判断是否遭受到了攻击或者是否在进行某些活动。
饱和度
饱和度通常是指某个资源的使用率。通常指的是我们通过容量的最大值和现在的使用量,来判断这个容量是否“满”了。某些程序,如果资源饱和度过高,可能会导致执行缓慢,甚至无法使用。比如 CPU 使用率如果达到 100%,会出现执行缓慢。
饱和度一般也会配合其他指标一起使用,比如在使用网络 I/O 时,网卡都是有流量上限的,我们通过流量上限值和当前网络 I/O 的使用情况,可以得知相应的饱和度。饱和度是衡量我们这个系统是否到达瓶颈的关键。如饱和度过高,这时候就需要考虑扩容、减少数据量或是其他降低饱和度的操作了。
在基础层中,需要监控的指标有 CPU 使用率、I/O 使用率、句柄使用情况等。在业务层中则会有线程池的线程使用数、JVM 中的堆内存使用率等信息。
Prometheus架构
宏观来说,Prometheus的架构可以分为三段式:
Prometheus监控实践 部署Prometheus
storage.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer --- apiVersion: v1 kind: PersistentVolume metadata: name: prometheus-local labels: app: prometheus spec: accessModes: - ReadWriteOnce capacity: storage: 20Gi storageClassName: local-storage local: path: /data/k8s/prometheus nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-master-01 persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: prometheus-data namespace: monitor spec: selector: matchLabels: app: prometheus accessModes: - ReadWriteOnce resources: requests: storage: 20Gi storageClassName: local-storage
configmap.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: ConfigMap metadata: name: prometheus-config namespace: monitor data: prometheus.yml: | global: scrape_interval: 15s # Prometheus每隔15s就会从所有配置的目标端点抓取最新的数据 scrape_timeout: 15s # 某个抓取操作在 15 秒内未完成,会被视为超时,不会包含在最新的数据中。 evaluation_interval: 15s # # 每15s对告警规则进行计算 scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"]
deployment.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 apiVersion: apps/v1 kind: Deployment metadata: name: prometheus namespace: monitor labels: app: prometheus spec: selector: matchLabels: app: prometheus template: metadata: labels: app: prometheus spec: serviceAccountName: prometheus securityContext: runAsUser: 0 containers: - image: harbor.in-road.com/dengjinjun/prometheus:2.53.0 name: prometheus args: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--storage.tsdb.retention.time=24h' - '--web.enable-admin-api' - '--web.enable-lifecycle' ports: - containerPort: 9090 name: http volumeMounts: - mountPath: '/etc/prometheus' name: config-volume - mountPath: '/prometheus' name: data resources: requests: cpu: 100m memory: 512Mi limits: cpu: 100m memory: 512Mi volumes: - name: data persistentVolumeClaim: claimName: prometheus-data - configMap: name: prometheus-config name: config-volume tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Equal" value: "" effect: "NoSchedule" --- apiVersion: v1 kind: Service metadata: name: prometheus namespace: monitor labels: app: prometheus spec: selector: app: prometheus type: NodePort ports: - name: web port: 9090 targetPort: 9090
prometheus-rdbc.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 apiVersion: v1 kind: ServiceAccount metadata: name: prometheus namespace: monitor --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus rules: - apiGroups: - '' resources: - nodes - services - endpoints - pods - nodes/proxy verbs: - get - list - watch - apiGroups: - 'extensions' resources: - ingresses verbs: - get - list - watch - apiGroups: - '' resources: - configmaps - nodes/metrics verbs: - get - nonResourceURLs: - /metrics verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus subjects: - kind: ServiceAccount name: prometheus namespace: monitor
物理节点的监控 通过在物理节点上安装node exporter,然后在prometheus配置文件中添加监控项来实现
node-exporter.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 apiVersion: apps/v1 kind: DaemonSet metadata: name: node-exporter namespace: monitor labels: app: node-exporter spec: selector: matchLabels: app: node-exporter template: metadata: labels: app: node-exporter spec: hostPID: true hostIPC: true hostNetwork: true volumes: - name: proc hostPath: path: /proc - name: dev hostPath: path: /dev - name: sys hostPath: path: /sys - name: root hostPath: path: / tolerations: - operator: 'Exists' nodeSelector: kubernetes.io/os: linux containers: - name: node-exporter image: harbor.in-road.com/dengjinjun/node-exporter:1.3.1 args: - --web.listen-address=$(HOSTIP):9100 - --path.procfs=/host/proc - --path.sysfs=/host/sys - --path.rootfs=/host/root - --no-collector.hwmon - --no-collector.nfs - --no-collector.nfsd - --no-collector.nvme - --no-collector.dmi - --no-collector.arp - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/containerd/.+|/var/lib/docker/.+|var/lib/kubelet/pods/.+)($|/) - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$ ports: - containerPort: 9100 env: - name: HOSTIP valueFrom: fieldRef: fieldPath: status.hostIP resources: requests: cpu: 150m memory: 180Mi limits: cpu: 150m memory: 180Mi securityContext: runAsNonRoot: true runAsUser: 65534 volumeMounts: - name: proc mountPath: /host/proc - name: sys mountPath: /host/sys - name: root mountPath: /host/root mountPropagation: HostToContainer readOnly: true
prometheus.yml
这里添加监控项有两种方式,一种是手动添加静态监控项,另一种是配置动态发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 - job_name: "node-exporter" static_configs: - targets: ["10.100.183.5:9100" ,"10.100.183.6:9100" ,"10.100.183.7:9100" ,"10.100.183.8:9100" ,"10.100.183.10:9100" ,"10.100.183.13:9100" ] - job_name: 'nodes' kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__ ] regex: '(.*):10250' replacement: '${1}:9100' target_label: __address__ action: replace - action: labelmap regex: __meta_kubernetes_node_label_(.+) - job_name: 'kubelet' kubernetes_sd_configs: - role: node scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+)
集群中的组件 组件信息和集群中资源的信息,一般都自带了metrics接口,可以通过添加静态target或者动态发现,都是在prometheus.yaml
中配置的
添加物理节点上容器的监控项
添加容器相关信息,一般容器都是通过cadvisor来获取信息的,现在cadvisor会集成在kubelet组件中,所以本质上也还是通过kubelet提供的接口实现的,不过跟上面kubelet不同的是,容器相关的信息用的是另外一个接口:https://node_ip:10250/metrics/cadvisor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - job_name: 'kubernetes-cadvisor' kubernetes_sd_configs: - role: node scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) replacement: $1 - source_labels: [__meta_kubernetes_node_name ] regex: (.+) replacement: /metrics/cadvisor target_label: __metrics_path_
添加apiserver的监控项
这些以集群内Pod运行的系统组件都需要通过svc来访问,apiserver是有一个svc的,在默认的命名空间下,通过kubectl get ep
可以看到其endpoint。
1 2 3 4 5 6 7 8 9 10 11 12 - job_name: 'kubernetes-apiservers' kubernetes_sd_configs: - role: endpoints scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__meta_kubernetes_namespace ,__meta_kubernetes_service_name ,__meta_kubernetes_endpoint_port_name ] action: keep regex: default;kubernetes;https
添加controller-manager的监控项
Pod的IP是不固定的,controller-manager默认没有svc,需要先创建一个svc,给prometheus访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/component: kube-controller-manager app.kubernetes.io/name: kube-controller-manager k8s-app: kube-controller-manager name: kube-controller-manager namespace: kube-system spec: clusterIP: None ports: - name: https-metrics port: 10257 targetPort: 10257 protocol: TCP selector: component: kube-controller-manager
在/etc/kubernetes/manifest
目录下各个组件的配置文件中可以看到,--bind-address=127.0.0.1
,说明每台机器上的组件默认都是监听本机地址的,需要把这个地址改为0.0.0.0
,更改之后kubelet会自动重启它们。
1 2 3 4 5 6 7 8 9 10 11 12 - job_name: 'kube-controller-manager' kubernetes_sd_configs: - role: endpoints scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__meta_kubernetes_namespace ,__meta_kubernetes_service_name ,__meta_kubernetes_endpoint_port_name ] action: keep regex: kube-system;kube-controller-manager;https-metrics
类似的原理,添加kube-scheduler
的监控也是一样的,不在赘述
添加etcd的监控项
etcd跟上面两个有一些不同之处,一是它是http协议访问的
etcd-svc.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: v1 kind: Service metadata: namespace: kube-system name: etcd labels: k8s-app: etcd spec: selector: component: etcd type: ClusterIP clusterIP: None ports: - name: http port: 2381 targetPort: 2381 protocol: TCP
二是它监听地址的配置是--listen-metrics-urls=http://127.0.0.1:2381
1 2 3 4 5 6 7 8 - job_name: 'etcd' kubernetes_sd_configs: - role: endpoints scheme: http relabel_configs: - source_labels: [__meta_kubernetes_namespace , __meta_kubernetes_service_name , __meta_kubernetes_endpoint_port_name ] action: keep regex: kube-system;etcd;http
业务Pod信息 k8s中运行的服务,如果没有自带/metrics接口,比如Redis、MySQL,这类需要在Pod中使用sidecar模式启动exporter容器来实现,并且这个sidecar容器需要能被prometheus自动发现并访问。
综上,业务Pod的监控需要满足三个条件:
具备/metrics接口
具备svc,svc生成的endpoint能被prometheus访问到
svc需要具备一些注解或标签,以便于promtheus筛选,比如:prometheus.io/scrape:true
以coredns为例,三个条件都已经满足,下面是promtheus配置文件的补充内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 - job_name: 'kubernetes-endpoints' kubernetes_sd_configs: - role: endpoints relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape ] action: keep regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme ] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path ] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__ , __meta_kubernetes_service_annotation_prometheus_io_port ] action: replace target_label: __address__ regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 - action: labelmap regex: __meta_kubernetes_service_label_(.+) - source_labels: [__meta_kubernetes_namespace ] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_service_name ] action: replace target_label: kubernetes_name - source_labels: [__meta_kubernetes_pod_name ] action: replace target_label: kubernetes_pod_name
集群状态信息 上述监控在各个维度上提供了相应资源的监控项,但是并不完善,因为kubernetes集群中还有一些资源状态信息需要被收集和监控,比如:
某个业务Pod当前的副本数量是多少,有多少个是可用的
处于running/stopping/terminated状态的Pod有多少
Pod重启了多少次
有多少个Job在等待运行
无论是apiserver、cadvisor,还是业务Pod暴露的metrics信息,都无法提供上述指标,所以我们需要一种新的exporter,来提供全局的状态指标:kube-state-metrics
注意:kube-state-metrics
与metrics
并不相同,虽然它们都是用于监控指标的收集,但是metrics-server
提供实时的资源监控指标,更多的是给HPA做决策,不擅长做历史数据分析;而kube-state-metrics
更擅长做这件事,对接Prometheus提供历史数据分析。
部署
1 2 3 4 5 6 7 8 https://github.com/kubernetes/kube-state-metrics.git unzip kube-state-metrics-main.zip cd kube-state-metrics-main/examples/standardkubectl apply -f .
PromQL语法与Grafana出图 PromQL语法 prometheus监控中对于采集过来的数据统一称为metrics数据。总体上来说metrics是对采集过来的数据的一种统称。
metrics数据分为以下几种类型:
Gauge:最简单的度量指标,只有一个简单的返回值(瞬时状态),比如当前的cpu使用率,内存使用率,磁盘使用率,温度等,这种就是Gauge类型的代表。
Counter:Counters就是计数器,它的统计数据是递增的,比如http请求的总数,出现的错误总数,总的处理时间,api请求数等
Histograms:统计在一定的时间范围内数据的分布情况,还提供度量指标的总和,数据以直方图显示。其主要用于表示一段时间内对数据的采样,并能够对其指定区间及总数进行统计。根据统计区间计算。如请求的持续/延长时间,请求的响应大小等。
Summary:类似Histogram,用于表示一段时间内数据采样结果,其直接存储quantile数据,而不是根据统计区间计算出来的。不需要计算,直接存储结果。
PromQL是Prometheus实现监控查询的核心功能,通过PromQL,用户可以非常方便地对监控样本数据进行统计分析,PromQL支持常见的运算操作符,同时PromQL中还提供了大量的内置函数可以实现对数据的高级处理。
下面是集中常见的PromQL查询语法:
标签匹配查询 最简单的查询方式,Prometheus通过指标名称和标签唯一定义一条时间序列进行查询。
比如想要查询CPU总的使用时间,可以输入指标名称node_cpu_seconds_total
,可以看到出现的metrics项
如果想要查询更详细的维度,比如CPU在内核态的使用时间,可以使用标签,即在指标名称后追加{mode="system"}
使用技巧:
在使用标签查询时,可以添加多个标签,比如node_cpu_seconds_total{cpu="0",mode="system"}
在使用标签查询时,可以使用=
和!=
,比如node_cpu_seconds_total{mode="system"}
,不看内核态的时间
除了完全匹配外,还可以使用正则表达式,匹配符号使用=~
模糊匹配,使用!~
表示反向模糊匹配
使用正则表达式的时候,在标签的值里面使用|
表示逻辑或,比如node_cpu_seconds_total{mode=~"system|nice"}
上面几个例子查询到的都是瞬时向量,可以在查询表达式后面添加[1m]
,查询1分钟内所有样本数据,此时查询到的是区间向量。
二元操作 二元操作符包括:数学运算符,逻辑运算符,布尔运算符
数学运算
PromQL支持对查询到的数据进行数学运算,对结果进行二次加工。
+
(加法)
-
(减法)
*
(乘法)
/
(除法)
%
(求余)
^
(幂运算)
比如:
1 2 3 4 5 node_memory_MemFree_bytes / (1024 * 1024) node_disk_written_bytes_total + node_disk_read_bytes_total
布尔运算
PromQL支持对查询到的数据的值进行布尔运算,对结果进行过滤。
另外,使用bool修改符后,布尔运算不会对结果进行过滤,而是将各个样本的数据与标量进行比较,得出结果0或1
==
(相等)
!=
(不相等)
>
(大于)
<
(小于)
>=
(大于等于)
<=
(小于等于)
bool
(布尔修饰符,需要结合其他布尔符号使用)
比如:
1 2 3 4 5 6 7 8 node_cpu_seconds_total > 10 (node_memory_MemTotal_bytes - node_memory_MemFree_bytes)/node_memory_MemTotal_bytes > 0.9 http_requests_total > bool 1000
集合运算符
PromQL支持两个瞬时向量之间的操作,可以在两个瞬时向量之间进行相应的集合操作,产生一个新的向量
and
(并且)
or
(或者)
unless
(排除)
1 2 3 vector1 and vector2 会产生一个新的向量,新向量包含vector1中完全匹配vector2中的元素组成 vector1 or vector2 会产生一个新的向量,新向量包含vector1和vector2中所有的样本数据 vector1 unless vector2 会产生一个新的向量,新向量由vector1中没有与vector2匹配的元素组成
聚合操作 聚合操作符作用于瞬时向量,可以将瞬时表达式返回的样本数据进行聚合,形成一个新的时间序列。
Prometheus提供了下列内置的聚合操作符:
sum ()
求和
min ()
最小值
max()
最大值
avg()
平均值
stddev()
标准差
stdvar()
标准方差
count()
计数
count_values()
对value进行计数
bottomk()
后n条时序
topk()
前n条时序
quantile()
分位数
by()
分组
聚合操作的语法如下:
1 <aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
举例:
1 2 3 4 5 6 7 8 9 10 sum (node_cpu_seconds_total) sum (node_cpu_seconds_total)by(cpu) count(node_cpu_seconds_total) topk(3, sum (node_cpu_seconds_total) by (mode)) bottomk(3, sum (node_cpu_seconds_total) by (mode))
内置函数 此处介绍三个内置函数,用于区间向量
increase()
增长量
rate()
增长率
irate()
瞬时增长率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 increase() 函数中的参数是一个区间向量,increase函数获取区间向量中的第一个后最后一个样本并返回其增长量。因此,可以通过以下表达式计算CPU运行指标的平均每秒增长率:increase(node_cpu_seconds_total[2m])/120 rate()函数可以直接计算区间向量在时间窗口内平均增长速率。因此,通过下面的表达式可以得到与上面increase函数相同的结果:rate(node_cpu_seconds_total[2m]) 需要注意的是,使用rate()或者increase()函数去计算样本的平均增长速率,容易陷入“长尾问题”,即无法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,但是计算时间窗口内的平均增长率却无法反应出该问题。为了解决该问题,PromQL提供了另外一个灵敏度更高的函数irate(), irate函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率,所以可以近似反映瞬时增长率:irate(node_cpu_seconds_total[2m]) 总结:rate适用于增长趋势稳定的场景; irate适用于指标时刻发生剧烈变化的场景 INSTANCE=`hostname -s` DOCKER_NAMES=`docker -H docker-server ps --format "{{.Names}}" ` function get_runtime (){ start_time=`docker -H docker-server inspect -f '{{.State.StartedAt}}' $1 ` start_time_stamp=`date +%s -d "$start_time " ` now=`date +%s` let run_time=$now -$start_time_stamp echo $run_time return $run_time } echo "" "# HELP docker_runtime time sec # TYPE docker_runtime gauge " "" > temp.log for i in ${DOCKER_NAMES} do runtime=`get_runtime $i ` echo "docker_runtime{name=\"$i \"} $runtime " >> temp.log done curl --data-binary "@temp.log" http://192.168.110.155:9091/metrics/job/pushgateway/instance/$INSTANCE rm -f temp.log
Grafana出图 安装grafana,然后可以使用grafana社区中提供的在线模板,也可以把模板下载下来,修改其中的PromQL语句再添加到自己的dashboard,更进一步,可以自己创建dashboard,自己编写业务相关的PromQL语句。
不再赘述。
Altermanager告警 部署Alertmanager alertmanager-config.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 apiVersion: v1 kind: ConfigMap metadata: name: alert-config namespace: monitor data: template_email.tmpl: |- {{ define "email.html" }} {{- if gt (len .Alerts.Firing) 0 -}} @报警<br> {{- range .Alerts }} <strong>实例:</strong> {{ .Labels.instance }}<br> <strong>概述:</strong> {{ .Annotations.summary }}<br> <strong>详情:</strong> {{ .Annotations.description }}<br> <strong>时间:</strong> {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }} <br> {{- end -}} {{- end }} {{- if gt (len .Alerts.Resolved) 0 -}} @恢复<br> {{- range .Alerts }} <strong>实例:</strong> {{ .Labels.instance }}<br> <strong>信息:</strong> {{ .Annotations.summary }}<br> <strong>恢复:</strong> {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }} <br> {{- end -}} {{- end }} {{ end }} config.yml: |- templates: # 增加 templates 配置,指定模板文件 - '/etc/alertmanager/template_email.tmpl' inhibit_rules: - source_match: alertname: NodeMemoryUsage severity: critical target_match: severity: normal equal: - instance # 一、全局配置 global: resolve_timeout: 5m smtp_smarthost: 'smtp.163.com:25' smtp_from: '18611453110@163.com' smtp_auth_username: '18611453110@163.com' smtp_auth_password: 'YKQQBTNSCEXOONLH' smtp_hello: '163.com' smtp_require_tls: false # 二、设置报警的路由分发策略 route: group_by: ['alertname', 'cluster'] group_wait: 30s group_interval: 30s repeat_interval: 120s receiver: default # 默认的receiver:如果一个报警没有被一个route匹配,则发送给默认的接收器 routes: - receiver: email group_wait: 10s group_by: ['instance' ] match: team: node continue: true - receiver: mywebhook group_wait: 10s group_by: ['instance' ] match: team: node receivers: - name: 'default' email_configs: - to: '378533872@qq.com' send_resolved: true - name: 'email' email_configs: - to: '18611453110@163.com' send_resolved: true html: '{{ template "email.html" . }} ' - name: 'mywebhook' webhook_configs: - url: 'http://promoter:8080/dingtalk/webhook1/send' send_resolved: true --- apiVersion: apps/v1 kind: Deployment metadata: name: alertmanager namespace: monitor labels: app: alertmanager spec: selector: matchLabels: app: alertmanager template: metadata: labels: app: alertmanager spec: volumes: - name: alertcfg configMap: name: alert-config containers: - name: alertmanager image: registry.cn-hangzhou.aliyuncs.com/egon-k8s-test/alertmanager:v0.27.0 imagePullPolicy: IfNotPresent args: - '--config.file=/etc/alertmanager/config.yml' ports: - containerPort: 9093 name: http volumeMounts: - mountPath: '/etc/alertmanager' name: alertcfg resources: requests: cpu: 100m memory: 256Mi limits: cpu: 100m memory: 256Mi --- apiVersion: v1 kind: Service metadata: name: alertmanager namespace: monitor labels: app: alertmanager spec: selector: app: alertmanager type: NodePort ports: - name: web port: 9093 targetPort: http
Alert配置优化
报警规则
报警指标是由promtheus通过计算得到,所以promtheus的配置文件需要有所改动:
promtheus.yml
增加以下内容,对接alertmanager:
1 2 3 4 5 6 7 alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 rule_files: - /etc/prometheus/rules.yml
增加一个rules.yml
文件,挂载到/etc/prometheus/rules.yml
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 rules.yml: | groups: - name: recording_rules rules: - record: job:node_memory_MemFree_bytes:percent expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 - name: test-node-mem rules: - alert: NodeMemoryUsage expr: job:node_memory_MemFree_bytes:percent > 20 for: 2m labels: team: node severity: critical annotations: summary: "{{$labels.instance}}: High Memory usage detected" description: "{{$labels.instance}}: Memory usage is above 20% (current value is: {{ $value }}" - name: test-node-load rules: - alert: NodeLoad expr: node_load5 < 1 # 故意设置这个值,让它报警,正常应该是超过某个值才报警,而不是小于 for: 2m labels: team: node severity: normal annotations: summary: '{{ $labels.instance }}: Low node load deteched' description: '{{ $labels.instance }}: node load is below 1 (current value is: {{ $value }})'
延迟报警
延迟告警机制,可以用于防止瞬间的、短暂的噪音引起不必要的告警,从而提高告警的质量和可用性。下面是三个重要的参数:
1 2 3 4 5 6 group_wait: 30s group_interval: 30s repeat_interval: 120s
上述三个参数的综合解释:
当一个新的告警被创建时,等待至少 group_wait 时间来初始化通知,这种方式可以确保能有足够的时间为同一分组来获取/累积多个警报,然后一起触发这个报警信息。
然后开始一个 group_interval 窗口(例如 30 秒),在 group_interval 窗口内,任何新的同分组告警会被聚合到一起,但不会立即触发发送。到达短期聚合的时间点后,告警信息将一起发送。
长期聚合的窗口用于延迟原有为解决的告警信息,如果没有抵达 repeat_interval 的时间点,则原有未解决的报警不会重复发送,直到到达下一个 repeat_interval 时间点。
分组合并
在复杂的监控系统中,告警规则可能非常多,分组合并的机制可以根据不同的服务、应用或功能模块进行分类,将多个告警输出合并为一个通知,减少告警通知的数量,避免告警风暴。
1 2 group_by: ['alertname' , 'cluster' ]
静默
静默机制可以让一些监控报警在我们设定的时间内哑火/闭嘴,不要让其一直发报警,静默的应用场景一般是在处理已知问题或维护期间,频繁触发并发送的报警信息十分烦人,可以对该报警进行静默设置。
静默是在alertmanager的管理界面中配置的。
抑制
某个告警触发后,与其相关的一些报警可能伴随着一齐触发,比如节点宕机了,会触发节点宕机的报警,该节点上的服务也都挂掉了也会触发一系列这些服务的故障报警,然而根因是节点宕机了,所以那些随之产生的报警项是无用的干扰项,应该加以抑制。
可以在promtheus的配置文件中添加抑制规则:
1 2 3 4 5 6 7 8 inhibit_rules: - source_match: alertname: NodeMemoryUsage severity: critical target_match: severity: normal equal: - instance
告警恢复与通知
告警产生之后,需要给运维人员发送适当的通知,告警恢复之后,自然也需要告知相关人员系统告警状态已恢复。之所以把告警恢复与通知放到一块,是因为本质上,告警恢复机制也是通过告警状态的通知逻辑来实现的。
下面是定义的一个路由匹配规则和接收者
路由匹配,这是其中的一个子路由,继承父路由的所有属性,可以进行覆盖和更具体的规则匹配。
1 2 3 4 5 6 7 routes: - receiver: email group_wait: 10s group_by: ['instance' ] match: team: node continue: true
接收者的定义
1 2 3 4 5 6 receivers: - name: 'email' email_configs: - to: '378533872@qq.com' html: '{{ template "email.html" . }} ' send_resolved: true
Promtheus中的告警规则有两个状态:alerting
和 resolved
。在上面的告警规则中,关键字for
的值是2m,代表当告警条件expr
在定义的持续时间内保持为true
时,告警触发,告警状态变为alerting
,会给alertmanager发送告警状态,在alertmanager中,有一个参数resolve_timeout: 5m
,代表当alertmanager持续多长时间未接收到告警后,标记告警状态为 resolved
,即恢复状态。
定制模板
减少无用信息,突出重点信息,增强可读性,美观。
下面是在alertmanager中定义的一个邮件模板,template_email.tmpl
,作为一个文件单独挂载,可以在receivers中通过自定义的名字email.html
被指定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 template_email.tmpl: |- {{ define "email.html" }} {{- if gt (len .Alerts.Firing) 0 -}} @报警<br > {{- range .Alerts }} <strong > 实例:</strong > {{ .Labels.instance }}<br > <strong > 概述:</strong > {{ .Annotations.summary }}<br > <strong > 详情:</strong > {{ .Annotations.description }}<br > <strong > 时间:</strong > {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }} <br > {{- end -}} {{- end }} {{- if gt (len .Alerts.Resolved) 0 -}} @恢复<br > {{- range .Alerts }} <strong > 实例:</strong > {{ .Labels.instance }}<br > <strong > 信息:</strong > {{ .Annotations.summary }}<br > <strong > 恢复:</strong > {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }} <br > {{- end -}} {{- end }} {{ end }}
性能优化
当Promtheus中的告警规则过多时,不可避免的会增大系统开销,所以性能优化也是需要注意的一个点。
基于Recording Rule机制,可以把多条报警规则里共用的表达式提取出来,由prometheus server周期性计算、计算的结果当成一个指标/时序数据存放下来,如此,其他的报警规则直接共用该现成的结果即可,避免重复的计算的开销。
下面是一个Recording Rule机制的例子:
1 2 3 4 - name: recording_rules rules: - record: job:node_memory_MemFree_bytes:percent expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100
上面的例子中,把复杂的表达式定义了一个新的名字job:node_memory_MemFree_bytes:percent
,这个名字可以在其他告警规则中被引用,实现重用,并且还可以直接在Promtheus中使用这个名字代替PromQL语句查询。
对接第三方OA软件 参考:https://github.com/feiyu563/PrometheusAlert
Promethues Operator自定义监控项与报警
Promethues Operator高级用法