Prometheus 服务发现机制

Prometheus 默认才用pull取拉取监控数据的,也就是定时去目标主机去抓取metrics数据。所以每一个被抓取目标需要暴露一个HTTP接口,通过这个借口获取对应指标数据。

这种方式需要由目标服务决定抓取的采集目标有哪些,通过配置在scrape_config中的各种job实现,无法动态感知新服务。如果后续添加新服务就要手动改prometheus的配置,所以要用动态服务发现,通过服务发现,prometheus能查询到需要监控的Target列表,然后轮询这个Target获取监控数据

获取数据源target的方式有很多种,静态配置和服务发现,实现服务发现的方式有:

  • kubernetes_sd_configs #k8s服务发现,让prome动态发现k8s中被监控的目标
  • static_configs #静态服务发现,基于prometheus配置文件指定的监控目标(默认使用的)
  • dns_sd_configs #DNS服务发现监控数据
  • consul_sd_configs. #Consul服务发现,基于consul动态发现监控目标
  • file_sd_configs #基于指定的文件实现服务发现

一、kubernetes_sd_configs

1
2
3
4
5
6
promethues 的relabeling (重新修改标签)功能很强大,它能够在抓取到目标实例之前把目标实例的元数据标签动态重新修改,动态添加或者覆盖标签
prometheus 加载 target 成功之后,在Target 实例中,都包含一些 Metadata 标签信息,默认的标签有:
__address__:以<host>:<port>格式显示目标targets的地址
__scheme__:采集的目标服务地址的Scheme形式, HTTP 或者HTTPS
__metrics_path__:采集的目标服务的访问路径

基础功能-重新标记目的

1
2
3
relabel_configs: 在采集之前(比如在采集数据之前重新定义元标签),可以使用relabel_configs添加一些标签、也可以只采集特定目标或过滤目标。

metric_relabel_configs: 如果是已经抓取到指标数据时,可以使用metric_relabel_configs做最后的重新标记和过滤

配置 -> 重新标签(relabel_configs)-> 抓取 -> 重新标签(metric_relabel_configs) -> TSDB

1
2
3
4
5
6
7
8
9
10
11
- job_name: 'kubernetes-apiserver'
kubernetes_sd_configs: #基于kubernetes_sd_configs 实现服务发现
- role: endpoints # 发现endpoints
scheme: https #当前job使用的发现协议
tls_config: #证书配置
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt #容器里的证书路径
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token #容器里的token路径
relabel_configs: #重新re修改标签label配置configs
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep #action定义里relabel的具体动作,action支持多种
regex: default;kubernetes;https #发现了default命名空间的kubernetes服务且时https协议
label 详解
1
2
3
4
source_labels: 源标签,没有经过relabel处理之前的标签名字
target_label: 通过action处理之后的新的标签名字
regex: 正则表达式,匹配源标签
replacement: 通过分组替换后标签(target_label)对应的值
action 详解

https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_con

1
2
3
4
5
6
7
8
9
replace: 替换标签纸,根据regex正则匹配到源标签的值,使用replacement来引用表达式匹配的分组
keep: 满足regex正则条件的实例进行采集,把source_labels中没有匹配到regex正则内容的Target实例丢掉,即只采集匹配成功的实例

drop: 满足 regex 正则条件的实例不采集,把source_labels中匹配到regex正则内容的Target实例丢掉,即只采集没有匹配到的实例
---------------------------------------------------------
labelmap: 匹配 regex所有标签名称,然后复制匹配标签的值进行分组,通过replacement分组引用($[1),$[2),…)替代
labelkeep: 匹配 regex所有标签名称,其它不匹配的标签都将从标签集中删除
labeldrop: 匹配 regex所有标签名称,其它匹配的标签都将从标签集中删除

支持的发现目标类型

https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config

api-server 指标数据

监控 api-server 实现

可以直接通过k8s的service来获取

1
2
3
4
5
6
7
      - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https #含义为匹配default的namespace,svc名称时kubernetes并且协议时https,匹配成功后进行保留,并且把regex作为source_labels相对应的值。 即labels为key、regex为值

# label替换方式如下:
__meta_kubernetes_namespace=default,__meta_kubernetes_service_name=kubernetes,__meta_kubernetes_endpoint_port_name=https
#最终可以匹配到api-server的地址

apiserver_request_total

查询apiserver最近一分钟不同方法的请求数量统计:

1
sum(rate(apiserver_request_total[1m]))by(resource,subresource,verb)

irate 和 rate 都会用于计算某个指标在一定时间间隔内的变化速率。但是它们的计算方法有所不同: irate取的是在指定时间范围内的最近两个数据点来算速率,而rate会取指定时间范围内所有数据点,算出一组速率,然后取平均值作为结果。

1
2
3
4
5
6
7
# 所以官网文档说:irate适合快速变化的计数器(counter),而rate适合缓慢变化的计数器(counter)。

# 根据以上算法我们也可以理解,对于快速变化的计数器,如果使用rate,因为使用了平均值,很容易把峰值削平。除非我们把时间间隔设置得足够小,就能够减弱这种效应。
------------------------------------------------
rate(apiserver_request_total{code=~"^(?:2..)$"}[5m])
irate(apiserver_request_total{code=~"^(?:2..)$"}[5m])
# 两者有着一定的区别

关于 annotation_prometheus_io_scrape

在 k8s 中,基于prometheus的发现规则,需要在发现的目的 target 定义注解匹配 annotation_prometheus_io_scrape=true,且必须匹配成功该注解才会保留监控 target,然后再进行数据抓取并进行标签替换

如 annotation_prometheus_io_scrape 标签为http 或者htpps

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
- job_name: 'kubernetes-service-endpoints'  #job名称
kubernetes_sd_configs: #sd_configs 发现
- role: endpoints #角色,endpoint发现
relabel_configs: #标签重写配置
# annotation_prometheus_io_scrape 的值为true,保留标签然后再向下执行
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
# 将__meta_kubernetes_service_annotation_prometheus_io_scheme 修改为 __scheme__
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?) #正则匹配协议http或https,即其他协议不替换
# 将sourece_labels 替换为 target_label
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+) #路径为1到任意长度
#地址发现及标签重写
- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2 #格式为地址:端口
#发现新的label并用新的 service name 作为label、将发现的值依然新的label的值
- action: labelmap
regex: __meta_kubernetes_service_label_(.+) #通过正则匹配 service name
#将__meta_kubernetes_namespace替换为kubernetes_namespace
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
#将__meta_kubernetes_service_name替换为kubernetes_name
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_name
kube-dns 的服务发现

查看 kube-dns 状态

1
2
3
4
kubectl describe svc kube-dns -n kube-system

prometheus.io/port:9153 #注释标签,用于prometheus服务发现端口
prometheus.io/scrape:true #运行prometheus抓取数据

修改副本数进行验证:

1
2
3
4
kubectl edit deployments coredns -n kube-system
3

# prometheus上查看,确实自动发现新增pod
node 节点发现及指标
1
2
3
4
5
6
7
8
9
10
11
12
- job_name: 'kubernetes-node'  #job name
kubernetes_sd_configs: #发现配置
- role: node #发现角色
relabel_configs: #标签重写配置
- source_labels: [__address__] #源标签
regex: '(.*):10250' #通过正则匹配后缀为:10250的实例,10250是kubelet端口
replacement: '${1}:9100' #重写为IP:9100,即将端口替换为prometheus node-exporter的端口
target_label: __address__ #将[__address__]替换为__address__
action: replace #将[__address__]的值依然赋值给__address__
#发现新的label并用新的service name作为label、将发现的值给予新的label的值
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)

kubelet 监听端口

lsof -i : 10250

node 节点指标数据

curl ip:9100/metrics

常见监控指标

node_cpu_ :CPU相关指标

node_load1: load average #系统负载指标 (包括node_load5 、node_load15)

node_memory_ : 内存相关指标

node_network_ : 网络相关指标

node_disk_ : 磁盘IO相关指标

node_filesystem_ : 文件系统相关指标

node_boot_time_seconds: 系统启动时间监控

go_* : node exporter 运行中go相关指标

process_* : node exporter 进程内部指标

pod 发现及指标数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- job_name: 'kubernetes-node-cadvisor' #job名称
kubernetes_sd_configs: #基于k8s的服务发现
- role: node #角色
scheme: https #协议
tls_config: #证书配置
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt #默认证书路径
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token #默认token路径
relabel_configs: #标签重写配置
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
#replacement指定的替换后的标签(target_label)对应的值为 kubernetes.default.svc:443
- target_label: __address__
replacement: kubernetes.default.svc:443
#将[__meta_kubernetes_node_name]重写为__metrics_path__
- source_labels: [__meta_kubernetes_node_name]
regex: (.+) #至少1位长度以上
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor #指定值

tls_config 配置的证书地址是每个 Pod 连接 apiserver 所使用的地址,无论证书是否用得上,在 Pod 启动的时候 kubelet 都会给每一个 pod 自动注入 ca 的公钥,即所有的 pod 启动的时候都会有一个 ca 公钥被注入进去用于在访问 apiserver 的时候被调用。

验证一下:

1
2
3
4
5
6
7
8
9
10
kubectl exec test-pod1 ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt
namespace
token

kubectl exec test-pod1 md5sum /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
dc631beObb3e4ca96cb9c671e607aa89 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

md5sum /etc/kubernetes/ca.crt
dc631beObb3e4ca96cb9c671e607aa89 /etc/kubernetes/ca.crt

pod 监控指标

1
2
3
4
5
6
sum(rate(container_cpu_usage_seconds_total{image!=""}[1m]))without(instance)
sum(rate(container_memory_usage_bytes{image!=""}[1m]))without(instance)
sum(rate(container_fs_io_current{image!=""}[1m]))without(device)
sum(rate(container_fs_writes_bytes_total{image!=""}[1m]))without(device) sum(rate(container_fs_reads_bytes_total{image!=""}[1m]))without(device)
sum(rate(container_network_receive_bytes_total{image!=""}[1m]))without(interface)

prometheus 部署在 k8s 集群以外并实现服务发现

编写创建用户并授权的yaml

vim prom-rbac.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
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitoring
---
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/v1beta1
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: monitoring

应用并获取token

1
2
3
4
5
6
7
8
kubectl create -f prom-rbac.yaml

# 获取 token
kubectl get sa prometheus -n monitoring -o yaml

# 将 token 保存至 prometheus server 节点的 k8s.token 文件,后期用于权限验证
kubectl describe secret prometheus-token-ff53s -n monitoring
vim /apps/prometheus/k8s.token

prometheus 添加 job

vim /app/prometheus/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
30
31
32
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
#- "/apps/prometheus/rules/pods_rule.yaml"
# - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"

# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.

static_configs:
- targets: ["localhost:9090"]

- job_name: 'kubernetes-apiserver-monitor'
kubernetes_sd_configs:
- role: endpoints
api_server: https://---master ip---:6443 #修改IP
tls_config:
insecure_skip_verify: true
bearer_token_file: /apps/prometheus/k8s.token #添加token

.....

二、staic_config

很简单的静态配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# A scrape configuration containing exactly one endpoint to scrape: #端点的抓取配置
# Here it's Prometheus itself. #prometheus 的默认配置
scrape_configs:
#作业名称 job=<job_name>会自动添加到此配置的时间序列数据中
# The job name is added as a label 'job=<job_name>' to any timeseries scraped from this config.
- job_name: "prometheus" #job 名称
# metrics_path defaults to '/metrics' #默认 uri
# scheme defaults to 'http'. #协议
static_configs: #静态服务配置
- targets: ["localhost:9090"] #目标端点地址 - job_name: 'promethues-node'
static_configs:
- targets: ['192.168.15.100:9100','192.168.15.101:9100']
- job_name: 'prometheus-containers'
static_configs:
- targets: ["192.168.15.100:8080","192.168.15.101:8080"]

三、consul_sd_configs

https://www.consul.io/

https://releases.hashicorp.com/consul/1.11.1/

部署集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
192.168.3.101
192.168.3.102
192.168.3.103

# Node1:
unzip consul_1.11.1_linux_arm64.zip
cp consul /usr/local/bin/
scp consul 192.168.3.102:/usr/local/bin/
scp consul 192.168.3.103:/usr/local/bin/

# consul -h #验证可执行

# 分别创建数据目录
mkdir /data/consul/ -p

# 启动服务
node1:
nohup ./consul agent -server -bootstrap -bind=192.168.15.100 -client=192.168.15.100 -data-dir=/data/consul -ui -node=192.168.15.100 &

node2:
nohup ./consul agent -bind=192.168.15.101 -client=192.168.15.101 -data-dir=/data/consul -node=192.168.15.101 -join=192.168.15.100 &

node3:
nohup ./consul agent -bind=192.168.15.102 -client=192.168.15.102 -data-dir=/data/consul -node=192.168.15.102 -join=192.168.15.100 &

测试写入数据

通过 consul 的 API 写入数据

1
2
3
4
5
6
curl -X PUT -d '{"id": "node-exporter100", "name": "node-exporter100", "address": "192.168.15.100","port":9100,"tags":["node-exporter"], "checks":[{"http":"http://192.168.15.100:9100/","interval":"5s"}]}' http://192.168.15.100:8500/v1/agent/service/register


curl -X PUT -d '{"id": "node-exporter101", "name": "node-exporter101", "address": "192.168.15.101","port":9100,"tags":["node-exporter"], "checks":[{"http":"http://192.168.15.101:9100/","interval":"5s"}]}' http://192.168.15.101:8500/v1/agent/service/register

# 登入 192.168.15.100:8500 验证
配置 prometheus 到 consul 发现服务

主要配置字段:

1
2
3
4
static_configs:  #配置数据源
consul_sd_configs: #指定基于 consul 服务发现的配置
rebel_configs: #重新标记
services: [] #表示配 consul 中所有的service

K8s 部署 prometheus 配置文件

vim prometheus-cfg.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#添加下面内容
- job_name: 'consul'
honor_lables: true
metrics_path: /metrics
scheme: http
consul_sd_configs:
- server: 192.168.15.100:8500
services: []
- server: 192.168.15.101:8500
services: []
- server: 192.168.15.102:8500
services: []
relabel_configs:
- source_labels: ['__meta_consul_tags']
target_label: 'product'
- source_labels: ['__meta_consul_dc']
target_label: 'idc'
- source_labels: ['__meta_consul_service']
regex: "consul"
action: drop

二进制 prometheus 配置文件

vim prometheus.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#添加下面内容
- job_name: 'consul'
honor_lables: true
metrics_path: /metrics
scheme: http
consul_sd_configs:
- server: 192.168.15.100:8500
services: [] #发现目标服务名称,空为所有服务,也可以写serviceA,serviceB...
- server: 192.168.15.101:8500
services: []
- server: 192.168.15.102:8500
services: []
relabel_configs:
- source_labels: ['__meta_consul_tags']
target_label: 'product'
- source_labels: ['__meta_consul_dc']
target_label: 'idc'
- source_labels: ['__meta_consul_service']
regex: "consul"
action: drop

重启prometheus

systemctl restart prometheus.service

测试 consul 服务删除

1
curl --request PUT http://192.168.15.100:8500/v1/agent/service/deregister/node-exporter100

四、file_sd_configs

编辑 sd_configs 文件

1
2
3
4
5
6
7
8
9
10
# pwd
/apps/prometheus

mkdir file_sd
vim file_sd/sd_my_server.json
[
{
"targets": ["192.168.15.100:9100", "192.168.15.101:9100","192.168.15.102:9100"]
}
]

prometheus 调用 sd_configs

1
2
3
4
5
6
7
8
9
# vim prometheus.yml
- job_name: 'file_sd_my_server'
file_sd_configs:
- files:
- /apps/prometheus/file_sd/sd_my_server.json
refresh_interval: 10s

# 重启服务
systemctl restart prometheus

五、DNS 服务发现

1
2
3
没有DNS服务器做实验 ... 像SRV服务发现做不了,那种就是在prome的配置文件中在type值写为SRV

可以在 /etc/hosts 修改一下,做一下A记录服务发现,但没必要...