
Go服务如何暴露标准Prometheus指标供K8s采集
不暴露正确格式的/metrics端点,HPA永远看不到你的CPU或自定义指标。Go服务必须用promhttp.Handler()暴露符合Prometheus文本格式的指标,且路径必须是/metrics(K8s默认抓取路径),不能是/actuator/prometheus或/debug/metrics。
常见错误:用expvar直接暴露、手写HTTP响应体、或用非标准标签名(如把service_name写成svc)——这些都会导致metric relabeling失败或指标被忽略。
- 必须注册
promhttp.Handler()到/metrics,不要加中间件拦截(如JWT校验) - 自定义指标建议用
prometheus.NewGaugeVec()而非NewCounterVec(),HPA只支持Gauge和Histogram的value(Counter无法做瞬时值比较) - 指标名称必须符合Kubernetes要求:小写字母、数字、下划线,不能含连字符(
my-service-reqs❌ →my_service_reqs✅) - 如果用
client_golangv1.16+,注意Register()可能panic,需用MustRegister()或显式错误处理
K8s HPA怎么读取Go服务的自定义指标(比如请求延迟P95)
HPA本身不直接解析Prometheus,它依赖metrics-server扩展——但metrics-server只提供CPU/MEM;要读自定义指标,必须部署prometheus-adapter,并配置好rules将Prometheus中的http_request_duration_seconds_bucket转换为HPA能识别的Service或Pod级别指标。
典型翻车点:规则里写seriesQuery漏了{job="my-go-service"},结果Adapter查不到任何时间序列;或者resources配成name: namespace,而实际想按Pod伸缩。
立即学习“go语言免费学习笔记(深入)”;
-
prometheus-adapter的rules中,metricName就是HPA里metric.name的值(如go_http_request_duration_seconds_p95) - 延迟类指标必须用
histogram_quantile(0.95, ...)在PromQL里算好,HPA不支持客户端聚合 - 如果Go服务用
prometheus.DefaultRegisterer,确保prometheus-adapter的scrape config与你的Prometheus一致(job name、instance label等) - 验证方式:调用
curl http://<adapter-url>/apis/custom.metrics.k8s.io/v1beta2/namespaces/default/services/my-go-svc/go_http_request_duration_seconds_p95</adapter-url>,应返回数值
HPA触发伸缩前,Go服务内部要不要做限流或预热
要。HPA从指标异常到新Pod Ready通常要30–90秒,这期间未处理的请求会打垮存量实例。Go服务不能只等K8s伸缩,得自己扛住突发流量峰值。
常见错觉:“HPA够快,我代码不用管”。现实是:Pod启动后Goroutine调度、DB连接池建立、缓存预热都耗时,新实例在Ready前可能持续超时。
- 用
golang.org/x/time/rate.Limiter在HTTP handler入口做请求速率限制,避免雪崩 - 就绪探针(
readinessProbe)必须检查真实依赖(如ping DB、check Redis),不能只返回200 - 启动时用
sync.Once加载热点配置或初始化连接池,别等到第一个请求才做 - 若用
gorilla/mux或chi,别在middleware里做重试——重试会放大流量,让HPA误判
为什么HPA有时不缩容,即使Go服务CPU已回落到30%
不是HPA坏了,是它的缩容有保守策略:默认scaleDownStabilizationWindowSeconds=300(5分钟),且只参考最近5分钟内的最低值。如果你的Go服务每4分钟来一波毛刺,HPA永远等不到“连续5分钟低负载”。
更隐蔽的问题:Go的runtime.ReadMemStats()显示的Alloc可能长期不回收,导致memory指标虚高;而HPA memory target设的是requests的百分比,不是limit——如果request设得太小,一点GC波动就触发扩容,缩容却卡在stabilization window里。
- 检查
kubectl describe hpa里的LastScaleTime和Conditions,确认是否卡在ScaleDownStabilized - 降低
scaleDownStabilizationWindowSeconds到120秒(最小支持值),但别低于60秒,否则抖动频繁 - Go服务内存指标优先用
process_resident_memory_bytes而非go_memstats_alloc_bytes,前者更接近OS视角 - 缩容阈值建议设为target的60%以下(如target 70%,则低于42%才考虑缩),留出缓冲余量
真正难的从来不是写个http.Handle("/metrics", promhttp.Handler()),而是搞清Prometheus Adapter规则里seriesQuery和resources字段怎么对应到你的label结构,以及HPA的stabilization window和Go GC周期怎么错开。这两处一错,指标就断,伸缩就飘。










