我想在自己的指标服务器上使用 HorizontalPodAutoscaler!
首先
当你想在除了资源指标API以外的情况下使用水平Pod自动缩放器(HPA),例如当你想使用Prometheus的指标时,你可能会考虑使用Prometheus适配器或Kube Metrics适配器。
如果没有针对要使用的数据源的自定义/外部度量服务器,例如当你想要使用内部自有监控基础设施的度量进行HPA时,你应该怎么做呢?
为了能够在出现这种情况时说出“没问题,我可以创建一个”,我创建了一个度量服务器。
“个性化/外部度量服务器是什么?”
我建议您参考Ladicleさん的《利用自有度量指标进行Pod的水平扩展》一文,其中详细解释了这一点。我在这里简要说明一下。
-
- カスタムメトリクスサーバ: Kubernetes APIリソースに紐づくリソースのメトリクスを提供する
- 外部メトリクスサーバ: Kubernetes APIリソースに紐づかないメトリクスを提供する
有些实现了这两个选项,有些只实现了其中一个。
因为自定义度量服务器和Kubernetes对象的关联有点复杂,所以这次我们实现了外部的度量服务器来替代。
关于已实施的外部度量服务器
这个成果物在这个存储库里。
这次我们制作了一些类似 Prometheus Adapter 或 Kube Metrics Adapter 的东西。当简单配置的外部度量被从 Kubernetes 调用时,它是一个外部度量服务器,会返回与其相关的 PromQL 结果。我们只是试着制作了一下,所以它做得相当粗糙,但总体上是可运行的。
将以下的配置用作配置文件来使用。
# 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资源的关联也不像我想象的那么麻烦,所以也许可以在那里实现它。现在,即使被告知要为公司内部的监控基础设施创建度量服务器,我也能够自信地说:“没问题,我能做到”。我打算在年末年始期间以此为基础,在家里安静地看电视并实现我的个人度量服务器。