k8s 监控(三)prometheus-adapter

发布时间 2023-09-27 16:23:03作者: whtjyt

原文链接: https://juejin.cn/post/6844903967218991117

kubernetes apiserver 提供了两种 api 用于监控指标相关的操作:

kubernetes apiserver 用于将 kubernetes 的功能通过 restapi 的方式暴露出去,给其他组件使用,但是它提供的都是核心相关功能。有些功能有用,但是非核心,apiserver 又得提供这样的功能咋办?

考虑到这样的情况,kubernetes apiserver 提供对应的 api,但是对于达到这个 api 的请求,它并不处理,而是转发到一个扩展 apiserver 上。有意思的是,这个扩展 apiserver 只要遵循规范,任何人都可以开发。

使用者在使用扩展 apiserver 时,只需要将其注册到 kube-aggregator(kubernetes apiserver 的功能),aggregator 就会将对于这个 api 的请求转发到这个扩展 apiserver 上。当然 kubernetes apiserver 和扩展 apiserver 之间的交互会涉及到非常多的细节,这里就不多提。

resource metrics API 和 custom metrics API 就是这样的例子,kubernetes apiserver 提供了这两个 api,但是具体的实现它就不管了。

而这篇文章的目的就是通过 Prometheus adapter 实现它们。

api group 和 api version

在实现这两个 api 之前,我们先来聊聊 api 组和 api 版本。

所谓的 api group 就是你执行 kubectl api-versions 出现的值,这些值由 api group 和 api version 构成。

 
复制代码
# kubectl api-versions
admissionregistration.k8s.io/v1beta1
apiextensions.k8s.io/v1beta1
apiregistration.k8s.io/v1
apiregistration.k8s.io/v1beta1
apps/v1
...

内容太多,这里只列出了 5 个。第一个 admissionregistration.k8s.io/v1beta1 中,admissionregistration.k8s.io 是 api group,v1beta1 表示它的版本。

如果 api group 为空表示核心 api,kubernetes 的资源都是由 api group 提供的。那么如何知道哪些资源是由哪些 api group 提供的呢?

执行 kubectl api-resources 就能够知道了:

 
复制代码
# kubectl api-resources
NAME                              SHORTNAMES   APIGROUP                       NAMESPACED   KIND
bindings                                                                      true         Binding
componentstatuses                 cs                                          false        ComponentStatus
configmaps                        cm                                          true         ConfigMap
endpoints                         ep                                          true         Endpoints
events                            ev                                          true         Event
limitranges                       limits                                      true         LimitRange
namespaces                        ns                                          false        Namespace
nodes                             no                                          false        Node
...

内容很多,这里只列出一部分。输出结果为 5 列,NAME 列就是资源名,它的功能由 APIGROUP 列的 api group 提供。SHORTNAMES 列就是这些资源的缩写了,缩写在使用 kubectl 时非常好用。

上面列出的所有结果中,APIGROUP 都为空,表示这些资源都是核心 api 提供的。当你看到某些 role 或者 clusterRole 中 apiGroup 的值为 "" 时,就应该知道它要访问的资源都是核心 api 提供的。

对于我们要实现的两个 api:

  • resource metrics API 的 api group 是 metrics.k8s.io,版本为 v1beta1
  • custom metrics API 的 api group 是 custom.metrics.k8s.io,版本为 v1beta1

它们的 api group 和 api version 会在后面注册时用到。

custom metrics API

先说这个 custom metrics API,resource metrics API 放在后面。你如果说你只需要 resource metrics API,那你也得看这个,因为这两个 api 都由 Prometheus adapter 来实现。

custom metrics API 完全就是给 HPA v2 准备的,因为 v1 只能使用 CPU 作为 pod 横向扩展的指标,很明显无法满足使用者的需要。

custom metrics API 的实现有多个,我们之所以选择 Prometheus adapter,是因为我们已经安装了 Prometheus。而通过 Prometheus adapter,只要存在于 Prometheus 中的指标,都可以拿来做 HPA 的条件,这样就能满足所有的使用场景了。

kubernetes 1.14 中 apiserver 已经开启了 Aggregation layer,因此我们只需要安装 Prometheus adapter 就行。

我们会使用 deployment 部署 Prometheus adapter,当然会为启动它的 serviceAccount 绑定各种角色。因为它本身就是一个 apiserver,因此它会监听一个端口,对外提供 http 服务。

我们还需要为它创建一个 service,kubernetes apiserver 转发请求时,会将请求发送到 service 上,经由 service 到达后面的 Prometheus adapter。

本文所有的 yml 文件均保存在 GitHub,这里就不一一列出了。Prometheus adapter 相关的文件都在 adapter 目录下,custom metrics API 用到的文件有:

 
复制代码
prometheus-adapter-apiServiceCustomMetrics.yml
prometheus-adapter-apiServiceMetrics.yml
prometheus-adapter-clusterRoleAggregatedMetricsReader.yml
prometheus-adapter-clusterRoleBindingDelegator.yml
prometheus-adapter-clusterRoleBinding.yml
prometheus-adapter-clusterRole.yml
prometheus-adapter-configMap.yml
prometheus-adapter-deployment.yml
prometheus-adapter-roleBindingAuthReader.yml
prometheus-adapter-serviceAccount.yml
prometheus-adapter-service.yml

其中:

  • apiServiceCustomMetrics:用来提供注册 api,api 组为 custom.metrics.k8s.io,版本为 v1beta1
  • apiServiceMetrics:用来提供注册 api,api 组为 metrics.k8s.io,版本为 v1beta1。这是给 resource metrics 使用的;
  • roleBindingAuthReader:让扩展 apiserver 可以去读取 configMap 来验证 kubernetes apiserver 的身份,这个不用细究;
  • clusterRoleAggregatedMetricsReader:对 metrics.k8s.iocustom.metrics.k8s.io 下面的任意资源具有任意权限;
  • clusterRoleBindingDelegator:绑定权限,让 Prometheus adapter 有权限提交 SubjectAccessReview 请求到 kubernetes apiserver;
  • clusterRole:Prometheus adapter 需要访问集群内的资源;
  • configMap:Prometheus adapter 的配置文件,这是重点之一;
  • service:kubernetes apiserver 先将请求发送到这个 service,再由它转发到后面的 Prometheus adapter pod。

部署和测试

很多人在使用的时候会使用 kubernetes CA 签署一个证书之后用这个证书来为 Prometheus adapter 提供 https。其实不需要如此,Prometheus adapter 启动的时候,如果你没有给它提供证书,它会生成一个自签署的证书来提供 https。

至于这个证书是否受信任并不重要,因为 prometheus-adapter-apiServiceCustomMetrics.yml 文件中存在 insecureSkipTLSVerify: true 这个选项。

clone 并部署:

 
复制代码
git clone https://github.com/maxadd/k8s-prometheus
kubectl apply -f k8s-prometheus/adapter

通过下面直接访问 api 的方式来测试部署是否 ok(你可能需要略等一会儿):

 
复制代码
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | python -mjson.tool
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/pods/*/fs_usage_bytes" | python -mjson.tool

第一条命令会输出非常多的值,都是可以用作 HPA 的指标。如果你没有,那么部署存在问题,原因后面会讲。

第二条命令会输出 monitoring 名称空间下所有 pod 的 fs_usage_bytes 指标的值,如果你没有,那么部署同样存在问题。

因为我前面安装 Prometheus 的文章中对 Prometheus 中的一些指标和标签做了一些修改,因此如果你直接使用官方的配置,那么肯定会出问题的。配置相关的内容下面会提到。

另外,Prometheus adapter 启动参数中的 -v 相当于 debug 级别,值越大,输出的日志就越详细,最高貌似为 10?但是好像这个日志没啥作用。。

配置文件

Prometheus adapter 的配置文件格式如下所示(由于过长,所以截取了一部分)。它分为两个部分,第一个是 rules,用于 custom metrics;另一个是 resourceRules,用于 metrics。如果你只用 Prometheus adapter 做 HPA,那么 resourceRules 就可以省略,反之亦然。

我们从 rules 规则讲起,这个规则下面有很多的查询语句,这些查询语句的作用就是尽可能多的获取指标,从而让这些指标都可以用于 HPA。

也就是说通过 Prometheus adapter,你可以将 Prometheus 中的任何一个指标都用于 HPA,但是前提是你得通过查询语句将它拿到(包括指标名称和其对应的值)。也就是说,如果你只需要使用一个指标做 HPA,那么你完全就可以只写一条查询,而不像下面使用了好多个查询。

 
复制代码
rules:
  - seriesQuery: '{__name__=~"^container_.*",container_name!="POD",namespace!="",pod!=""}'
    seriesFilters: []
    resources:
      overrides:
        namespace:
          resource: namespace
        pod: # 官方示例中的这个值为 pod_name,由于我之前将 pod_name 改为了 pod,所以这里也为 pod
          resource: pods
    name:
      matches: ^container_(.*)_seconds_total$
      as: ""
    metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[1m])) by (<<.GroupBy>>)
---
resourceRules:
  cpu:
    containerQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)
    nodeQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[1m])) by (<<.GroupBy>>)
    resources:
      overrides:
        instance:
          resource: nodes
        namespace:
          resource: namespace
        pod:
          resource: pods
    containerLabel: container_name

接下来我们会对其关键字进行解释:

  • seriesQuery:查询 Prometheus 的语句,通过这个查询语句查询到的所有指标都可以用于 HPA;
  • seriesFilters:查询到的指标可能会存在不需要的,可以通过它过滤掉。过滤器有两种方式:
    • is: <regex>:只获取正则表达式匹配到的指标名称;
    • isNot: <regex>
  • resources:通过 seriesQuery 查询到的只是指标,我如果要查询某个 pod 的指标,肯定要将它的名称和所在的名称空间作为指标的标签进行查询,resources 就是将指标的标签和 k8s 的资源类型关联起来,最常用的就是 pod 和 namespace。有两种添加标签的方式,一种是 overrides,另一种是 template
    • overrides:它会将指标中的标签和 k8s 资源关联起来。上面示例中就是将指标中的 pod 和 namespace 标签和 k8s 中的 pods(pod 和 pods 都行,这就跟你 kubectl get pod/pods 一样)和 namespace 关联起来,因为 pod 和 namespace 都属于核心 api 组,所以不需要指定 api 组。等你查询某个 pod 的指标时,它会自动将 pod 的名称和名称空间作为标签加入到查询条件中;
      • microservice: {group: "apps", resource: "deployment"} 这么写表示将指标中 microservice 这个标签和 apps 这 api 组中 deployment 资源关联起来;
    • template:通过 go 模板的形式。
      • template: "kube_<<.Group>>_<<.Resource>>" 这么写表示,假如 <<.Group>> 为 apps,<<.Resource>> 为 deployment,那么它就是将指标中 kube_apps_deployment 标签和 deployment 资源关联起来;
  • name:用来给指标重命名的。之所以要给指标重命名是因为有些指标是只增的,比如以 total 结尾的指标。这些指标拿来做 HPA 是没有意义的,我们一般计算它的速率,以速率作为值,那么此时的名称就不能以 total 结尾了,所以要进行改名。
    • matches:通过正则表达式来匹配指标名,可以进行分组;
    • as:默认值为 $1,也就是第一个分组。as 为空就是使用默认值的意思。
  • metricsQuery:这就是查询 Prometheus 了,前面的 seriesQuery 查询是获得 HPA 指标。当我们要查某个指标的值时就要通过它指定的查询语句进行了。可以看到查询语句使用了速率和分组,这就是解决上面提到的只增指标的问题,它还用到了模板。
    • Series:指标名称;
    • LabelMatchers:附加的标签,目前只有 pod 和 namespace 两种,因此我们要在之前使用 resources 进行关联;
    • GroupBy:就是 pod 名称,同样需要使用 resources 进行关联。

前面访问 /apis/custom.metrics.k8s.io/v1beta1/ 出现的所有指标都是这些规则中 seriesQuery 查询到的,当然名称可能和 Prometheus 中不完全一样,因为这里使用了 name 进行了重命名。

其实很多指标拿来做 HPA 是没有必要的,比如说 Prometheus 自身的指标以及 k8s 组件指标等,但是 Prometheus adapter 肯定希望将所有的指标都暴露出来,让你想使用啥就使用啥,所以它的 seriesQuery 才会这么多。

访问 /apis/custom.metrics.k8s.io/v1beta1/namespaces/monitoring/pods/*/fs_usage_bytes 则是通过 metricsQuery 进行查询,从而获取每个 pod 的指标值。

部署有问题的多半就是配置文件中的关联没有做好,只有理解了这个配置文件的意思才能保证部署没有问题。

剩下的 resourceRules 规则则是用于 resource metrics,只有 cpu 和 memory 两个属性,而这两个属性又分为 node 和 pod,很容易看懂。当执行 kubectl top pods/nodes 时就会执行这两条查询语句。

关于 custom metrics 就到此为止了,HPA 的内容这里就不涉及了。

resource metrics API

resource metrics API 官方的说法是给 k8s 核心组件提供监控指标的,但是它只提供了 pod 和 node 的 CPU 和内存指标,功能实在有限。

官方给出它可以做以下工作:

  • HPA:CPU 指标可以拿来做 HPA。v1 版本的 HPA 也许依赖这个,现在已经无所谓了;
  • pod 调度:官方的意思是这是个扩展的功能,因为现在的 pod 调度根本没有考虑到 node 的使用情况;
  • 集群联邦:同样是资源使用,但是现在没有使用;
  • dashboard:出图,没用过 dashboard,也不知道是不是有效果;
  • kubectl top:这算是最实用的功能吧。

所以总结下来,resource metrics API 的最大作用就是竟然是让你可以使用 kubectl top 命令?当然咱们先不管它是否有用,我们目的之一就是部署一个扩展 apiserver 来实现它,接下来就是选一个扩展 apiserver。

很多人会使用 metrics server 提供 resource metrics API,然后使用 Prometheus adapter 提供 custom metrics API。但是其实 Prometheus adapter 完全可以支持这两种 api,因此我们完全不需要 metrics server,只部署一个 Prometheus adapter 就行。

前面我们其实已经部署好了,只需要验证就行。

 
复制代码
# kubectl -n monitoring top pods
NAME                                  CPU(cores)   MEMORY(bytes)
alertmanager-c8d754fbc-2slzr          1m           15Mi
grafana-74bf6c49f6-lf7vw              7m           50Mi
kube-state-metrics-856448748c-xtdxl   0m           24Mi
prometheus-adapter-548c9b9c4c-mr9zq   0m           39Mi

但是执行 kubectl top node 会出现问题,因为我将 id='/' 的指标都删掉了,如果不想将这些指标恢复,可以看我下一篇收集宿主机指标的文章。

这篇文章就到这了,感谢阅读,谢谢!