k8s.io/metrics 官方包不提供 custom metrics adapter 实现,因它仅含 client 和 metrics-server 内部逻辑,未暴露 getmetricbyselector 等 grpc 接口;hpa 依赖 custom.metrics.k8s.io apiservice 下的 grpc 服务而非 http 接口,故需自行实现符合规范的 grpc server。

为什么 k8s.io/metrics 官方包不能直接支持自定义指标 HPA
Go 语言里没有现成的、开箱即用的 Custom Metrics Adapter 实现,因为 Kubernetes 的 custom.metrics.k8s.io API 要求你实现一个符合特定 gRPC 接口规范的服务器,而官方 k8s.io/metrics 只提供 client 和 metrics-server 内部逻辑,不暴露 adapter 所需的 GetMetricBySelector 等方法。
常见错误现象是:写了个 Go HTTP server 暴露 /metrics,然后以为 HPA 能自动识别——结果 kubectl get hpa 显示 Unknown 或报错 failed to get metric。
- HPA controller 不拉取你的 HTTP 接口,它只调用你注册在 APIService 下的 gRPC 服务
- 你必须实现
custom.metrics.k8s.io/v1beta2的完整 gRPC 接口(不是 REST) - Kubernetes v1.26+ 已弃用 v1beta2,但目前主流 adapter(包括 prometheus-adapter)仍用它;v1beta3 尚未被广泛支持
怎么用 Go 写一个最小可用的 Custom Metrics Adapter
核心是启动一个 gRPC server,注册 CustomMetricsProviderServer,并对接你的指标源(比如 Prometheus、StatsD 或本地 expvar)。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
github.com/prometheus/client_golang/prometheus拉取指标,别自己写 HTTP client 去轮询 —— 它自带重试和缓存 - 不要硬编码
http://prometheus:9090,从环境变量读取,比如PROMETHEUS_URL - 对每个
MetricIdentifier,你要解析selector标签(如app=order-service),再拼 Prometheus query,例如:sum(rate(http_request_duration_seconds_count{job="order-service"}[2m])) - 返回值单位必须匹配 HPA 配置:CPU 是
m(毫核),内存是Mi,自定义指标可以是任意字符串(如requests_per_second),但要在 CRD 里声明
示例片段(关键逻辑):
func (s *server) GetMetricBySelector(ctx context.Context, req *custom_metrics.MetricRequest) (*custom_metrics.MetricValueList, error) {
metricName := req.MetricName
selector := labels.ConvertSelectorToLabelsMap(req.Selector)
query := fmt.Sprintf(`sum(rate(%s{app="%s"}[2m]))`, metricName, selector["app"])
result, err := s.promAPI.Query(ctx, query, time.Now())
// ... 处理 result.Vector() 提取 value
return &custom_metrics.MetricValueList{
Items: []custom_metrics.MetricValue{{
DescribedObject: req.DescribedObject,
MetricName: metricName,
Timestamp: metav1.Time{Time: time.Now()},
Value: *resource.NewMilliQuantity(int64(v*1000), resource.DecimalSI),
}},
}, nil
}
部署时最常踩的三个坑
Adapter 跑起来不等于 HPA 能用,Kubernetes 控制面有多个校验点。
-
APIService对象没创建或状态为False:检查kubectl get apiservice v1beta2.custom.metrics.k8s.io -o wide,Conditions里是否显示Available=True;常见原因是 TLS 证书不匹配或 service port 错误 - HPA 引用指标名写错:必须和 adapter 返回的
MetricName完全一致,且大小写敏感;如果 adapter 返回http_requests_total,HPA 里写httpRequestsTotal就会失败 - RBAC 权限不足:adapter 的 ServiceAccount 至少需要
get和listnamespaces、services、endpoints,否则无法解析DescribedObject中的 namespace/service 名
验证命令:
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/default/services/order-service/http_requests_total" | jq .
要不要自己写?先看 prometheus-adapter 是否够用
90% 的 Go 微服务场景,不需要从零写 adapter —— prometheus-adapter 是用 Go 写的、社区维护、支持 relabel、支持多租户、可配置 query 模板,而且它本身就能对接你的 Prometheus。
只有当你遇到这些情况才值得自己写:
- 指标源不是 Prometheus(比如直接读 Kafka offset、Redis key count、或进程内 expvar)
- 需要动态计算指标(如“过去 5 分钟错误率 > 5%”这种布尔型指标,HPA 要求数值型,得转成 0/1)
- 对延迟极其敏感,不能接受 prometheus-adapter 的两级缓存(Prometheus → adapter → HPA)
注意:prometheus-adapter 的 rules 配置里,seriesQuery 和 resources 字段容易配错,导致 HPA 找不到目标 Pod;务必用 kubectl get --raw 直接测 endpoint 返回是否含预期 items。










