k8s中prometheus抓取go应用指标失败主因是服务发现链路未打通,需确保service端口命名、servicemonitor匹配、指标注册正确及client_golang版本兼容。

Go 应用要被 Prometheus 正确抓取指标,光写 http.Handle("/metrics", promhttp.Handler()) 是不够的——K8s 环境下,服务发现、端口暴露、权限配置、指标路径和标签对齐这四点任一出错,target 就会显示 DOWN。
为什么 /metrics 路径在 K8s 里经常 404 或 connection refused
根本原因不是 Go 代码没跑起来,而是 Prometheus Server 根本没找到你的 Pod 的真实 IP 和可访问端口。K8s 默认不暴露 /metrics 端点给外部,必须靠 Service + ServiceMonitor(或 PodMonitor)打通链路。
- 确保你的 Deployment 中定义了
ports,且 name 是metrics(很多 Operator 依赖这个命名):ports: - containerPort: 8080 name: metrics protocol: TCP
- Service 必须显式将该端口映射出去,哪怕只用于内部抓取:
spec: ports: - port: 8080 targetPort: metrics - 如果用 Prometheus Operator,必须部署
ServiceMonitor,且namespaceSelector和selector要匹配你的 Service 的namespace和labels;漏掉任意一个 label 键值对,Operator 就忽略它
client_golang 版本和注册方式选错会导致指标不采集
用 promauto.NewCounter 还是 prometheus.NewCounter,差别不只是写法——前者自动注册到默认注册器,后者需要手动 prometheus.MustRegister()。但在 K8s 多实例场景下,如果你用了 promauto 却又没控制好包级变量初始化顺序,可能在 handler 启动前就 panic。
- 推荐统一用
promauto.With(registry).NewCounter(...)显式传入自定义注册器,避免和默认注册器冲突 - 不要在
init()里调用MustRegister,尤其当你的指标变量跨多个文件时,导入顺序不可控 - 验证是否注册成功:启动后 curl
http://<pod-ip>:8080/metrics</pod-ip>,确认自定义指标名(如myapp_requests_total)出现在输出里,而不仅是go_*或process_*
抓取失败时看哪几个关键日志和配置项
Prometheus UI 的 Status → Targets 页面只告诉你 DOWN,但不告诉你为什么。真正要查的是三处:
- Prometheus Server 日志中是否有类似
server returned HTTP status 401 Unauthorized或connection refused—— 前者说明 RBAC 缺权限,后者说明 Service 没转发到 Pod - K8s Event:
kubectl get events -n <your-ns></your-ns>,常能看到Failed to pull image或Liveness probe failed,这些会让 Pod 反复重启,导致 scrape 瞬间失败 - 检查 Prometheus 配置中的
scrape_timeout:Go 应用若在/metricshandler 里做了重计算(比如聚合大量内存数据),超时默认是 10s,容易触发context deadline exceeded
最易被忽略的一点:Prometheus 抓取时默认带 Accept: application/openmetrics-text; version=1.0.0;q=0.5, application/json;q=0.3,而老版本 promhttp.Handler()(v1.12.0 之前)不支持 OpenMetrics 格式,会返回 406。升级到 github.com/prometheus/client_golang v1.14.0+ 并确保用 promhttp.HandlerFor(registry, promhttp.HandlerOpts{}) 才能兼容。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











