环境信息
kubectl version
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.9", GitCommit:"a17149e1a189050796ced469dbd78d380f2ed5ef", GitTreeState:"clean", BuildDate:"2020-04-16T11:44:51Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.9", GitCommit:"a17149e1a189050796ced469dbd78d380f2ed5ef", GitTreeState:"clean", BuildDate:"2020-04-16T11:36:15Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
术语解释
- Endpoint:抓取目标实际访问的抓取地址,组成为:
__scheme__
+__address__
+__metrics_path__
。因此,很多标签重写都是重写的其中之一,或者全部,例如,__address_
以及__metrics_path_
两个是高频重写点
原理解析
有的应用具有暴露容器内具体进程性能指标的需求,这些指标由应用侧实现采集并暴露,平台侧做汇聚。具体方案有如下两种:
- 业务应用自己暴露性能指标
- 通过Sidecar方式,集成相关exporter来暴露指标(本例属于此种方案)
此种方案中,一般还需要通过
__meta_kubernetes_endpoint_port_name
进行进一步的过滤,仅仅保留Pod中通过sidecar方式打入的exporter容器端点才行,正常是不需要业务容器端点的
本例中,Prometheus自动发现K8S的EndPoint地址,同时,通过标签重写可以构造出Pod中部署的实际监控代理的抓取目标端点;例如,本例中我们将redis_exporter
作为Sidecar
容器一起部署到了redis
本身所在的Pod中,因此,redis_exporter
还需要监控localhost:6379
即可,同时,也通过POD
的IP暴露出自己的端点出来,供Prometheus服务器抓取指标数据之用。
如何标识哪些是主动暴露监控指标的应用并获取指标
平台侧可以约定好带哪些annotation前缀的服务是自主暴露监控指标的服务。应用添加平台侧约定的这些annotations,平台侧可以根据这些annotations实现Prometheus的scrape。
例如,应用侧为自己的服务添加如下平台侧约定约定的annotation:
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
prometheus.io/port: "9121"
prometheus.io/app-scrape: "true",过滤应用业务组件监控类型
如何给应用加一些自定义标记信息
可能还需要根据平台和业务的需求添加其他一些以prometheus.io/app-label-
为前缀的annotation,Prometheus截取下前缀,保留后半部分做key,连同value保留下来。这样满足在平台对应用做其他一些标识的需求。比如加入如下annotation来标识应用所属的的环境、租户以及应用名称:
prometheus.io/app-label-env: "dev"
prometheus.io/app-label-tenant: "hzero"
prometheus.io/app-label-name: "redis-app"
Pod准备
mkdir -p /u01/repo/exporter
cd /u01/repo/exporter
vim prom-redis.yaml
kubectl apply -f prom-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: kube-system
labels:
app: redis
spec:
selector:
matchLabels:
app: redis
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
prometheus.io/port: "9121"
prometheus.io/app-scrape: "true"
prometheus.io/app-label-env: "dev"
prometheus.io/app-label-tenant: "hzero"
prometheus.io/app-label-name: "redis-app"
labels:
app: redis
spec:
containers:
- name: redis
image: redis:4
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 6379
- name: redis-exporter
image: oliver006/redis_exporter:latest
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9121
---
kind: Service
apiVersion: v1
metadata:
name: redis
namespace: kube-system
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
- name: prom
port: 9121
targetPort: 9121
几点需要注意:
- 此处的命名空间是
kube-system
,那么,在Prometheus抓取任务中,K8S服务发现需要添加这个命名空间,否则无法抓取 - 业务侧需要增加三个元数据标签注解
- prometheus.io/scrape: “true”
- prometheus.io/path: “/metrics”
- prometheus.io/port: “9121”
- prometheus.io/app-scrape: “true”:过滤应用业务组件监控类型
- 业务侧可以按需增加自定义元数据标签注解(注意:按需在Pod上或者Service上)
- prometheus.io/app-label-env: “dev”
- prometheus.io/app-label-tenant: “hzero”
- prometheus.io/app-label-name: “redis-app”
- 访问方式
- 方式一:通过
apiserver proxy URLs
方式访问(也即本例,推荐采用此种方案
) - 方式二:Service维度需要通过
NodePort
方式暴露redis_exporter
的指标端口,例如,30021
,也即prometheus.io/port
注解的值,然后通过节点IP+NodePort
方式访问
- 方式一:通过
此种方式需要将
__address_
重写为:节点ID:prometheus.io/port
,也即:
relabel_configs:
- source_labels: [__address__, __meta_kubernetes_pod_host_ip,__meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;([^:]+);(\d+)
replacement: $2:$3
target_label: __address__
注意,正则表达式中,
?:
开头的为非捕获组,因此,对于10.244.24.143:6379;172.23.16.106;30021
来讲,$0
代表整个表达式,$1
代表10.244.24.143
(因为第二段的6379
为非捕获组(?::\d+)?
),$2
代表172.23.16.106
,$3
代表30021
,最终构造出了172.23.16.106:30021
。具体可参考正则表达式高级用法(分组与捕获)。一般的材料上直接替换了端口部分,IP部分仍然使用Pod的IP,这是因为有一个前提是,Prometheus本身就部署在了待监控K8S集群中,因此,可以访问到集群内的IP地址及端口信息,因此,也就不需要使用NodePort
方式再做一层映射了。
- 我们自动发现的是EndPoint,而如果后端承载着Pod,则可以获取到Pod的标签注解,因此,只需要Pod上有相关标签注解即可。Service暂时是不需要的。下一节探测Service时会添加上;那么,此处为什么又要有Service呢?是为了对外暴露后端Pod服务的。
- 抓取任务中,协议为
http
核心配置项解析
抓取任务配置
任务编码 | 指标路径 | 协议 | 请求参数 | 认证授权及TLS | 说明 |
---|---|---|---|---|---|
K8S-APP-CUSTOM | /metrics | https | - | Token认证,禁用验证服务器端证书 | 访问协议为https |
K8S服务发现配置
资源类型 | API Server地址 | 命名空间 | 认证授权及TLS | 说明 |
---|---|---|---|---|
endpoints | https://172.23.16.106:8443 | default,kube-system | Token认证,禁用验证服务器端证书 | API Server地址此处配置的是负载均衡地址,命名空间正常讲应该留空,代表所有空间;本例中我们构造的Pod在kube-system 命名空间中,因此需要将其纳入进来 |
标签重写配置
K8S-APP-CUSTOM
标签重写主要关注如下几点:
- 需要过滤掉不需要监控的端点,仅保留
prometheus.io/scrape: true
、prometheus.io/app-scrape: true
及__meta_kubernetes_endpoint_port_name: prom
的端点;至于是在Pod上还是Service上,需要视情况而定,本例中打在了Pod上。
特别注意:一定需要通过
__meta_kubernetes_endpoint_port_name
限制端口名称,否则,在本例中,同一个Pod通过Sidecar方式既运行了业务容器Redis
,也同时运行了监控组件redis-exporter
,那么,依据endpoints角色的发现机制,会同时发现两个端点抓取目标,但是实际上我们只需要redis-exporter
的这条数据,通过端口名称过滤可以解决这个问题(同一个Pod多个端口时,K8S要求必须指定端口名称)。
- 将
instance
标签设置为原始的抓取目标地址,也即保留原始抓取目标地址 - 将
__address__
标签设置为API Server的地址,因为,本例中通过apiserver proxy URLs
方式实现监控 - 替换
__metrics_path__
为apiserver proxy URLs
,注意,此处的端口部分必须是prometheus.io/port
指定的端口,而不能是__meta_kubernetes_pod_container_port_number
,此Pod将会存在多个容器、多个端口 - 添加额外的富有含义的指标标签
- 生成
命名空间
标签 - 生成
服务名
标签 - 添加额外的业务含义的指标标签
| 序号 | 重写动作 | 来源标签名称 | 分隔符 | 正则匹配 | Hash模数 | 目标标签名称 | 目标标签替换值 |
| —- | —- | —- | —- | —- | —- | —- | —- |
| 0 | 保持 |
[__meta_kubernetes_pod_annotation_prometheus_io_scrape,__meta_kubernetes_pod_annotation_prometheus_io_app_scrape,__meta_kubernetes_endpoint_port_name]
| | true;true;prom | | | | | 1 | 替换 |[__address__]
| | | |instance
| | | 2 | 替换 | | | | |__address__
| 172.23.16.106:8443 | | 3 | 替换 |[__meta_kubernetes_namespace, __meta_kubernetes_pod_name, __meta_kubernetes_pod_annotation_prometheus_io_port]
| |([^;]+);([^;]+);([^;]+)
| |__metrics_path__
|/api/v1/namespaces/${1}/pods/http:${2}:${3}/proxy/metrics
| | 4 | 标签映射 | | | |__meta_kubernetes_pod_label_(.+)
| | | | 5 | 替换 |[__meta_kubernetes_namespace]
| | | | kubernetesnamespace | | | 6 | 替换 |[__meta_kubernetes_pod_name]
| | | | kubernetes_pod_name | | | 7 | 标签映射 | | | | `__meta_kubernetes_pod_annotation_prometheus_io_app_label(.+)` | | |
最终抓取任务配置
- job_name: K8S-APP-CUSTOM
honor_timestamps: true
scrape_interval: 1m
scrape_timeout: 10s
metrics_path: /metrics
scheme: https
file_sd_configs:
- files:
- /u01/prometheus/target/nodes/K8S-APP-CUSTOM_targets_hosts.json
refresh_interval: 5m
kubernetes_sd_configs:
- api_server: https://172.23.16.106:8443
role: endpoints
bearer_token: <secret>
tls_config:
insecure_skip_verify: true
namespaces:
names:
- default
- kube-system
bearer_token: <secret>
tls_config:
insecure_skip_verify: true
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape, __meta_kubernetes_pod_annotation_prometheus_io_app_scrape,
__meta_kubernetes_endpoint_port_name]
separator: ;
regex: true;true;prom
replacement: $1
action: keep
- source_labels: [__address__]
separator: ;
regex: (.*)
target_label: instance
replacement: $1
action: replace
- separator: ;
regex: (.*)
target_label: __address__
replacement: 172.23.16.106:8443
action: replace
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_pod_name, __meta_kubernetes_pod_annotation_prometheus_io_port]
separator: ;
regex: ([^;]+);([^;]+);([^;]+)
target_label: __metrics_path__
replacement: /api/v1/namespaces/${1}/pods/http:${2}:${3}/proxy/metrics
action: replace
- separator: ;
regex: __meta_kubernetes_pod_label_(.+)
replacement: $1
action: labelmap
- source_labels: [__meta_kubernetes_namespace]
separator: ;
regex: (.*)
target_label: kubernetes_namespace
replacement: $1
action: replace
- source_labels: [__meta_kubernetes_pod_name]
separator: ;
regex: (.*)
target_label: kubernetes_pod_name
replacement: $1
action: replace
- separator: ;
regex: __meta_kubernetes_pod_annotation_prometheus_io_app_label_(.+)
replacement: $1
action: labelmap
- separator: ;
regex: (.*)
target_label: PersonNum
replacement: hops
action: replace
- separator: ;
regex: (.*)
target_label: _tenant_id
replacement: "0"
action: replace
验证预览
注意:其中的
env
、name
、tenant
均是业务含义的自定义指标标签。同时仅保留了instance=10.244.46.126:9121
也即redis-exporter
的容器端点。
结论
此种方案适合于node_exporter
、redis_exporter
等手工打到Pod容器当中的监控代理自动发现方式。