Go语言不执行扩缩容,K8s HPA控制器负责计算副本数并更新Deployment;Go服务只需合规暴露/metrics(promhttp.Handler、小写指标名、namespace/pod标签、Histogram延迟指标),配合prometheus-adapter转换,再配对HPA YAML字段与custom.metrics API查询路径,最后结合服务内限流与优雅停机保障扩缩窗口稳定性。

Go 语言本身不执行自动扩缩容,Kubernetes 的 horizontal-pod-autoscaler 控制器才做;你用 Go 写的代码,只负责“让 HPA 能正确读到指标、能安全启停、能稳定提供服务”。漏掉任一环,HPA 就静默失效或反复震荡。
Go 服务暴露 /metrics 必须满足这四个硬条件
HPA 不直接拉你的 HTTP 接口,而是通过 prometheus-adapter 查 Prometheus,再由 adapter 把结果转成 custom.metrics.k8s.io API。你的 Go 服务只需把指标交到 Prometheus 手里,但必须合规:
-
/metrics端点必须用promhttp.Handler()注册,路径严格为/metrics(不能是/actuator/prometheus或/debug/metrics) - 指标名只能含小写字母、数字、下划线:
go_http_request_duration_seconds_p95✅,go-http-req-p95❌ - 关键标签必须带
namespace和pod,例如:http_requests_total{namespace="default",pod="myapp-7f8d4b9c-xvq9t"};否则prometheus-adapter无法按 Pod 维度聚合 - 延迟类指标必须是
Histogram类型,并用prometheus.NewHistogramVec()构建;HPA 不支持在客户端算 P95,得靠 Prometheus 的histogram_quantile(0.95, ...)在服务端聚合
HPA YAML 里 metrics 字段写错就等于没写
填了 customMetrics 却不起作用?大概率是字段嵌套或类型不匹配。autoscaling/v2 要求结构非常严格:
-
scaleTargetRef.name必须和 Deployment 的metadata.name完全一致(区分大小写),不能填 Service 名或 Pod 名 - 若用 Pod 级别指标,
metrics[].type填Pods,然后metrics[].pods.metric.name填你在 adapter rules 里定义的name(不是原始指标名) -
target.type是AverageValue时,target.averageValue必须是带单位的字符串,如"100m"(表示每秒 0.1 次请求),不能写100或"100" - 若用
type: Object(比如按 Deployment 总量扩缩),必须显式写describedObject.apiVersion和describedObject.kind,且metric.selector得匹配 adapter rules 中定义的resources映射
为什么 kubectl get hpa 显示 Unknown 或 no metrics
这不是 Go 服务的问题,而是整个指标链路断在中间。先跳过 Go,直查底层:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 运行
kubectl get apiservice v1beta2.custom.metrics.k8s.io,看AVAILABLE是否为True;不是就查prometheus-adapterPod 日志,常见原因是 TLS 证书校验失败或 RBAC 缺失system:auth-delegator - 验证指标是否真能查到:
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/default/pods/*/go_http_request_duration_seconds_p95";返回空数组说明 adapter 的seriesQuery没匹配上你的指标,或 Prometheus 里压根没抓到数据 - 别用
kubectl top pods测试自定义指标——它只走metrics.k8s.io(CPU/内存),跟你的指标无关 - 如果
describe hpa出现FailedGetCustomMetric,重点检查 adapter 的rules[].resources.overrides:想按 Pod 扩缩,就得写pod: {resource: "pod"},写成"pods"或漏掉这一层,HPA 就找不到目标
Go 服务内部不预热、不限流,HPA 扩再快也没用
从 HPA 触发扩容,到新 Pod 进入 Ready 状态,通常要 30–90 秒。这期间所有流量仍打在老 Pod 上,极易击穿。
-
readinessProbe别查外部依赖(如 Redis、第三方 API),只查本地状态(DB 连接池db.PingContext()可以,但得设短超时) - 收到
SIGTERM后,立刻调srv.SetKeepAlivesEnabled(false)拒绝新连接,再用带 timeout 的context调srv.Shutdown();terminationGracePeriodSeconds必须 ≥ 这个 timeout - 在流量入口加简单限流(如
golang.org/x/time/rate.Limiter),阈值略高于单实例常态承载能力,扛住扩缩窗口期的尖峰 - 别依赖 HPA 做“突发兜底”——它本质是稳态调节器;瞬时毛刺得靠服务内限流 + 队列缓冲来消化
最常被忽略的一点:HPA 的决策依据是过去 3 分钟的指标滑动平均,而你的 Go 服务打点频率、Prometheus 抓取间隔、adapter 查询时间窗口三者必须对齐;差 10 秒,就可能错过峰值或误判抖动。

















