环境信息

  1. kubectl version
  2. 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"}
  3. 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:

  1. prometheus.io/scrape: "true"
  2. prometheus.io/path: "/metrics"
  3. prometheus.io/port: "9121"
  4. prometheus.io/app-scrape: "true",过滤应用业务组件监控类型

如何给应用加一些自定义标记信息

可能还需要根据平台和业务的需求添加其他一些以prometheus.io/app-label-为前缀的annotation,Prometheus截取下前缀,保留后半部分做key,连同value保留下来。这样满足在平台对应用做其他一些标识的需求。比如加入如下annotation来标识应用所属的的环境、租户以及应用名称:

  1. prometheus.io/app-label-env: "dev"
  2. prometheus.io/app-label-tenant: "hzero"
  3. prometheus.io/app-label-name: "redis-app"

Pod准备

  1. mkdir -p /u01/repo/exporter
  2. cd /u01/repo/exporter
  3. vim prom-redis.yaml
  4. kubectl apply -f prom-redis.yaml
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: redis
  5. namespace: kube-system
  6. labels:
  7. app: redis
  8. spec:
  9. selector:
  10. matchLabels:
  11. app: redis
  12. template:
  13. metadata:
  14. annotations:
  15. prometheus.io/scrape: "true"
  16. prometheus.io/path: "/metrics"
  17. prometheus.io/port: "9121"
  18. prometheus.io/app-scrape: "true"
  19. prometheus.io/app-label-env: "dev"
  20. prometheus.io/app-label-tenant: "hzero"
  21. prometheus.io/app-label-name: "redis-app"
  22. labels:
  23. app: redis
  24. spec:
  25. containers:
  26. - name: redis
  27. image: redis:4
  28. resources:
  29. requests:
  30. cpu: 100m
  31. memory: 100Mi
  32. ports:
  33. - containerPort: 6379
  34. - name: redis-exporter
  35. image: oliver006/redis_exporter:latest
  36. resources:
  37. requests:
  38. cpu: 100m
  39. memory: 100Mi
  40. ports:
  41. - containerPort: 9121
  42. ---
  43. kind: Service
  44. apiVersion: v1
  45. metadata:
  46. name: redis
  47. namespace: kube-system
  48. spec:
  49. selector:
  50. app: redis
  51. ports:
  52. - name: redis
  53. port: 6379
  54. targetPort: 6379
  55. - name: prom
  56. port: 9121
  57. 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,也即:

  1. relabel_configs:
  2. - source_labels: [__address__, __meta_kubernetes_pod_host_ip,__meta_kubernetes_pod_annotation_prometheus_io_port]
  3. action: replace
  4. regex: ([^:]+)(?::\d+)?;([^:]+);(\d+)
  5. replacement: $2:$3
  6. 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: trueprometheus.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(.+)` | | |

最终抓取任务配置

  1. - job_name: K8S-APP-CUSTOM
  2. honor_timestamps: true
  3. scrape_interval: 1m
  4. scrape_timeout: 10s
  5. metrics_path: /metrics
  6. scheme: https
  7. file_sd_configs:
  8. - files:
  9. - /u01/prometheus/target/nodes/K8S-APP-CUSTOM_targets_hosts.json
  10. refresh_interval: 5m
  11. kubernetes_sd_configs:
  12. - api_server: https://172.23.16.106:8443
  13. role: endpoints
  14. bearer_token: <secret>
  15. tls_config:
  16. insecure_skip_verify: true
  17. namespaces:
  18. names:
  19. - default
  20. - kube-system
  21. bearer_token: <secret>
  22. tls_config:
  23. insecure_skip_verify: true
  24. relabel_configs:
  25. - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape, __meta_kubernetes_pod_annotation_prometheus_io_app_scrape,
  26. __meta_kubernetes_endpoint_port_name]
  27. separator: ;
  28. regex: true;true;prom
  29. replacement: $1
  30. action: keep
  31. - source_labels: [__address__]
  32. separator: ;
  33. regex: (.*)
  34. target_label: instance
  35. replacement: $1
  36. action: replace
  37. - separator: ;
  38. regex: (.*)
  39. target_label: __address__
  40. replacement: 172.23.16.106:8443
  41. action: replace
  42. - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_pod_name, __meta_kubernetes_pod_annotation_prometheus_io_port]
  43. separator: ;
  44. regex: ([^;]+);([^;]+);([^;]+)
  45. target_label: __metrics_path__
  46. replacement: /api/v1/namespaces/${1}/pods/http:${2}:${3}/proxy/metrics
  47. action: replace
  48. - separator: ;
  49. regex: __meta_kubernetes_pod_label_(.+)
  50. replacement: $1
  51. action: labelmap
  52. - source_labels: [__meta_kubernetes_namespace]
  53. separator: ;
  54. regex: (.*)
  55. target_label: kubernetes_namespace
  56. replacement: $1
  57. action: replace
  58. - source_labels: [__meta_kubernetes_pod_name]
  59. separator: ;
  60. regex: (.*)
  61. target_label: kubernetes_pod_name
  62. replacement: $1
  63. action: replace
  64. - separator: ;
  65. regex: __meta_kubernetes_pod_annotation_prometheus_io_app_label_(.+)
  66. replacement: $1
  67. action: labelmap
  68. - separator: ;
  69. regex: (.*)
  70. target_label: PersonNum
  71. replacement: hops
  72. action: replace
  73. - separator: ;
  74. regex: (.*)
  75. target_label: _tenant_id
  76. replacement: "0"
  77. action: replace

验证预览

image.png

注意:其中的envnametenant均是业务含义的自定义指标标签。同时仅保留了instance=10.244.46.126:9121也即redis-exporter的容器端点。

结论

此种方案适合于node_exporterredis_exporter等手工打到Pod容器当中的监控代理自动发现方式。