Prometheus之kubernetes-sd自动发现
一、什么是服务发现?
我们在每个节点上面都运行了 node-exporter,如果我们通过一个 Service 来将数据收集到一起用静态配置的方式配置到 Prometheus 去中,就只会显示一条数据,我们得自己在指标数据中去过滤每个节点的数据,当然我们也可以手动的把所有节点用静态的方式配置到 Prometheus 中去,但是以后要新增或者去掉节点的时候就还得手动去配置,那么有没有一种方式可以让 Prometheus 去自动发现我们节点的 node-exporter 程序,并且按节点进行分组呢?这就是 Prometheus 里面非常重要的「服务发现」功能。
配置参考链接:https://prometheus.io/docs/prometheus/latest/configuration/configuration/
Prometheus支持多种服务发现机制:文件、DNS、Consul、Kubernetes、OpenStack、EC2等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮训这些Target获取监控数据,下面主要介绍Kubernetes服务发现机制。
目前,在Kubernetes下,Prometheus 通过与 Kubernetes API 集成主要支持5种服务发现模式:Node、Service、Pod、Endpoints、Ingress。不同的服务发现模式适用于不同的场景,例如:node适用于与主机相关的监控资源,如节点中运行的Kubernetes 组件状态、节点上运行的容器状态等;service 和 ingress 适用于通过黑盒监控的场景,如对服务的可用性以及服务质量的监控;endpoints 和 pod 均可用于获取 Pod 实例的监控数据,如监控用户或者管理员部署的支持 Prometheus 的应用。
官方文档如下:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config
二、角色类型
1. node
node角色可以发现集群中每个node节点的地址端口,默认为Kubelet的HTTP端口。目标地址默认为Kubernetes节点对象的第一个现有地址,地址类型顺序为NodeInternalIP、NodeExternalIP、NodeLegacyHostIP和NodeHostName。
可用标签
__meta_kubernetes_node_name: node节点的名称__meta_kubernetes_node_label_: k8s中node节点的标签.代表标签名称__meta_kubernetes_node_labelpresent_: 标签存在则为true.代表标签名称__meta_kubernetes_node_annotation_: k8s中node节点的注解.代表注解名称__meta_kubernetes_node_annotationpresent_: 注解存在则为true.代表注解名称-
__meta_kubernetes_node_address_: 不同类型的node节点地址,例如:_meta_kubernetes_node_address_Hostname="test-k8s-node1"_meta_kubernetes_node_address_InternalIP="10.0.0.11"
instance: 从apiserver获取到的节点名称
2. service
service角色可以发现每个service的ip和port,将其作为target。这对于黑盒监控(blackbox)很有用
可用标签
__meta_kubernetes_namespace: service所在的命名空间__meta_kubernetes_service_annotation_: k8s中service的注解__meta_kubernetes_service_annotationpresent_: 注解存在则为true__meta_kubernetes_service_cluster_ip: k8s中service的clusterIP__meta_kubernetes_service_external_name: k8s中service的external_name__meta_kubernetes_service_label_: k8s中service的标签__meta_kubernetes_service_labelpresent_: 标签存在则为true__meta_kubernetes_service_name: k8s中service的名称__meta_kubernetes_service_port_name: k8s中service的端口__meta_kubernetes_service_port_protocol: k8s中service的端口协议__meta_kubernetes_service_type: k8s中service的类型
3. pod
pod角色可以发现所有pod并将其中的pod ip作为target。如果有多个端口或者多个容器,将生成多个target(例如:80,443这两个端口,pod ip为10.0.244.22,则将10.0.244.22:80,10.0.244.22:443分别作为抓取的target)。
如果容器没有指定的端口,则会为每个容器创建一个无端口target,以便通过relabel手动添加端口。
__meta_kubernetes_namespace: pod所在的命名空间__meta_kubernetes_pod_name: pod的名称__meta_kubernetes_pod_ip: pod的ip__meta_kubernetes_pod_label_: pod的标签__meta_kubernetes_pod_labelpresent_: 标签存在则为true__meta_kubernetes_pod_annotation_: pod的注解__meta_kubernetes_pod_annotationpresent_: 注解存在则为true__meta_kubernetes_pod_container_init: 如果容器是InitContainer,则为true__meta_kubernetes_pod_container_name: 容器的名称__meta_kubernetes_pod_container_port_name: 容器的端口名称__meta_kubernetes_pod_container_port_number: 容器的端口号__meta_kubernetes_pod_container_port_protocol: 容器的端口协议__meta_kubernetes_pod_ready: pod的就绪状态,true或false。__meta_kubernetes_pod_phase: pod的生命周期状态.Pending,Running,Succeeded,FailedorUnknown__meta_kubernetes_pod_node_name: pod所在node节点名称__meta_kubernetes_pod_host_ip: pod所在node节点ip__meta_kubernetes_pod_uid: pod的uid__meta_kubernetes_pod_controller_kind: pod控制器的类型ReplicaSet,DaemonSet,Job,StatefulSet...__meta_kubernetes_pod_controller_name: pod控制器的名称
4. endpoints
endpoints角色可以从ep列表中发现target。对于每个ep地址和端口都会发现target。如果端点由Pod支持,则该Pod的所有其他容器端口(未绑定到端点端口)也将作为目标。
可用标签
__meta_kubernetes_namespace: ep对象所在的命名空间__meta_kubernetes_endpoints_name: ep的名称-
直接从ep对象的列表中获取的所有target,下面的标签将会被附加上
__meta_kubernetes_endpoint_hostname: ep的主机名__meta_kubernetes_endpoint_node_name: ep的node节点名__meta_kubernetes_endpoint_ready: ep的就绪状态,true或false。__meta_kubernetes_endpoint_port_name: ep的端口名称__meta_kubernetes_endpoint_port_protocol: ep的端口协议__meta_kubernetes_endpoint_address_target_kind: ep对象的目标类型,比如Pod__meta_kubernetes_endpoint_address_target_name: ep对象的目标名称,比如pod名称
- 如果ep是属于service的话,则会附加
service角色的所有标签 - 对于ep的后端节点是pod,则会附加
pod角色的所有标签(即上边介绍的pod角色可用标签)
比如我么手动创建一个ep,这个ep关联到一个pod,则prometheus的标签中会包含这个pod角色的所有标签
5. ingress
ingress角色发现ingress的每个路径的target。这通常对黑盒监控很有用。该地址将设置为ingress中指定的host。
可用标签
__meta_kubernetes_namespace: ingress所在的命名空间__meta_kubernetes_ingress_name: ingress的名称__meta_kubernetes_ingress_label_: ingress的标签__meta_kubernetes_ingress_labelpresent_: 标签存在则为true__meta_kubernetes_ingress_annotation_: ingress的注解__meta_kubernetes_ingress_annotationpresent_: 注解存在则为true__meta_kubernetes_ingress_scheme: ingress的协议,如果设置了tls则是https,默认http__meta_kubernetes_ingress_path: ingress中指定的的路径。默认为/
三、规则解析
kubernetes SD 配置允许从 kubernetes REST API 接受搜集指标,且总是和集群保持同步状态,任何一种 role 类型都能够配置来发现我们想要的对象。
规则配置使用 yaml 格式,下面是文件中一级配置项。自动发现 k8s Metrics 接口是通过 scrape_configs 来实现的:
#全局配置
global:
#规则配置主要是配置报警规则
rule_files:
#抓取配置,主要配置抓取客户端相关
scrape_configs:
#报警配置
alerting:
#用于远程存储写配置
remote_write:
#用于远程读配置
remote_read:
举例说明:
# Kubernetes的API SERVER会暴露API服务,Promethues集成了对Kubernetes的自动发现,它有5种模式:Node、Service # 、Pod、Endpoints、ingress,下面是Prometheus官方给出的对Kubernetes服务发现的实例。这里你会看到大量的relabel_configs, # 其实你就是把所有的relabel_configs去掉一样可以对kubernetes做服务发现。relabel_configs仅仅是对采集过来的指标做二次处理,比如 # 要什么不要什么以及替换什么等等。而以__meta_开头的这些元数据标签都是实例中包含的,而relabel则是动态的修改、覆盖、添加删除这些标签 # 或者这些标签对应的值。而且以__开头的标签通常是系统内部使用的,因此这些标签不会被写入样本数据中,如果我们要收集这些东西那么则要进行 # relabel操作。当然reabel操作也不仅限于操作__开头的标签。 # # action的行为: # replace:默认行为,不配置action的话就采用这种行为,它会根据regex来去匹配source_labels标签上的值,并将并将匹配到的值写入target_label中 # labelmap:它会根据regex去匹配标签名称,并将匹配到的内容作为新标签的名称,其值作为新标签的值 # keep:仅收集匹配到regex的源标签,而会丢弃没有匹配到的所有标签,用于选择 # drop:丢弃匹配到regex的源标签,而会收集没有匹配到的所有标签,用于排除 # labeldrop:使用regex匹配标签,符合regex规则的标签将从target实例中移除,其实也就是不收集不保存 # labelkeep:使用regex匹配标签,仅收集符合regex规则的标签,不符合的不收集 global: # 间隔时间 scrape_interval: 30s # 超时时间 scrape_timeout: 10s # 另一个独立的规则周期,对告警规则做定期计算 evaluation_interval: 30s # 外部系统标签 external_labels: prometheus: monitoring/k8s prometheus_replica: prometheus-k8s-1 # 抓取服务端点,整个这个任务都是用来发现node-exporter和kube-state-metrics-service的,这里用的是endpoints角色,这是通过这两者的service来发现 # 的后端endpoints。另外需要说明的是如果满足采集条件,那么在service、POD中定义的labels也会被采集进去 scrape_configs: # 定义job名称,是一个拉取单元 - job_name: "kubernetes-endpoints" # 发现endpoints,它是从列出的服务端点发现目标,这个endpoints来自于Kubernetes中的service,每一个service都有对应的endpoints,这里是一个列表 # 可以是一个IP:PORT也可以是多个,这些IP:PORT就是service通过标签选择器选择的POD的IP和端口。所以endpoints角色就是用来发现server对应的pod的IP的 # kubernetes会有一个默认的service,通过找到这个service的endpoints就找到了api server的IP:PORT,那endpoints有很多,我怎么知道哪个是api server呢 # 这个就靠source_labels指定的标签名称了。 kubernetes_sd_configs: # 角色为 endpoints - role: endpoints # 下面的含义是源标签__meta_kubernetes_namespace等如果其值为default;kubernetes;https标签顺序和值要对应。换句话说就是 # 当__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name三者对应的 # 值为default、kubernetes、https则进行保留,而且该endpoints对应的地址为api server的地址。 # # __meta_kubernetes_namespace 端点对象的命名空间,在不同对象上这个标签的含义不同,在角色是endpoints中这个是端点对象的名称空间 # __meta_kubernetes_service_name 端点对象的服务名称 # __meta_kubernetes_endpoint_port_name 端点的端口名称 # # kubernetes默认在default名称空间有一个叫做kubernetes的service,所以这个service的有3个设置对应的就是下面三个标签 # __meta_kubernetes_namespace 值为default # __meta_kubernetes_service_name 值为kubernetes # __meta_kubernetes_endpoint_port_name 值为https relabel_configs: # 重新打标仅抓取到的具有 "prometheus.io/scrape: true" 的annotation的端点,意思是说如果某个service具有prometheus.io/scrape = true annotation声明则抓取 # annotation本身也是键值结构,所以这里的源标签设置为键,而regex设置值,当值匹配到regex设定的内容时则执行keep动作也就是保留,其余则丢弃. # node-exporter这个POD的service里面就有一个叫做prometheus.io/scrape = true的annotations所以就找到了node-exporter这个POD - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] # 动作 删除 regex 与串联不匹配的目标 source_labels action: keep # 通过正式表达式匹配 true regex: true # 重新设置scheme # 匹配源标签__meta_kubernetes_service_annotation_prometheus_io_scheme也就是prometheus.io/scheme annotation # 如果源标签的值匹配到regex则把值替换为__scheme__对应的值 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] action: replace target_label: __scheme__ regex: (https?) # 匹配来自 pod annotationname prometheus.io/path 字段 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] # 获取POD的 annotation 中定义的"prometheus.io/path: XXX"定义的值,这个值就是你的程序暴露符合prometheus规范的metrics的地址 # 如果你的metrics的地址不是 /metrics 的话,通过这个标签说,那么这里就会把这个值赋值给 __metrics_path__这个变量,因为prometheus # 是通过这个变量获取路径然后进行拼接出来一个完整的URL,并通过这个URL来获取metrics值的,因为prometheus默认使用的就是 http(s)://X.X.X.X/metrics # 这样一个路径来获取的。 action: replace # 匹配目标指标路径 target_label: __metrics_path__ # 匹配全路径 regex: (.+) # 匹配出 Pod ip地址和 Port - 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 # service 对象的名称 - source_labels: [__meta_kubernetes_service_name] action: replace target_label: kubernetes_name # pod对象的名称 - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name
四、具体实现
1、创建发现规则
这里以我们收集pod为例,我们定义的 Prometheus 的配置如下:prometheus-additional.yaml:
- job_name: 'dev-kubernetes-endpoints' scrape_interval: 10s scrape_timeout: 10s metrics_path: (.*)/actuator/prometheus scheme: http relabel_configs: - action: keep regex: true source_labels: - __meta_kubernetes_pod_annotation_prometheus_io_scrape - action: replace regex: (.+) source_labels: - __meta_kubernetes_pod_annotation_prometheus_io_path target_label: __metrics_path__ - action: replace regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 source_labels: - __address__ - __meta_kubernetes_pod_annotation_prometheus_io_port target_label: __address__ - action: labelmap regex: __meta_kubernetes_pod_label_(.+) - action: replace source_labels: - __meta_kubernetes_namespace target_label: kubernetes_namespace - action: replace source_labels: - __meta_kubernetes_pod_name target_label: kubernetes_pod_name kubernetes_sd_configs:
- role: pod
kubeconfig_file: ""
follow_redirects: true
namespaces:
names: []
2、创建Secret 对象
将上面文件直接保存为 prometheus-additional.yaml,然后通过这个文件创建一个对应的 Secret 对象:
$ kubectl create secret generic additional-configs --from-file=prometheus-additional.yaml -n monitoring
secret "additional-configs" created
3、创建资源对象
然后我们需要在声明 prometheus 的资源对象文件中通过 additionalScrapeConfigs 属性添加上这个额外的配置:
「prometheus-prometheus.yaml」:
apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: labels: app.kubernetes.io/component: prometheus app.kubernetes.io/name: prometheus app.kubernetes.io/part-of: kube-prometheus app.kubernetes.io/version: 2.29.1 prometheus: k8s name: k8s namespace: monitoring spec: retention: 7d alerting: alertmanagers: - apiVersion: v2 name: alertmanager-main namespace: monitoring port: web enableFeatures: [] externalLabels: {} image: quay.io/prometheus/prometheus:v2.29.1 nodeSelector: kubernetes.io/os: linux podMetadata: labels: app.kubernetes.io/component: prometheus app.kubernetes.io/name: prometheus app.kubernetes.io/part-of: kube-prometheus app.kubernetes.io/version: 2.29.1 podMonitorNamespaceSelector: {} podMonitorSelector: {} probeNamespaceSelector: {} probeSelector: {} replicas: 2 resources: requests: memory: 400Mi ruleNamespaceSelector: {} ruleSelector: {} securityContext: fsGroup: 2000 runAsNonRoot: true runAsUser: 1000 serviceAccountName: prometheus-k8s serviceMonitorNamespaceSelector: {} serviceMonitorSelector: {} version: 2.29.1 additionalScrapeConfigs: name: prometheus-additional-configs key: prometheus-additional-config.yaml
关于 additionalScrapeConfigs 属性的具体介绍,我们可以使用 kubectl explain 命令来了解详细信息:
$ kubectl explain prometheus.spec.additionalScrapeConfigs KIND: Prometheus VERSION: monitoring.coreos.com/v1 RESOURCE: additionalScrapeConfigs
添加完成后,直接更新 prometheus 这个 CRD 资源对象即可:
kubectl apply -f prometheus-prometheus.yaml
过一段时间,刷新 promethues 上的 config,将会查看配置已经生效。
自动发现规则配置好后如何让prometheus抓取pod内的metrics指标呢,抓取的路径端口等信息如何指定呢,这就要在應用deployments中的spec.template.metadata.annotations中指定了。配置如下:
annotations: prometheus.io/path: /actuator/prometheus prometheus.io/port: "7070" prometheus.io/scheme: http prometheus.io/scrape: "true"
定义好后prometheus即可抓取pod内的metrics指标数据了,在prometheus的targets页面即可看到job名称为 dev-kubernetes-endpoints 的target。
4、创建 RBAC 权限
我们切换到 targets 页面下面却并没有发现对应的监控任务,查看 Prometheus 的 Pod 日志,发现很多错误日志出现,都是 xxx is forbidden,这说明是 RBAC 权限的问题。
通过 prometheus 资源对象的配置可以知道 Prometheus 绑定了一个名为 prometheus-k8s 的 ServiceAccount 对象,而这个对象绑定的是一个名为 prometheus-k8s 的 ClusterRole:
创建 prometheus-clusterRole.yaml:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus-k8s rules: - apiGroups: - "" resources: - nodes/metrics verbs: - get - nonResourceURLs: - /metrics verbs: - get
上面的权限规则中我们可以看到明显没有对 Service 或者 Pod 的 list 权限,所以报错了,要解决这个问题,我们只需要添加上需要的权限即可:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app.kubernetes.io/component: prometheus app.kubernetes.io/name: prometheus app.kubernetes.io/part-of: kube-prometheus app.kubernetes.io/version: 2.29.1 name: prometheus-k8s rules: - apiGroups: - "" resources: - nodes - services - endpoints - pods - nodes/proxy verbs: - get - list - watch - apiGroups: - "" resources: - nodes/metrics verbs: - get - nonResourceURLs: - /metrics - /actuator/prometheus verbs: - get
更新上面的 ClusterRole 这个资源对象,然后重建下 Prometheus 的所有 Pod,正常就可以看到 targets 页面下面有 dev-kubernetes-endpoints 这个监控任务了。
参考:
https://www.scriptjc.com/article/1256
https://cloud.tencent.com/developer/article/1802679
https://github.com/prometheus/prometheus/blob/release-2.23/documentation/examples/prometheus-kubernetes.yml
https://prometheus.io/docs/prometheus/latest/configuration/configuration/
https://www.cnblogs.com/pythonPath/p/12505457.html