Prometheus自定义监控指标适配器

    下文使用Prometheus 自定义监控指标适配器 v0.5.0。这是一个示例。只有集群所有者才可以执行以下步骤。

    1. 获取集群监控使用的 service account,它应该已经配置了这个 workload ID:。如果您没有自定义任何选项,service account 的名字应该是cluster-monitoring

    2. 授予 service account 需要的两个权限。

      一个角色是kube-system中的extension-apiserver-authentication-reader,您需要在kube-system创建一个Rolebinding。这个权限的作用是从kube-system的 config map 获取 api 集合器配置。

      Copy

      另一个角色是集群角色system:auth-delegator,您需要创建一个ClusterRoleBinding。这个权限的作用是允许代理身份认证和鉴权,以实现统一的身份认证和鉴权。

      1. apiVersion: rbac.authorization.k8s.io/v1
      2. kind: ClusterRoleBinding
      3. metadata:
      4. name: custom-metrics:system:auth-delegator
      5. roleRef:
      6. apiGroup: rbac.authorization.k8s.io
      7. kind: ClusterRole
      8. name: system:auth-delegator
      9. subjects:
      10. - kind: ServiceAccount
      11. name: cluster-monitoring
      12. namespace: cattle-prometheus

      Copy

    3. 创建自定义参数适配器的配置文件,以下代码是一个配置文件的示例。下一节会详细讲述如何完成该配置文件。

      1. apiVersion: v1
      2. kind: ConfigMap
      3. metadata:
      4. name: adapter-config
      5. namespace: cattle-prometheus
      6. data:
      7. config.yaml: |
      8. rules:
      9. - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
      10. seriesFilters: []
      11. resources:
      12. overrides:
      13. namespace:
      14. resource: namespace
      15. pod_name:
      16. resource: pod
      17. name:
      18. matches: ^container_(.*)_seconds_total$
      19. as: ""
      20. metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[1m])) by (<<.GroupBy>>)
      21. - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
      22. seriesFilters:
      23. - isNot: ^container_.*_seconds_total$
      24. resources:
      25. overrides:
      26. namespace:
      27. resource: namespace
      28. pod_name:
      29. resource: pod
      30. name:
      31. matches: ^container_(.*)_total$
      32. as: ""
      33. metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[1m])) by (<<.GroupBy>>)
      34. - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod_name!=""}'
      35. seriesFilters:
      36. - isNot: ^container_.*_total$
      37. resources:
      38. overrides:
      39. namespace:
      40. resource: namespace
      41. pod_name:
      42. resource: pod
      43. name:
      44. as: ""
      45. metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}) by (<<.GroupBy>>)
      46. - seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
      47. seriesFilters:
      48. - isNot: .*_total$
      49. resources:
      50. template: <<.Resource>>
      51. name:
      52. matches: ""
      53. as: ""
      54. metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)
      55. - seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
      56. seriesFilters:
      57. - isNot: .*_seconds_total
      58. resources:
      59. template: <<.Resource>>
      60. name:
      61. matches: ^(.*)_total$
      62. as: ""
      63. metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
      64. - seriesQuery: '{namespace!="",__name__!~"^container_.*"}'
      65. seriesFilters: []
      66. resources:
      67. template: <<.Resource>>
      68. name:
      69. matches: ^(.*)_seconds_total$
      70. as: ""
      71. metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
      72. resourceRules:
      73. cpu:
      74. containerQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
      75. nodeQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[1m])) by (<<.GroupBy>>)
      76. resources:
      77. overrides:
      78. instance:
      79. resource: node
      80. namespace:
      81. resource: namespace
      82. pod_name:
      83. resource: pod
      84. containerLabel: container_name
      85. memory:
      86. containerQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)
      87. nodeQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>)
      88. resources:
      89. overrides:
      90. instance:
      91. resource: node
      92. namespace:
      93. resource: namespace
      94. pod_name:
      95. resource: pod
      96. containerLabel: container_name
      97. window: 1m

      Copy

    4. 为您的 api server 创建 HTTPS TLS 证书。

      1. openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out serving.crt -keyout serving.key -subj "/C=CN/CN=custom-metrics-apiserver.cattle-prometheus.svc.cluster.local"
      2. # And you will find serving.crt and serving.key in your path. And then you are going to create a secret in cattle-prometheus namespace.
      3. kubectl create secret generic -n cattle-prometheus cm-adapter-serving-certs --from-file=serving.key=./serving.key --from-file=serving.crt=./serving.crt

      Copy

    5. 然后您可以创建 Prometheus 自定义监控指标适配器。部署前需要以导入 YAML 的方式创建一个服务。请在cattle-prometheus这个命名空间内创建以下资源。

      以下是 Prometheus 自定义监控指标适配器的部署示例。

      Copy

      以下是服务的部署示例。

      1. apiVersion: v1
      2. kind: Service
      3. metadata:
      4. name: custom-metrics-apiserver
      5. namespace: cattle-prometheus
      6. spec:
      7. ports:
      8. - port: 443
      9. targetPort: 6443
      10. selector:
      11. app: custom-metrics-apiserver

      Copy

    6. 为您的自定义参数 server 创建 API service。

      1. apiVersion: apiregistration.k8s.io/v1beta1
      2. kind: APIService
      3. metadata:
      4. name: v1beta1.custom.metrics.k8s.io
      5. spec:
      6. service:
      7. name: custom-metrics-apiserver
      8. namespace: cattle-prometheus
      9. version: v1beta1
      10. insecureSkipTLSVerify: true
      11. groupPriorityMinimum: 100
      12. versionPriority: 100

      Copy

    7. 在命令行界面输入kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1命令,校验自定义参数 server 是否已经配置成功。如果从 api 返回了参数,则表示配置成功。

      1. kind: HorizontalPodAutoscaler
      2. apiVersion: autoscaling/v2beta1
      3. metadata:
      4. name: nginx
      5. spec:
      6. scaleTargetRef:
      7. # point the HPA at the nginx deployment you just created
      8. apiVersion: apps/v1
      9. kind: Deployment
      10. name: nginx
      11. # autoscale between 1 and 10 replicas
      12. minReplicas: 1
      13. maxReplicas: 10
      14. metrics:
      15. # use a "Pods" metric, which takes the average of the
      16. # given metric across all pods controlled by the autoscaling target
      17. - type: Pods
      18. pods:
      19. metricName: memory_usage_bytes
      20. targetAverageValue: 5000000

      Copy

      然后,您的 NGINX 规模会变大,表示使用自定义参数创建的 HPA 开始运行。

    配置 Prometheus 自定义监控指标适配器

    简而言之,每一条规则由以下四个部分组成:

    • 服务发现(Discovery):告诉适配器如何找到这条规则涉及的所有参数。

    • 关联(Association):说明了参数与 Kubernetes 资源之间的关系,如参数 A 代表的是某个 Kubernetes 资源。

    • 名称(Naming):说明了适配器如何在自定义参数 API 中将特定的参数暴露出去。

    • 查询(Querying):说明了如何将查询 Kubernetes 参数的转换为 Prometheus 的查询语句。

    您可以参考下方的代码示例,查看如何配置只有一条规则的配置文件:

    Copy

    服务发现(Discovery)指定需要处理的 Prometheus 的参数,通过seriesQuery挑选需要处理的参数集合,通过seriesFilters精确过滤参数。seriesQuery用于挑选需要处理的参数集合。Prometheus 参数适配器会使用该机器的标签信息,后续还会用到 “metric-name-label-names”。

    seriesFilters用于精确过滤参数。在大部分情况下,过滤参数只需要用到seriesQuery。但是当两条规则的关系不为互斥时,需要同时使用seriesFiltersseriesQuery,以达到精确过滤的目的。首先通过seriesQuery查询,然后通过seriesFilters过滤返回信息。

    seriesFilters提供了以下两个过滤方式:

    • is: <regex>,返回名称和<regex>中的字符串匹配的 series。

    • isNot: <regex>,返回名称和<regex>中的字符串不匹配的 series。

    例如:

    1. # match all cAdvisor metrics that aren't measured in seconds
    2. seriesQuery: '{__name__=~"^container_.*_total",container_name!="POD",namespace!="",pod_name!=""}'
    3. seriesFilters:
    4. isNot: "^container_.*_seconds_total"

    Copy

    关联负责的是设置 metric 与 kubernetes resources 的映射关系,resources控制这个过程。

    有两种关联 Kubernetes 资源和参数的方式。在这两种方式中,标签(label)的值都会变成某个对象。

    1. # any label `kube_<group>_<resource>` becomes <group>.<resource> in Kubernetes
    2. resources:
    3. template: "kube_<<.Group>>_<<.Resource>>"

    Copy

    另一种方式是使用overrides指定匹配某些样式的名称,一个overrides可以指定一种 Prometheus label 和 Kubernetes 资源的映射关系,例如:

    1. # the microservice label corresponds to the apps.deployment resource
    2. resource:
    3. overrides:
    4. microservice: { group: "apps", resource: "deployment" }

    Copy

    上述两种方法没有互斥性,您可以在一条规则中同时使用这两种方法。使用第一种方法的目的是创建一个模板,使用第二种方式的目的是针对某些资源中的可能存在特例,可以使用该方式处理这些特例。

    只要您有对应的标签,resource 可以指代 Kubernetes 集群中的任意资源。

    命名(Naming)用于将 prometheus metrics 名称转化为 custom metrics API 所使用的 metrics 名称,但不会改变其本身的 metric 名称,name控制这个过程。

    命名是通过指定参数名称的模板,从 Prometheus name 提取 API name,然后将 name 转换为您指定的 name。

    该名称的模板通过 matches指定,是一个正则表达式。如果没有指定特定的值,默认值为.*

    名称的转换通过 as 指定。您可以使用在matches提到任何捕获组(capture group)。如果matches没有捕获组,as的默认值为$0。如果matches有 1 个捕获组,as的默认值为$1。如果matches有 2 个或更多的捕获组,您必须指定as的值。

    例如:

    Copy

    处理调用 custom metrics API 获取到的 metrics 的 value,该值最终提供给 HPA 进行扩缩容,通过 metricsQuery指定。

    metricsQuery是一个 GO 模板,它会转化为一个 Prometheus 查询语句,使用 custom metrics API 调用的某个特定 call。一个给定的 call 会被分解为 metric name、组资源和一个或多个组资源内的对象。这些参数会被转换成以下格式:

    • Series: metric name,参数名称
    • LabelMatchers: 以逗号分割的 objects,当前表示特定 group-resource 加上命名空间的 label(如果该 group-resource 是 namespaced 的)
    • GroupBy: 以逗号分割的 label 的集合,当前表示 LabelMatchers 中的 group-resource label。当前GroupBy包含LabelMatchers内的所有组资源 label。

    假设我们有一个 series,http_requests_total (以http_requests_per_second 在 API 中暴露),有servicepodingressnamespaceverb这几个 label。前四个 label 有对应的 Kubernetes 资源。如果有人请求了pod1pod2的参数pods/http_request_per_second,我们会有如下的代码:

    • Series: "http_requests_total"
    • LabelMatchers: "pod=~\"pod1|pod2",namespace="somens"
    • GroupBy: pod

    除了以上三个域之外,还有两个域

    • LabelValuesByNameLabelMatchers 中 label 和 value 的映射。使用|预连接(Prometheus 使用的是=~)。

    • GroupBySliceGroupBy的一个子集。

    大多数情况下,您只会用到 SeriesLabelMatchers、和GroupBy,其他两个是高级选项。

    这个查询语句应该为每个请求对象返回一个值。适配器会使用返回 series 的 labels 关联对应的对象。

    1. # convert cumulative cAdvisor metrics into rates calculated over 2 minutes
    2. metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"

    Copy