Prometheus 管理 API 完全指南:健康检查、就绪探针与配置热重载/优雅关停
【免费下载链接】prometheusThe Prometheus monitoring system and time series database.项目地址: https://gitcode.com/GitHub_Trending/pr/prometheus
本篇技术指南以 Prometheus 官方文档 管理 API 说明 为主体,系统讲解/-/healthy、/-/ready、/-/reload、/-/quit四类管理端点的用法、默认行为与启用条件,并结合 web/web.go 与 cmd/prometheus/main.go 的源码实现说明其底层原理。读完后,你将能够正确配置 Kubernetes/负载均衡器的存活与就绪探针,通过--web.enable-lifecycle开启远程重载与关停能力,并在自动化脚本中可靠地验证操作结果。
管理 API 总览与生命周期开关
Prometheus 提供了一组管理 API,用于自动化运维和集成对接。官方文档(docs/management_api.md)归纳出的端点如下:
| 端点 | 允许的 HTTP 方法 | 默认状态 | 用途 |
|---|---|---|---|
/-/healthy | GET、HEAD | 始终可用 | 健康检查,恒返回 200 |
/-/ready | GET、HEAD | 始终可用 | 就绪检查,可响应流量时返回 200 |
/-/reload | PUT、POST | 禁用,需--web.enable-lifecycle | 重新加载配置文件与规则文件 |
/-/quit | PUT、POST | 禁用,需--web.enable-lifecycle | 触发优雅关停 |
其中,/-/reload和/-/quit这两个"生命周期 API"默认是关闭的。启动参数--web.enable-lifecycle的定义见 main.go 第 470 行:
--web.enable-lifecycle: Enable shutdown and reload via HTTP request. (default false)从源码结构看,web/web.go 第 581–595 行 清晰地展示了这一开关的路由注册逻辑:当o.EnableLifecycle为 true 时,/-/quit与/-/reload的 POST/PUT 请求被绑定到真实的处理函数h.quit/h.reload;否则这四个方法全部注册到一个统一返回403 Forbidden的处理器,响应体为:
Lifecycle API is not enabled.也就是说,即使不启用生命周期 API,调用这两个端点也不会得到 404,而是明确的 403 提示——这在排查"为什么远程重载不生效"时很有价值:如果收到的是 403 而不是 405 或成功响应,说明部署时漏加了--web.enable-lifecycle启动参数(参数默认值说明见 命令行参考)。
另外两个细节也来自同一路由注册代码:
- 对
/-/quit和/-/reload使用GET方法会收到405 Method Not Allowed,响应体为Only POST or PUT requests allowed(web/web.go 第 596–603 行)。因此健康巡检脚本不应使用 GET 探测这两个端点; - 健康/就绪端点则无此限制,GET 与 HEAD 均已显式注册(web/web.go 第 608–621 行)。
健康检查:GET /-/healthy
GET /-/healthy HEAD /-/healthy按文档说明,该端点始终返回 200,用于检查 Prometheus 进程是否存活。源码实现印证了这一点(web/web.go 第 608–614 行):GET 请求返回 200 和Prometheus is Healthy.文本,HEAD 请求仅返回 200 状态码,二者都不依赖任何内部组件状态:
router.Get("/-/healthy", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "%s is Healthy.\n", o.AppName) }) router.Head("/-/healthy", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })这意味着/ready失败而/healthy成功,说明进程活着但尚未准备好服务(例如启动初期 TSDB 加载阶段);/healthy失败则说明进程或监听端口本身出了问题。这一区分正是 Kubernetes 中 liveness 与 readiness 探针分离设计的典型落地场景。
就绪检查:GET /-/ready
GET /-/ready HEAD /-/ready该端点在 Prometheus 已准备好接收流量(即可正常响应查询)时返回 200。与/healthy的"无条件 200"不同,/-/ready的响应被一层就绪状态包装器readyf(即Handler.testReady)拦截,实现见 web/web.go 第 681–698 行:
func (h *Handler) testReady(f http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch ReadyStatus(h.ready.Load()) { case Ready: f(w, r) case NotReady: w.Header().Set("X-Prometheus-Stopping", "false") w.WriteHeader(http.StatusServiceUnavailable) fmt.Fprint(w, "Service Unavailable") case Stopping: w.Header().Set("X-Prometheus-Stopping", "true") w.WriteHeader(http.StatusServiceUnavailable) fmt.Fprint(w, "Service Unavailable") default: w.WriteHeader(http.StatusInternalServerError) ...由此可以得到三个可验证的实现事实:
- 状态由原子变量
h.ready保存,通过SetReady切换(web/web.go 第 663–673 行),同时联动ready指标暴露; - 未就绪(
NotReady)或正在停止(Stopping)时均返回503 Service Unavailable,并通过响应头X-Prometheus-Stopping区分二者(false/true)。探针或客户端可以用这个头判断实例是"还没起来"还是"正在退出"; - 同样的包装器还应用在
/federate、/consoles/*等路由上(web/web.go 第 504–508 行),即未就绪时联邦抓取同样会被拒。
此外,Prometheus 自带的 Web UI 也把/-/ready当作前端门禁:新版前端组件 ReadinessWrapper.tsx 和旧版 useFetch.ts 在渲染页面前都会先请求/-/ready,未就绪时不展示查询界面。
行为验证可参考 web/web_test.go 第 359–389 行 的测试用例:它构造了 Ready 与 503 交替出现的场景,并断言/ready端点在各状态下返回的状态码计数正确;第 713–734 行附近还覆盖了 RoutePrefix 前缀下/healthy、/ready的可达性。
配置热重载:PUT /-/reload 与 POST /-/reload
PUT /-/reload POST /-/reload该端点触发 Prometheus配置与规则文件的重新加载。文档明确指出它默认禁用,需通过--web.enable-lifecycle开启。
请求处理与同步应答
处理函数Handler.reload的实现见 web/web.go 第 962–968 行:
func (h *Handler) reload(w http.ResponseWriter, _ *http.Request) { rc := make(chan error) h.reloadCh <- rc if err := <-rc; err != nil { http.Error(w, fmt.Sprintf("failed to reload config: %s", err), http.StatusInternalServerError) } }可以注意到这是一个同步等待的设计:HTTP handler 把一次性结果通道rc投递到全局的reloadCh,然后阻塞等待结果,失败时返回500并在响应体中携带错误信息。也就是说curl -X POST成功返回本身就等价于"本次重载已成功",可以直接在 CI/CD 或配置下发脚本里作为成功判据。
底层重载循环:HTTP 与 SIGHUP 共用同一条链路
真正执行重载的是主进程中的重载 goroutine,见 main.go 第 1376–1429 行。它的核心是一个select循环,同时监听三类事件:
for { select { case <-hup: // 1. SIGHUP 信号 if err := reloadConfig(...); err != nil { logger.Error("Error reloading config", "err", err) } case rc := <-webHandler.Reload(): // 2. HTTP /-/reload 请求 if err := reloadConfig(...); err != nil { logger.Error("Error reloading config", "err", err) rc <- err } else { rc <- nil } case <-time.Tick(time.Duration(cfg.autoReloadInterval)): // 3. 周期自动重载 ...这段代码揭示了三个要点:
- SIGHUP 与 HTTP 重载是等价入口:
signal.Notify(hup, syscall.SIGHUP)(main.go 第 1381–1382 行)注册的信号通道与webHandler.Reload()通道汇聚到同一个reloadConfig(...)调用,二者行为一致,只是错误传播路径不同(HTTP 入口会同步把错误回传给客户端); - 存在周期自动重载机制:当启用了自动重载(源码中
cfg.enableAutoReload、cfg.autoReloadInterval控制)时,循环还会按固定间隔比对配置文件校验和(config.GenerateChecksum)并触发重载,此时可以完全不依赖信号或 HTTP 端点; - 重载成败有指标可观测:web/web.go 第 920–922 行 的
/metrics查询映射中列出了prometheus_config_last_reload_successful(gauge,最近一次重载是否成功)与prometheus_config_last_reload_success_timestamp_seconds(最近一次成功重载的时间戳)。即使不关注 HTTP 响应码,也可以用这两个指标做告警,例如"配置重载长期不成功"。
端到端行为有专门的测试覆盖:cmd/prometheus/reload_test.go 验证了配置修改后触发重载、以及重载失败时的回滚与报错;web/web_test.go 第 508 行附近 则对/-/quit//-/reload在生命周期 API 禁用时返回 403 的场景做了断言。
典型调用示例
# 通过 HTTP 触发重载(需 --web.enable-lifecycle) curl -X POST http://localhost:9090/-/reload -w "%{http_code}" # 等价方式:发送 SIGHUP(不需要 enable-lifecycle,但需进程管理权限) kill -HUP $(pidof prometheus)优雅关停:PUT /-/quit 与 POST /-/quit
PUT /-/quit POST /-/quit该端点触发 Prometheus 的优雅关停,同样默认禁用,需--web.enable-lifecycle开启。
处理函数Handler.quit见 web/web.go 第 950–960 行:
func (h *Handler) quit(w http.ResponseWriter, _ *http.Request) { var closed bool h.quitOnce.Do(func() { closed = true close(h.quitCh) fmt.Fprint(w, "Requesting termination... Goodbye!") }) if !closed { fmt.Fprint(w, "Termination already in progress.") } }两个值得注意的实现细节:
- 幂等设计:
quitOnce(sync.Once)保证quitCh只被 close 一次。首个请求会得到Requesting termination... Goodbye!,并发的重复请求会得到Termination already in progress.的提示而不会造成二次关停副作用; - 关停是异步开始的:HTTP 响应返回表示"关停请求已被接受",而非"进程已退出"。客户端若以该请求完成作为下线判据,应配合
/-/ready返回 503(且X-Prometheus-Stopping: true,见上文Stopping状态)来确认实例已进入停止流程。主进程侧通过Handler.Quit()暴露的只读通道(web/web.go 第 701–704 行)接收该信号并执行资源清理。
信号方式:不启用 HTTP 生命周期 API 时的替代方案
文档对两个生命周期端点各给出了一条替代路径——直接向进程发信号:
| 操作 | HTTP 端点(需 enable-lifecycle) | 信号方式(始终可用) |
|---|---|---|
| 重载配置 | PUT/POST /-/reload | kill -HUP <pid> |
| 优雅关停 | PUT/POST /-/quit | kill -TERM <pid> |
信号处理均在主进程初始化阶段注册:SIGHUP的重载监听见 main.go 第 1381–1382 行;SIGTERM(含os.Interrupt,即Ctrl+C的SIGINT)的关停监听见 main.go 第 1271 行:
signal.Notify(term, os.Interrupt, syscall.SIGTERM)从源码结构看,信号通道的关闭被包在sync.Once中,以便在多个执行阶段(SIGTERM 或配置加载完成时)都能安全关闭同一条通道,这与 HTTP 入口的幂等关停设计相呼应。
两种方式的选型建议:
- 容器化/编排环境(Kubernetes、Docker):优先用
/-/ready做就绪/存活探测,配置下发流水线用/-/reload热更新,避免重启实例;关停交给编排器发 SIGTERM,/-/quit作为补充手段; - 裸机/systemd 环境:如果无法保证进程管理工具支持信号转发,或希望在 HTTP 层统一运维入口,则启用
--web.enable-lifecycle; - 安全考虑:
/-/quit是一个能终止服务的能力端点,若 Prometheus 的 Web 端口直接暴露给不可信网络,务必通过--web.enable-lifecycle=false(默认值)关闭它,并依靠反向代理/网络策略限制管理面访问。
小结
Prometheus 的管理 API 由四个端点构成:/-/healthy提供无条件 200 的存活探测;/-/ready依据内部就绪状态返回 200/503,并用X-Prometheus-Stopping头区分"未就绪"与"停止中";/-/reload与/-/quit在--web.enable-lifecycle开启后提供与SIGHUP/SIGTERM等价的远程热重载与优雅关停能力,且重载端点会同步返回失败原因、失败重载可通过prometheus_config_last_reload_successful指标持续观测。相关实现集中于 web/web.go(路由注册与处理器)与 cmd/prometheus/main.go(启动参数与信号/重载循环),行为验证可参考 web/web_test.go 与 cmd/prometheus/reload_test.go。
【免费下载链接】prometheusThe Prometheus monitoring system and time series database.项目地址: https://gitcode.com/GitHub_Trending/pr/prometheus
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考