我想在自己的指标服务器上使用 HorizontalPodAutoscaler!

首先

当你想在除了资源指标API以外的情况下使用水平Pod自动缩放器(HPA),例如当你想使用Prometheus的指标时,你可能会考虑使用Prometheus适配器或Kube Metrics适配器。

如果没有针对要使用的数据源的自定义/外部度量服务器,例如当你想要使用内部自有监控基础设施的度量进行HPA时,你应该怎么做呢?
为了能够在出现这种情况时说出“没问题,我可以创建一个”,我创建了一个度量服务器。

“个性化/外部度量服务器是什么?”

我建议您参考Ladicleさん的《利用自有度量指标进行Pod的水平扩展》一文,其中详细解释了这一点。我在这里简要说明一下。

    • カスタムメトリクスサーバ: Kubernetes APIリソースに紐づくリソースのメトリクスを提供する

 

    外部メトリクスサーバ: Kubernetes APIリソースに紐づかないメトリクスを提供する

有些实现了这两个选项,有些只实现了其中一个。
因为自定义度量服务器和Kubernetes对象的关联有点复杂,所以这次我们实现了外部的度量服务器来替代。

关于已实施的外部度量服务器

这个成果物在这个存储库里。
这次我们制作了一些类似 Prometheus Adapter 或 Kube Metrics Adapter 的东西。当简单配置的外部度量被从 Kubernetes 调用时,它是一个外部度量服务器,会返回与其相关的 PromQL 结果。我们只是试着制作了一下,所以它做得相当粗糙,但总体上是可运行的。

スクリーンショット 2020-12-22 9.44.51.png

将以下的配置用作配置文件来使用。

# PrometheusのURL
url: http://prometheus.kube-system:9090
queries: 
  # External Metrics名: PromQL
  prom_up: up{component="prometheus"}
  http_requests_total: http_requests_total
  go_gc_duration_seconds_count: go_gc_duration_seconds_count

已将Prometheus的查询结果中包含的标签作为外部指标的标签来使用。因此,在PromQL阶段可以使用标签进行筛选,同时也可以在HPA的选择器中进行筛选。

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
...
spec:
  metrics:
  - type: External
    external:
      metric:
        name: http_requests_total
        # Prometheusのメトリクスに含まれるラベルで絞り込み可能
        selector:
          matchLabels:
            component: web-app
      target:
        type: AverageValue
        averageValue: 30

实施

在Custom Metrics Server中,官方已经提供了一个Boilerplate,如果使用它,可以相对容易地创建一个。你只需要实现一个接口即可。该存储库还包含了在创建度量服务器时有用的辅助函数。
样本实现在此处可见,并且有关创建自定义度量服务器的文档也已经准备好了。

如果要实现Custom Metrics Server,则需在pkg/provider中实现CustomMetricsProvider接口。

type CustomMetricsProvider interface {
    // 条件にマッチするカスタムメトリクスのリストを返す
    GetMetricByName(name types.NamespacedName, info CustomMetricInfo, metricSelector labels.Selector) (*custom_metrics.MetricValue, error)
    GetMetricBySelector(namespace string, selector labels.Selector, info CustomMetricInfo, metricSelector labels.Selector) (*custom_metrics.MetricValueList, error)

    // その時点におけるメトリクスサーバで利用可能なメトリクス情報のリストを返す
    ListAllMetrics() []CustomMetricInfo
}

如果要实现External Metrics Server,需要实现ExternalMetricsProvider。由于这次是外部度量服务器,因此我们进行了此实现。

type ExternalMetricsProvider interface {
    // 条件にマッチする外部メトリクスのリストを返す
    GetExternalMetric(namespace string, metricSelector labels.Selector, info ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error)
    // その時点におけるメトリクスサーバで利用可能なメトリクス情報のリストを返す
    ListAllExternalMetrics() []ExternalMetricInfo
}

获取外部度量值

当调用GetExternalMetric函数时,将传递命名空间(字符串)、labels.Selector和ExternalMetricInfo作为参数。
第一个参数命名空间将被传递为HPA的命名空间,可以用于访问控制和指标标识,但也可以选择不使用。
第二个参数将作为labels.Selector传递给HPA的spec.metrics.external.metric.selector。为了使用labels.Selector,我们需要将来自外部(比如Prometheus)的指标的标签和标记转换为Kubernetes的labels.Set,以便能够使用labels.Selector,所以我们采用了这种方法。

ls := labels.Set{}
// PrometheusのラベルをKubernetesのラベルへ変換
for k, v := range samples[i].Metric {
        ls[string(k)] = string(v)
}
// labels.Selectorを満たすかどうか確認
if !metricSelector.Matches(ls) {
        continue
}

返回值将是ExternalMetricValueList对象。它是一个指标列表对象,顾名思义。每个指标都包含时间戳、指标名称、标签和指标值等信息。这些指标的总和将被计算为HPA的结果值,下面是Kubernetes端的相关代码。

// https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/controller/podautoscaler/replica_calculator.go#L330-L337
// or
// https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/controller/podautoscaler/replica_calculator.go#L352-L359

metrics, timestamp, err := c.metricsClient.GetExternalMetric(metricName, namespace, metricLabelSelector)
if err != nil {
    return 0, 0, time.Time{}, fmt.Errorf("unable to get external metric %s/%s/%+v: %s", namespace, metricName, metricSelector, err)
}
utilization = 0
for _, val := range metrics {
    utilization = utilization + val
}

第三个参数的ExternalMetricInfo仅包含指标名称的信息。在我们创建的指标服务器中,我们使用这些信息来获取相关的PromQL。

type ExternalMetricInfo struct {
    Metric string
}

列出所有外部指标

ListAllExternalMetrics需要返回可用作外部指标的指标名称列表。实现方法是将在配置中指定的指标名称简单地返回为ExternalMetricInfo的列表。

供应商的注册

只需要将上述实现的Provider传递给AdapterBase函数WithExternalMetrics,并运行它,就可以作为一个很好的自定义API服务器启动并注册到API Aggregation Layer的APIService中。
(根据名为AdapterBase的命名,我认为它是为了这种写法而设计的,但为了简单起见,本例中采用了以下方法)
下面的代码被省略了一些解释。

func main() {
        cmd := basecmd.AdapterBase{}

        provider, err := NewPrometheusProvider(config)
        if err != nil {
                klog.Fatal(err)
        }
        cmd.WithExternalMetrics(provider)

        klog.Infof("starting sample metrics server")
        if err := cmd.Run(wait.NeverStop); err != nil {
                klog.Fatalf("unable to run custom metrics adapter: %v", err)
        }
}

并且给予适当的Role/ClusterRole以使其运行,并注册为APIService,则它将正常工作。
由于没有仔细研究作为度量服务器需要什么权限,所以我只是对示例实现稍作修改,用于外部度量。然后进行了部署。
如果部署成功并且配置正确,那么它应该可以作为常规的外部度量供HPA使用。

最后

比我想的要简单得多,我成功地自己制作了度量服务器。根据文档来看,自定义度量服务器与所需的Kubernetes API资源的关联也不像我想象的那么麻烦,所以也许可以在那里实现它。现在,即使被告知要为公司内部的监控基础设施创建度量服务器,我也能够自信地说:“没问题,我能做到”。我打算在年末年始期间以此为基础,在家里安静地看电视并实现我的个人度量服务器。

广告
将在 10 秒后关闭
bannerAds