服务端面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 05月27日 18:04

如何使用 Pine Script 创建自定义指标和交易策略?

Pine Script 是 TradingView 平台专有的脚本语言,用于创建自定义技术指标、绘图工具和交易策略。无论是想自定义一个均线变色显示,还是构建一套完整的量化交易系统,Pine Script 都能实现。Pine Script 的基本语法结构Pine Script 的语法融合了 Python 和 JavaScript 的特点,上手门槛不高,但要写出健壮的策略代码需要理解其核心机制。一个最简单的指标脚本如下://@version=5indicator("My First Indicator", overlay=true)length = input.int(20, "MA Length")ma = ta.sma(close, length)plot(ma, color=color.blue, linewidth=2)第一行 //@version=5 声明脚本版本,indicator() 函数定义指标名称和属性,overlay=true 表示指标叠加在K线图上。input.int() 创建用户可配置的参数,ta.sma() 计算简单移动平均,plot() 将结果绘制到图表上。版本选择:v4、v5 还是 v6?目前 Pine Script 有三个主要版本在使用:v4:老旧版本,TradingView 已不再推荐,仅用于维护旧脚本v5:当前最稳定、社区资源最丰富的版本,绝大多数教程和开源代码基于 v5v6:2026 年最新版本,新增枚举类型、动态请求、多段线绘制等功能,但社区生态仍在建设中新项目建议从 v5 起步,掌握后再迁移到 v6。v5 到 v6 的迁移主要涉及整数除法行为变化(v5 中 1/2=0,v6 中 1/2=0.5)、更严格的类型检查等,TradingView 提供了自动迁移工具。核心概念详解输入参数:让指标可配置input() 系列函数允许用户在指标设置面板中调整参数,无需修改代码://@version=5indicator("Configurable RSI")length = input.int(14, "RSI Length", minval=1)src = input.source(close, "Source")ovb = input.int(70, "Overbought Level")oss = input.int(30, "Oversold Level")rsi = ta.rsi(src, length)plot(rsi, "RSI", color=color.purple)hline ovb, "Overbought", color=color.redhline oss, "Oversold", color=color.greeninput.int() 定义整数参数,input.source() 定义数据源(如 close、open、high),input.float() 定义浮点参数,input.string() 定义字符串选项。这些函数都支持 minval、maxval、step 等约束。变量声明:理解 var 和 varipPine Script 按照每根K线逐根执行脚本,变量的生命周期是理解其运行模型的关键:普通赋值:每根K线重新计算,如 count = count + 1 在每根K线上初始值都是 navar 声明:只在第一根K线上初始化,之后保持值跨K线持久化,如 var count = 0 然后 count := count + 1 会持续累加varip 声明:与 var 类似,但在实时K线的每次价格变动时更新,适用于需要跟踪盘口变化的场景//@version=5indicator("var vs varip Demo")var int barCount = 0barCount := barCount + 1varip int tickCount = 0tickCount := tickCount + 1plot(barCount, "Bars", color=color.blue)plot(tickCount, "Ticks", color=color.red)自定义函数Pine Script 支持自定义函数,且可以返回多个值(用元组实现)://@version=5indicator("Custom Function Demo")calcMA(float src, int len, string method) => switch method "SMA" => ta.sma(src, len) "EMA" => ta.ema(src, len) "WMA" => ta.wma(src, len) => ta.sma(src, len)maType = input.string("SMA", "MA Type", options=["SMA", "EMA", "WMA"])maLen = input.int(20, "Length")result = calcMA(close, maLen, maType)plot(result, "Custom MA", color=color.orange, linewidth=2)v5 引入的 switch 语句使多条件逻辑比 if-else 更简洁,v6 进一步支持枚举类型来替代字符串选项。创建自定义指标:从思路到实现以创建一个带买卖信号的均线交叉指标为例,完整演示开发流程://@version=5indicator("MA Crossover Signal", overlay=true)// 1. 定义输入参数fastLen = input.int(10, "Fast MA Length", minval=1)slowLen = input.int(30, "Slow MA Length", minval=1)maType = input.string("EMA", "MA Type", options=["SMA", "EMA"])// 2. 计算指标fastMA = maType == "SMA" ? ta.sma(close, fastLen) : ta.ema(close, fastLen)slowMA = maType == "SMA" ? ta.sma(close, slowLen) : ta.ema(close, slowLen)// 3. 判断交叉bullCross = ta.crossover(fastMA, slowMA)bearCross = ta.crossunder(fastMA, slowMA)// 4. 绘制plot(fastMA, "Fast MA", color=color.blue, linewidth=2)plot(slowMA, "Slow MA", color=color.red, linewidth=2)plotshape(bullCross, "Buy Signal", shape.triangleup, location.belowbar, color.green, size=size.small)plotshape(bearCross, "Sell Signal", shape.triangledown, location.abovebar, color.red, size=size.small)// 5. 设置警报alertcondition(bullCross, "Golden Cross", "Fast MA crossed above Slow MA")alertcondition(bearCross, "Death Cross", "Fast MA crossed below Slow MA")这个指标实现了:可配置的快慢均线参数、SMA/EMA 切换、交叉信号标记、以及警报触发条件。在 TradingView 的 Pine Script 编辑器中粘贴代码,点击"添加到图表"即可看到效果。构建交易策略与回测将 indicator() 替换为 strategy() 就能启用回测功能,自动生成交易报告://@version=5strategy("MA Crossover Strategy", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1)fastLen = input.int(10, "Fast MA")slowLen = input.int(30, "Slow MA")fastMA = ta.ema(close, fastLen)slowMA = ta.ema(close, slowLen)bullCross = ta.crossover(fastMA, slowMA)bearCross = ta.crossunder(fastMA, slowMA)if bullCross strategy.entry("Long", strategy.long)if bearCross strategy.close("Long")plot(fastMA, "Fast", color=color.blue)plot(slowMA, "Slow", color=color.red)strategy() 函数的关键参数:initial_capital:回测初始资金default_qty_type 和 default_qty_value:每次交易的仓位大小commission_type 和 commission_value:手续费设置slippage:滑点模拟strategy.entry() 开仓,strategy.close() 平仓,strategy.exit() 可以设置止损止盈。回测结果面板显示净利润、胜率、最大回撤、夏普比率等关键指标。多时间框架分析request.security() 函数可以在当前图表的时间框架下访问其他时间框架的数据,这是构建多周期策略的基础://@version=5indicator("Multi-Timeframe MA", overlay=true)currentMA = ta.ema(close, 20)dailyMA = request.security(syminfo.tickerid, "D", ta.ema(close, 20))weeklyMA = request.security(syminfo.tickerid, "W", ta.ema(close, 20))plot(currentMA, "Chart MA", color=color.blue)plot(dailyMA, "Daily MA", color=color.orange)plot(weeklyMA, "Weekly MA", color=color.purple)使用 request.security() 时要注意:避免在当前时间框架比请求时间框架更小时产生重绘问题,推荐在历史数据回测中使用 barmerge.lookahead_on 配合 barmerge.gaps_off 来确保数据对齐。常用内置函数速查| 类别 | 函数 | 说明 ||------|------|------|| 趋势指标 | ta.sma() / ta.ema() / ta.wma() | 简单/指数/加权移动平均 || 趋势指标 | ta.macd() | MACD 指标,返回元组 || 动量指标 | ta.rsi() | 相对强弱指标 || 动量指标 | ta.stoch() | 随机指标 || 波动指标 | ta.atr() | 平均真实波幅 || 波动指标 | ta.bb() | 布林带,返回元组 || 成交量 | ta.obv() | 能量潮指标 || 交叉判断 | ta.crossover() / ta.crossunder() | 交叉信号检测 || 绘图 | plot() / plotshape() / plotchar() | 线条/形状/字符绘制 || 警报 | alertcondition() | 自定义警报条件 || 策略 | strategy.entry() / strategy.close() / strategy.exit() | 开仓/平仓/带止损止盈平仓 |实际开发中的注意事项性能优化: Pine Script 在每根K线上逐根执行,避免在循环中进行复杂计算。for 循环的迭代次数有上限(默认 100 次),超出会报错。将能用内置函数实现的逻辑优先使用 ta.* 系列,它们经过了底层优化。重绘问题: 某些写法会导致历史信号随新数据变化,这会严重干扰回测结果的可靠性。使用 barstate.isrealtime 区分实时和历史数据,在策略中避免使用 request.security() 获取未来数据。代码组织: 随着指标复杂度增加,建议将逻辑拆分为独立函数,使用有意义的变量名,添加注释说明关键逻辑。Pine Script 支持用 import 引入库(library),将通用功能封装为可复用模块。调试技巧: 使用 label.new() 在图表上打印变量值,比反复修改 plot() 更灵活。str.tostring() 将数值转为字符串用于标签显示。在开发阶段,可以临时用 plot() 输出中间变量来排查逻辑错误。从想法到发布的完整流程在 TradingView 图表底部打开 Pine Script 编辑器选择"新建/指标"或"新建/策略"模板编写代码,点击"添加到图表"预览效果切换不同品种和时间框架验证指标的适用性确认无误后点击"发布",可设为公开或私有在指标设置中调整参数,观察不同配置下的表现对策略类脚本,使用策略测试器面板查看回测报告Pine Script 的学习曲线平缓,从几行代码的简单指标到复杂的量化策略,都可以在 TradingView 平台内完成开发、测试和发布。掌握输入参数、变量声明、内置技术分析函数和策略框架这几个核心模块后,就能应对大部分自定义指标和策略的开发需求。
服务端阅读 05月27日 18:03

Gin 框架的性能优化技巧和最佳实践有哪些?

生产环境基础配置Gin 在 debug 模式下会输出大量路由调试信息,拖慢启动和请求处理速度。上线前务必切换到 release 模式:gin.SetMode(gin.ReleaseMode)r := gin.New()同时,Go 默认的 HTTP Server 没有超时限制,容易遭受 Slowloris 攻击,必须显式设置:srv := &http.Server{ Addr: ":8080", Handler: r, ReadHeaderTimeout: 5 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 120 * time.Second,}srv.ListenAndServe()如果项目跑在容器里,Go 默认可能识别不到 CPU 限制,导致创建了过多的 goroutine。引入 uber-go/automaxprocs 自动匹配容器的 CPU 配额:import _ "go.uber.org/automaxprocs"路由优化路由分组与结构规划Gin 的基数树路由在热路径上实现了零堆分配,路由解析时间在数十纳秒级别。但不合理的路由结构仍会影响可维护性和间接性能:api := r.Group("/api/v1"){ users := api.Group("/users") { users.GET("", listUsers) // 高频路由放前面 users.GET("/:id", getUser) // 静态路由优先于动态路由 users.POST("", createUser) users.PUT("/:id", updateUser) }}需要注意:静态路由和动态路由不要冲突,例如 /users/new 和 /users/:id 同时存在时,Gin 会在启动时报 panic。路由嵌套层级也不宜过深,3 层以内为佳。路由注册时机所有路由必须在服务启动前注册完毕。Gin 不支持运行时动态增删路由,启动后路由树是只读的,这也是它零分配的前提。JSON 序列化优化标准库 encoding/json 在大负载场景下性能瓶颈明显。替换为 json-iterator/go 可获得 2-3 倍的序列化速度提升,且 API 完全兼容:import jsoniter "github.com/json-iterator/go"var json = jsoniter.ConfigCompatibleWithStandardLibrary// 使用方式与标准库完全一致c.JSON(200, data)另一个选择是 goccy/go-json,无需替换 import 路径,直接通过编译标签切换:// go build -tags=go_json .// 自动替换 encoding/json 为高性能实现中间件优化精确挂载中间件不要全局挂载所有中间件,只在需要的路由组上添加。例如公开接口不需要鉴权:r.GET("/public/health", healthCheck)authorized := r.Group("/api")authorized.Use(authMiddleware(), rateLimitMiddleware()){ authorized.GET("/profile", getProfile) authorized.POST("/data", createData)}中间件顺序可能提前中断请求的中间件(限流、鉴权)应该放在最前面,避免已执行的中间件白费开销:api.Use( rateLimitMiddleware(), // 先限流,拦截恶意请求 authMiddleware(), // 再鉴权,拒绝未授权请求 logMiddleware(), // 最后记录日志)避免中间件中的阻塞操作中间件里不要做同步的远程调用或重计算。如果必须做,放到 goroutine 中并使用 context 控制超时:func asyncLogMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() go func() { log.Printf("%s %s %d %v", c.Request.Method, c.Request.URL.Path, c.Writer.Status(), time.Since(start)) }() }}数据绑定优化使用明确的绑定方法ShouldBind 会根据 Content-Type 自动推断绑定方式,多了推断逻辑。直接使用明确的方法更高效:// 推荐:明确指定绑定方式c.ShouldBindJSON(&req)// 不推荐:通用绑定,运行时推断c.ShouldBind(&req)控制验证规则只验证业务必需的字段,避免在 struct tag 中堆砌过多验证规则。复杂的验证逻辑放到业务层处理,不要让绑定层承担过重职责。数据库优化连接池配置默认的连接池配置不适合生产环境,必须根据负载调整:db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})sqlDB, _ := db.DB()sqlDB.SetMaxOpenConns(100) // 最大打开连接数sqlDB.SetMaxIdleConns(10) // 最大空闲连接数sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间sqlDB.SetConnMaxIdleTime(10 * time.Minute) // 空闲连接最大存活时间启用 PreparedStmtGORM 默认不启用预编译语句。开启后,重复查询可提升约 25% 的性能:db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{ PrepareStmt: true,})查询层面的优化为 WHERE 条件和 JOIN 字段建立索引使用 Preload 或 Joins 解决 N+1 查询问题只 SELECT 需要的字段,避免 SELECT *大量数据使用分页查询,不要一次加载全表响应优化启用 Gzip 压缩对于文本类响应(JSON、HTML),Gzip 压缩可减少 60%-80% 的传输体积:import "github.com/gin-contrib/gzip"r.Use(gzip.Gzip(gzip.DefaultCompression))注意:图片和视频等已压缩的内容不需要再开 Gzip,反而浪费 CPU。可以按路由组粒度挂载。流式响应处理大数据当响应体积较大或需要逐步推送数据时(如 LLM 推理),使用 c.Stream:c.Stream(func(w io.Writer) bool { data, done := getNextChunk() w.Write(data) return !done // 返回 false 结束流})Gin v1.12.0 的 c.Stream 支持生产者背压,防止消费者过慢导致内存溢出。设置缓存头对不常变化的接口响应设置合适的缓存头,减少重复请求:c.Header("Cache-Control", "public, max-age=3600")c.Header("ETag", computeETag(data))内存优化sync.Pool 复用对象高频创建的临时对象用 sync.Pool 复用,减少 GC 压力。这在 JSON 编解码、字节缓冲等场景效果显著:var bufPool = sync.Pool{ New: func() any { return new(bytes.Buffer) },}func handler(c *gin.Context) { buf := bufPool.Get().(*bytes.Buffer) defer func() { buf.Reset() bufPool.Put(buf) }() // 使用 buf ...}避免在 Context 中存放大对象c.Set(key, value) 存储的数据会随请求生命周期持有。不要在这里放大的 slice 或 map,请求结束前用 c.Set(key, nil) 主动释放。字符串与字节切片Go 中字符串和 []byte 的转换会触发内存分配和拷贝。频繁转换的场景尽量统一使用 []byte,或者用 unsafe 零拷贝(需谨慎)。并发处理控制并发 goroutine 数量不要无限制地 go func(),用 worker pool 或 semaphore 限制并发:sem := make(chan struct{}, 100) // 最多 100 个并发func handler(c *gin.Context) { sem <- struct{}{} go func() { defer func() { <-sem }() processTask(c) }() c.JSON(202, gin.H{"status": "accepted"})}Context 传递与超时控制启动 goroutine 时必须传递 c.Request.Context(),而非 c 本身(gin.Context 不并发安全):ctx := c.Request.Context()go func() { select { case <-ctx.Done(): return // 请求已取消 case result := <-doWork(ctx): handleResult(result) }}()日志优化异步写入日志同步写日志会阻塞请求处理,高 QPS 下尤其明显。用 channel 做异步缓冲:type AsyncLogger struct { ch chan string}func NewAsyncLogger() *AsyncLogger { l := &AsyncLogger{ch: make(chan string, 10000)} go l.drain() return l}func (l *AsyncLogger) Log(msg string) { select { case l.ch <- msg: default: // channel 满了,丢弃日志,避免阻塞请求 }}func (l *AsyncLogger) drain() { for msg := range l.ch { os.Stdout.Write([]byte(msg + "\n")) }}合理设置日志级别生产环境使用 INFO 或 WARN 级别,DEBUG 级别会产生大量 I/O。Gin 自带日志中间件在 release 模式下会自动减少输出。性能监控与分析pprof 集成线上服务必须开启 pprof,用于定位 CPU 热点、内存泄漏和 goroutine 泄漏:import "github.com/gin-contrib/pprof"pprof.Register(r)访问 /debug/pprof/ 查看概览,用 go tool pprof 生成火焰图:go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30Prometheus 指标采集用 gin-prometheus 中间件暴露请求延迟、错误率等指标:import "github.com/zsais/go-gin-prometheus"p := ginprometheus.NewPrometheus("gin")p.Use(r)压测验证优化前后都要压测,用数据说话。常用工具:wrk 或 hey 发起 HTTP 压测go test -bench 做基准测试go-wrk 专门压测 Go HTTP 服务wrk -t4 -c200 -d30s http://localhost:8080/api/v1/users优化检查清单| 类别 | 优化项 | 预期收益 ||------|--------|---------|| 运行模式 | gin.SetMode(gin.ReleaseMode) | 减少 debug 开销,约 12% 性能提升 || HTTP Server | 设置 ReadHeaderTimeout/WriteTimeout/IdleTimeout | 防止连接耗尽攻击 || JSON | 替换为 json-iterator 或 go-json | 大负载下 2-3x 序列化速度 || 数据库 | GORM PrepareStmt: true | 重复查询约 25% 提升 || 数据库 | 配置连接池参数 | 避免连接泄漏和排队等待 || 压缩 | Gzip 中间件 | 传输体积减少 60%-80% || 内存 | sync.Pool 复用对象 | 减少 GC 压力 || 并发 | goroutine 池 / semaphore | 防止 goroutine 爆炸 || 日志 | 异步写入 + 合理级别 | 减少 I/O 阻塞 || 监控 | pprof + Prometheus | 定位瓶颈,数据驱动优化 |以上优化措施不是一次性全部做完,而是根据实际瓶颈逐步实施。先开 pprof 和监控找到热点,再针对性优化,效果最明显。
服务端阅读 05月27日 18:03

什么是 Ollama,它有哪些核心特性和优势?

Ollama 是什么Ollama 是一款开源的大语言模型本地运行平台,让开发者无需依赖云服务,即可在本地机器上一键运行和部署各种开源 LLM。它由 Go 语言开发,底层基于 llama.cpp 进行模型推理,通过量化技术压缩模型体积,使得消费级硬件也能流畅运行大型模型。截至 2026 年,Ollama 在 GitHub 已累积超过 165k Stars,拥有 40,000+ 社区集成,成为本地 LLM 部署领域使用最广泛的工具之一。核心特性本地运行,隐私保护所有模型在本地执行,数据交互全程在本地完成,不会上传到任何云端服务器。对于隐私敏感场景(医疗、金融、法律等),这意味着零数据泄露风险,满足企业合规要求。极简安装与使用Ollama 的安装过程非常简单,macOS、Windows 和 Linux 三端均支持一键安装,无需配置 CUDA、PyTorch 等复杂环境。安装完成后,只需一条命令即可拉取模型并启动对话:# 安装后拉取并运行模型ollama run llama3模型下载、量化、推理配置全部自动化完成,开发者无需手动处理任何底层细节。丰富的模型支持Ollama 支持超过 150 个开源大模型,涵盖当前主流的模型系列:Meta Llama 系列:Llama 3、Llama 3.1、Llama 3.2阿里巴巴 Qwen 系列:Qwen2、Qwen2.5Mistral 系列:Mistral、MixtralGoogle Gemma 系列:Gemma 2微软 Phi 系列:Phi-3DeepSeek 系列:DeepSeek R1其他:Code Llama、Stable Code 等所有模型均以 GGUF 格式存储,Ollama 自动选择最适合当前硬件的量化等级。OpenAI 兼容的 API 服务Ollama 内置 RESTful API 服务,默认监听 http://localhost:11434,提供与 OpenAI API 兼容的接口格式,方便快速集成到现有应用中:# 调用聊天接口curl http://localhost:11434/v1/chat/completions \ -d '{ "model": "llama3", "messages": [{ "role": "user", "content": "你好" }] }'这种兼容性意味着基于 OpenAI SDK 构建的应用,只需修改 base URL 即可切换到本地 Ollama 服务,无需重写代码。同时,Ollama 也与 LangChain、LlamaIndex、Open WebUI 等主流 AI 开发框架无缝集成。模型管理与量化优化Ollama 提供完整的模型生命周期管理命令:ollama pull llama3 # 下载模型ollama list # 查看已安装模型ollama rm mistral # 删除模型ollama run qwen2.5 # 运行模型量化是 Ollama 在消费级硬件上运行大模型的关键。它通过将模型权重从 16 位浮点数压缩为 4 位整数(Q4_0 量化),将模型体积缩减至原来的 1/4 左右,同时保持较高的推理质量。例如,Llama 3 8B 模型原始大小约 16GB,Q4 量化后仅约 4.7GB,可在 8GB 内存的笔记本上流畅运行。跨平台支持Ollama 原生支持三大操作系统:macOS:支持 Apple Silicon(M1/M2/M3/M4)的 Metal 加速,推理速度出色Windows:支持 CUDA 和 CPU 推理,可通过 WSL2 获得 Linux 兼容性Linux:支持 CUDA 加速,适合服务器部署场景2026 年架构升级要点2025 年底 Ollama 进行了一次重要的架构升级,引入了以下改进:精确内存分配机制:OOM(内存溢出)崩溃减少了 70%,大幅提升运行稳定性模型进程隔离:每个模型在独立进程中运行,单个模型崩溃不影响其他模型和主进程并发请求优化:改进了多用户同时请求时的调度策略适用场景开发者本地开发调试在本地快速验证 Prompt、测试模型效果,无需每次调用云 API 产生费用,迭代速度更快。适合 AI 应用开发、RAG 系统原型搭建等场景。企业私有化部署在内网环境中部署 Ollama 作为 AI 推理层,所有数据不出内网,满足金融、医疗、政务等行业的合规要求。配合 Open WebUI 可提供类 ChatGPT 的内部服务。隐私敏感的离线场景完全无网络的环境下运行 LLM,如科研机构的封闭网络、军事或涉密单位,确保数据零外泄。教学与科研研究人员可以本地运行各种开源模型进行对比实验,教学场景下学生可以亲手体验大模型的运行过程。局限性Ollama 虽然在本地部署场景表现出色,但也存在一些限制:并发能力有限:原生架构面向单机设计,高并发场景(如同时服务数百用户)建议使用 vLLM 或 TensorRT-LLM仅支持开放权重模型:无法运行 GPT-4、Claude 等闭源商业模型命令行门槛:对非技术用户有一定使用门槛,需要配合 Open WebUI 等图形界面降低使用难度硬件要求:运行 70B 及以上参数的模型仍需要高端 GPU 或大内存服务器支持与同类工具对比| 特性 | Ollama | LM Studio | vLLM ||------|--------|-----------|------|| 安装难度 | 极低 | 低 | 较高 || API 兼容性 | OpenAI 兼容 | OpenAI 兼容 | OpenAI 兼容 || 模型数量 | 150+ | 100+ | 取决于手动配置 || 并发能力 | 低 | 低 | 高 || 图形界面 | 需额外安装 | 内置 | 无 || 适用场景 | 本地开发/小规模部署 | 个人使用 | 生产级服务 |Ollama 在易用性和生态丰富度上占据优势,是个人开发者和小团队的首选;vLLM 则更适合对并发和吞吐量有要求的生产环境。
服务端阅读 05月27日 18:01

Prometheus Alertmanager 告警怎么配置?

线上服务出问题却收不到告警,或者告警多到看不过来——这是很多团队上 Prometheus 后遇到的典型问题。核心原因往往出在两个环节:告警规则写得不准,或者 Alertmanager 路由配置没理顺。下面从规则定义到通知分发,把完整链路讲清楚。告警规则:Prometheus 端定义触发条件告警规则写在独立的规则文件中,由 Prometheus 负责评估。一条规则的核心要素:expr:PromQL 表达式,定义什么条件算异常for:条件持续多久才触发,避免瞬时抖动误报labels:附加标签,供 Alertmanager 路由和分组使用annotations:告警描述,出现在通知内容里示例——CPU 使用率超过 80% 持续 5 分钟:groups: - name: node_alerts rules: - alert: HighCPUUsage expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80 for: 5m labels: severity: warning team: infra annotations: summary: "{{ $labels.instance }} CPU 使用率过高" description: "当前值 {{ $value }}%,阈值 80%"再补一个内存告警的例子,实际生产中 CPU 和内存往往配对出现: - alert: HighMemoryUsage expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85 for: 5m labels: severity: critical team: infra annotations: summary: "{{ $labels.instance }} 内存使用率过高" description: "当前值 {{ $value }}%,阈值 85%"规则文件在 prometheus.yml 中通过 rule_files 加载:rule_files: - "alerts/*.yml"for 子句的工作机制for 不是简单的延迟。Prometheus 内部对每条规则维护三个状态:Inactive:表达式不满足,无告警Pending:表达式满足,但还没持续到 for 指定的时间Firing:表达式满足且持续了 for 时间,告警真正触发并推送给 Alertmanager理解这个状态机有助于排查告警"延迟触发"的问题——如果 for 设了 10m,但指标在 9 分钟时恢复正常又再次超限,Pending 计时器会重置。连接 Alertmanager:让告警有去处Prometheus 自身不发送通知,需要把告警推给 Alertmanager。在 prometheus.yml 中配置:alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093如果部署了多个 Alertmanager 实例做高可用,直接列出所有目标即可:alerting: alertmanagers: - static_configs: - targets: - alertmanager-1:9093 - alertmanager-2:9093 - alertmanager-3:9093Prometheus 会向所有实例推送告警,Alertmanager 内部通过 Gossip 协议同步状态,确保同一条告警不会重复发送通知。配置完成后,Prometheus 会将 Firing 状态的告警持续推送到 Alertmanager。Alertmanager 路由:决定谁收到什么通知Alertmanager 的核心逻辑是「收到告警 → 分组 → 路由 → 抑制/静默检查 → 发送通知」。路由配置决定了告警最终走向哪个接收器。路由是一个树状结构:根节点是默认路由,子节点通过标签匹配来覆盖默认行为。告警从根节点进入,深度优先遍历,匹配到第一个符合条件的节点就停下来处理。基础路由示例:route: group_by: ['alertname', 'cluster'] group_wait: 30s group_interval: 5m repeat_interval: 4h receiver: 'default' routes: - match: severity: critical receiver: 'oncall' repeat_interval: 1h - match: team: infra receiver: 'infra-team'四个时间参数的含义:group_wait:收到该组第一条告警后等多久再发通知,目的是攒一批一起发group_interval:同组后续告警的最小发送间隔repeat_interval:同一条告警重复通知的最小间隔group_by:按哪些标签分组,相同标签值的告警合并为一条通知子路由 routes 支持按标签匹配,实现不同级别的告警走不同通道。match 和 match_re 的区别match:精确匹配,标签值必须完全相等match_re:正则匹配,标签值满足正则表达式即可routes: - match_re: service: nginx|apache receiver: 'web-team' - match_re: service: mysql|mongodb|redis receiver: 'db-team'当一个告警可能匹配多条子路由时,默认只走第一条匹配到的。如果需要一条告警同时发送给多个接收器,在子路由中加上 continue: true:routes: - match: severity: critical receiver: 'oncall' continue: true - match: team: infra receiver: 'infra-team'这样 critical 级别的告警会同时发给 oncall 和 infra-team。接收器配置:通知发到哪里Alertmanager 支持多种通知渠道,包括 Email、Slack、PagerDuty、Webhook、企业微信、钉钉等:receivers: - name: 'default' email_configs: - to: 'ops@example.com' from: 'alertmanager@example.com' smarthost: 'smtp.example.com:587' - name: 'oncall' webhook_configs: - url: 'https://hooks.example.com/alert' send_resolved: true - name: 'infra-team' email_configs: - to: 'infra@example.com'send_resolved: true 表示告警恢复时也发通知,生产环境建议开启。通知模板自定义默认通知格式信息量有限,可以通过 Go Template 自定义通知内容。在全局配置中指定模板文件路径:templates: - '/etc/alertmanager/templates/*.tmpl'模板中可以引用告警的 Labels 和 Annotations,灵活组织通知内容。告警抑制:高优先级告警压制低优先级当集群整体故障时,不需要再收到该集群上每个服务的低级别告警。抑制规则实现这个逻辑:inhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'cluster']含义:当同一个集群同一种告警存在 critical 级别时,warning 级别的不再单独通知。equal 列表是判断「同一种告警」的依据。也可以同时定义多条抑制规则,覆盖不同场景:inhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'cluster'] - source_match: alertname: 'NodeDown' target_match: severity: 'warning' equal: ['instance']第二条规则表示:当某个节点宕机时,该节点上的所有 warning 级别告警都抑制掉,因为它们大概率是节点宕机的连锁反应。告警静默:维护窗口免打扰计划内维护期间可以通过 API 创建静默规则,匹配到的告警不会发送通知:curl -X POST http://alertmanager:9093/api/v2/silences \ -H 'Content-Type: application/json' \ -d '{ "matchers": [ {"name": "cluster", "value": "prod-east", "isRegex": false} ], "startsAt": "2026-05-27T02:00:00Z", "endsAt": "2026-05-27T06:00:00Z", "createdBy": "ops-team", "comment": "Planned maintenance" }'也可以在 Alertmanager Web UI(默认 9093 端口)中可视化创建和管理静默规则。静默与抑制的区别:抑制是配置文件中静态定义的规则,随 Alertmanager 启动生效;静默是运行时动态创建的,适合临时场景,到期自动失效。全局配置与 resolve_timeoutAlertmanager 的全局配置中有一个容易忽略的参数 resolve_timeout:global: resolve_timeout: 5m含义是:如果 Alertmanager 在 5 分钟内没有收到某条告警的更新(即 Prometheus 不再推送该告警),就认为该告警已恢复。这个机制是告警自动恢复的兜底策略——正常情况下 Prometheus 会主动发送 resolved 事件,但如果 Prometheus 重启或网络中断,resolved 事件可能丢失,此时 resolve_timeout 就起作用了。生产环境建议根据告警的重要程度调整:关键告警可以设长一些(15m-30m),避免因短暂断连导致误报恢复。配置验证与常见问题修改 Alertmanager 配置后,用 amtool 检查语法:amtool check-config alertmanager.yml也可以用 amtool 测试路由匹配结果,确认一条告警会走哪个接收器:amtool config routes test --config.file alertmanager.yml \ severity=critical team=infra alertname=HighCPUUsage几个生产环境常见的坑:告警一直 Firing 不恢复:检查 for 时间是否过长,或 PromQL 表达式本身是否有问题;另外确认 resolve_timeout 是否合理收不到通知:确认 Prometheus 能连通 Alertmanager,检查路由匹配条件是否正确,用 amtool config routes test 验证通知太频繁:增大 repeat_interval,启用 group_by 合并同类告警静默未生效:确认 matchers 的标签名和值与告警标签完全一致,注意大小写敏感子路由不生效:检查是否因为前面的子路由已经匹配,后面被跳过了;需要同时匹配时加 continue: trueHA 部署下重复通知:确认多个 Alertmanager 实例之间网络互通,Gossip 协议正常同步
服务端阅读 05月27日 18:01

如何优化 Prometheus 的存储和性能?

Prometheus 存储架构基础Prometheus 使用自研的 TSDB(Time Series Database)作为本地存储引擎。数据写入流程为:先写入内存中的 Head Block,同时通过 WAL(Write-Ahead Log)保证持久性;Head Block 满两个时间窗口后被持久化为磁盘上的 Block;后台 Compaction 进程定期合并小 Block 并清理过期数据。每个 Block 由以下部分组成:chunks/:存储实际的时间序列数据点,使用 Facebook Gorilla 压缩算法,16 字节的数据点可压缩至平均 1.37 字节index:倒排索引,支持按标签快速查询时间序列meta.json:Block 元信息tombstones:删除标记,删除操作不会立即清除数据,而是记录标记等待下次 Compaction 时清理理解这个架构是进行存储优化的前提。数据保留策略配置通过启动参数控制本地数据的保留时间和磁盘上限:# prometheus.yml 或启动参数storage: tsdb: retention.time: 15d # 数据保留时长,默认 15d retention.size: 50GB # 磁盘使用上限,达到后自动清理最旧数据两个参数同时配置时,任一条件触发都会清理数据。生产环境建议同时设置,防止磁盘打满。关键原则:本地存储只保留近期热数据,长期存储需求交给 remote write 后端处理。标签基数控制标签基数(Label Cardinality)是影响 Prometheus 存储和查询性能最关键的因素。每一个唯一的标签组合都会产生一条独立的时间序列,基数爆炸会导致内存飙升、查询变慢、磁盘膨胀。必须避免的高基数标签:用户 ID、请求 ID、会话 ID原始 IP 地址未截断的 URL 路径# 使用 metric_relabel_configs 在采集阶段丢弃或重写高基数标签scrape_configs: - job_name: 'my-app' metric_relabel_configs: - source_labels: [__name__] regex: 'go_memstats_.*' action: drop # 丢弃不需要的指标 - source_labels: [path] regex: '/api/v1/.*' replacement: '/api/v1/:path' # 合并路径,降低基数 target_label: path排查高基数指标的方法:# 查看当前时间序列总数count({__name__=~".+"})# 按指标名分组统计序列数,找出最大的topk(20, count by (__name__)({__name__=~".+"}))采集间隔优化采集频率直接影响数据写入量和存储占用。合理分层设置采集间隔:global: scrape_interval: 30s # 全局默认值 scrape_timeout: 10sscrape_configs: - job_name: 'critical-service' scrape_interval: 15s # 核心服务用短间隔 scrape_timeout: 10s - job_name: 'batch-job' scrape_interval: 60s # 后台任务用长间隔 scrape_timeout: 15s注意事项:scrape_timeout 不能大于 scrape_interval采集间隔从 15s 改为 30s,存储量直接减半不重要的指标可以单独配置更长的间隔WAL 压缩与写入优化WAL(Write-Ahead Log)是数据持久性的保障,但默认未压缩时会占用大量磁盘空间,崩溃恢复也较慢。# 启用 WAL 压缩(Prometheus 2.20+ 默认开启)--storage.tsdb.wal-compression# 控制写入队列大小(Prometheus 2.29+)--storage.tsdb.head-chunks.write-queue-size=0 # 默认 0 表示同步写入# 设为非 0 值(如 1000)可异步写入,降低写入延迟但增加内存使用WAL 压缩使用 Snappy 算法,可将 WAL 体积缩减约 50%,CPU 开销极小。一旦开启,无法回退到 2.11 之前的版本。如果 WAL 异常增长,通常是 remote write 消费跟不上或 Compaction 失败导致,排查方法:# 检查 WAL 目录大小du -sh /data/prometheus/wal/# 检查 Compaction 是否正常curl -s http://localhost:9090/metrics | grep prometheus_tsdb_compactions_failed_totalRecording Rules 预计算对于复杂的聚合查询或频繁使用的仪表盘,Recording Rules 可以将计算结果预存为新指标,显著降低查询时的 CPU 和内存压力。groups: - name: http_request_rules interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: method:http_requests:rate5m expr: sum by (method) (rate(http_requests_total[5m])) - record: http:request_duration_seconds:p99 expr: histogram_quantile(0.99, sum by (le, job) (rate(http_request_duration_seconds_bucket[5m])))使用原则:仪表盘反复执行的聚合查询都应该转为 Recording Rule规则名称采用 level:metric:operations 的命名约定规则的 interval 不应小于全局 scrape_intervalRemote Write 调优Remote Write 是将 Prometheus 数据远程写入长期存储后端(Thanos、VictoriaMetrics、Mimir 等)的机制。配置不当会导致 WAL 堆积和内存溢出。remote_write: - url: 'http://thanos-receive:19291/api/v1/receive' queue_config: max_samples_per_send: 500 # 每批发送样本数 batch_send_deadline: 5s # 批次等待最大时间 max_shards: 100 # 最大并发分片数 min_shards: 1 # 最小分片数 capacity: 2500 # 队列容量 write_relabel_configs: - source_labels: [__name__] regex: 'go_.*' action: drop # 远端不需要的指标可在发送前丢弃调优要点:分片数(shards)根据吞吐量动态调整,max_shards 设为预估峰值即可如果远端持续写入失败超过 2 小时,WAL 会被 Compaction 截断,未发送数据将丢失监控关键指标:prometheus_remote_storage_samples_failed_total、prometheus_remote_storage_samples_pending查询性能优化使用标签过滤缩小范围# 差:全量扫描后过滤sum(http_requests_total) by (job)# 好:在查询时就限定范围sum(http_requests_total{job="api-server", env="prod"}) by (method)控制查询时间窗口大范围查询(如 30 天)会扫描大量 Block。建议:仪表盘默认显示 1-6 小时需要更长范围时使用 Recording Rules 预聚合的数据利用 API 的 step 参数控制返回点数避免高开销函数rate() 和 irate() 的区间选择不宜过长,通常 [5m] 或 [1m]避免对高基数指标使用 group_left 做 1:N 的 joinhistogram_quantile() 尽量配合 Recording Rules 预计算监控 Prometheus 自身生产环境必须对 Prometheus 自身进行监控和告警:# 关键监控指标- alert: PrometheusTSDBCompactionFailing expr: increase(prometheus_tsdb_compactions_failed_total[5m]) > 0- alert: PrometheusWALCorruptions expr: increase(prometheus_tsdb_wal_corruptions_total[5m]) > 0- alert: PrometheusRemoteWriteFailures expr: increase(prometheus_remote_storage_samples_failed_total[5m]) > 0- alert: PrometheusHighCardinality expr: prometheus_tsdb_head_series > 1000000同时关注以下运行指标:prometheus_tsdb_head_samples_appended_total:写入速率prometheus_target_interval_length_seconds:采集间隔偏差process_resident_memory_bytes:实际内存占用prometheus_tsdb_compaction_duration_seconds:压缩耗时长期存储与架构扩展单机 Prometheus 的本地存储有上限,大规模场景需要架构层面的扩展:Thanos 方案:Sidecar 模式对现有部署侵入最小,周期性将 Block 上传到对象存储Receive 模式支持多 Prometheus remote write 汇聚Store Gateway 提供对历史数据的查询能力VictoriaMetrics 方案:专有压缩算法,压缩比可达 Prometheus 的 10 倍单节点部署即可替代 Prometheus + 远端存储的组合完全兼容 PromQLGrafana Mimir 方案:支持 Multi-Tenant,适合平台级部署与 Grafana 生态深度集成选择建议:中小规模优先考虑 VictoriaMetrics,多租户平台选 Mimir,需要兼容现有对象存储选 Thanos。磁盘与硬件建议使用 SSD 存储 TSDB 数据目录,HDD 在高写入负载下 Compaction 性能极差磁盘剩余空间保持 30% 以上,Compaction 需要额外临时空间内存分配参考:每百万活跃时间序列约需 1-2 GB 内存Kubernetes 部署时建议设置合理的 requests 和 limits,避免 OOM Kill总结Prometheus 存储优化是一个从基数控制到架构扩展的系统性工程。核心思路是:在采集端过滤无用指标、控制标签基数;在存储端合理配置保留策略、启用 WAL 压缩;在查询端善用 Recording Rules、避免全表扫描;在架构层通过 remote write 实现冷热分离、长期存储。每个环节的优化都建立在对 TSDB 工作原理的理解之上,监控 Prometheus 自身的关键指标则是发现和预防问题的最后一道防线。
服务端阅读 05月27日 17:56

如何在 K8s 中部署 Prometheus 监控?

Prometheus 是 Kubernetes 生态中最主流的监控方案。本文覆盖 Helm 快速部署和 Prometheus Operator 生产级部署两种方式,并给出 ServiceMonitor 配置、自动发现、常用指标和排错要点。一、部署方式选择| 方式 | 适用场景 | 复杂度 ||------|----------|--------|| Helm + kube-prometheus-stack | 快速体验、测试环境 | 低 || Prometheus Operator | 生产环境、需要 CRD 管理 | 中 |两种方式都推荐部署到独立命名空间(如 monitoring),避免与业务负载混用。二、Helm 快速部署2.1 安装 kube-prometheus-stackkube-prometheus-stack 是社区维护的一体化 Chart,打包了 Prometheus、Alertmanager、Grafana 及常用 Exporter。# 添加仓库helm repo add prometheus-community https://prometheus-community.github.io/helm-chartshelm repo update# 安装到 monitoring 命名空间helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --create-namespace2.2 验证部署状态kubectl get pods -n monitoring# 预期看到 prometheus-operator、prometheus-prometheus、alertmanager、grafana 等 Pod 均为 Running2.3 访问 Grafana 仪表盘# 端口转发访问 Grafanakubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring# 浏览器打开 http://localhost:3000,默认账号 admin/prom-operator安装完成后 Grafana 已内置 Kubernetes 集群监控仪表盘,无需额外配置。三、Prometheus Operator 部署(生产推荐)Prometheus Operator 通过 CRD 管理 Prometheus 实例,无需手动维护配置文件,是生产环境的推荐方案。3.1 核心 CRD 说明| CRD | 作用 ||-----|------|| Prometheus | 定义 Prometheus 实例,指定副本数、资源限制、存储卷 || Alertmanager | 定义告警管理器实例 || ServiceMonitor | 声明式配置监控目标,按 Label 选择 Service || PodMonitor | 直接按 Pod Label 选择监控目标(跳过 Service) || PrometheusRule | 管理告警规则和记录规则 |3.2 部署 Operator# 使用 kubectl apply 安装 Operator 及 CRDkubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/main/example/rbac/prometheus-operator-deployment.yaml3.3 创建 Prometheus 实例apiVersion: monitoring.coreos.com/v1kind: Prometheusmetadata: name: k8s namespace: monitoringspec: replicas: 2 serviceAccountName: prometheus-k8s serviceMonitorSelector: matchLabels: team: frontend resources: requests: cpu: 500m memory: 1Gi limits: cpu: "2" memory: 4Gi storage: volumeClaimTemplate: spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 50Gi关键参数说明:replicas: 2 — 生产环境建议至少 2 副本实现高可用serviceMonitorSelector — 只匹配带有对应 Label 的 ServiceMonitorstorage — 必须配置 PersistentVolume,否则重启后数据丢失四、ServiceMonitor 配置监控目标ServiceMonitor 是 Operator 模式下配置采集目标的核心资源,通过 Label 选择器自动发现 Service。apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: my-app namespace: monitoring labels: team: frontend # 需与 Prometheus 的 serviceMonitorSelector 匹配spec: selector: matchLabels: app: my-app endpoints: - port: metrics interval: 30s path: /metrics namespaceSelector: matchNames: - default配置要点:labels.team 必须与 Prometheus CR 的 serviceMonitorSelector 匹配,否则不会被抓取namespaceSelector 指定从哪些命名空间发现 Serviceinterval 不宜设置过短(< 15s),避免对目标服务造成压力五、Kubernetes 自动发现除 ServiceMonitor 外,也可通过原生 Prometheus 配置实现 Pod 自动发现:scrape_configs: - job_name: 'kubernetes-pods' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port] action: replace target_label: __address__ regex: (.+) replacement: ${1}使用方式:在 Pod 的 annotation 中添加 prometheus.io/scrape: "true" 和 prometheus.io/port: "9090",Prometheus 即可自动采集。建议:Operator 模式下优先使用 ServiceMonitor,自动发现适用于无法修改 CRD 的场景。六、常用指标速查| 类别 | 指标名 | 用途 ||------|--------|------|| 容器 CPU | container_cpu_usage_seconds_total | 计算 CPU 使用率 || 容器内存 | container_memory_working_set_bytes | 实际使用内存(OOM 判定依据) || Pod 状态 | kube_pod_status_phase | Pod 运行状态统计 || 节点内存 | node_memory_MemAvailable_bytes | 节点可用内存 || 网络流量 | container_network_receive_bytes_total | 容器入站流量 |CPU 使用率计算示例(PromQL):rate(container_cpu_usage_seconds_total{container!="",pod!=""}[5m])七、生产环境注意事项资源限制必须设置 — Prometheus 内存占用随时间序列数增长,不设上限会 OOM 并影响节点上其他 Pod持久化存储不可省略 — 默认使用 emptyDir,Pod 重启数据全部丢失,必须配置 volumeClaimTemplate采集间隔不宜过短 — 15s 是下限推荐值,大规模集群建议 30s-60s使用 recording rules 降频 — 对高频查询的指标用 recording rule 预计算,减少实时查询压力告警规则分优先级 — P0 级告警走即时通知(PagerDuty/电话),P1/P2 走邮件/IM常见排错| 现象 | 原因 | 解决 ||------|------|------|| Target 显示 0/0 active | ServiceMonitor Label 不匹配 | 检查 team Label 是否与 Prometheus CR 的 selector 一致 || Prometheus Pod CrashLoopBackOff | 内存不足 | 增大 resources.limits.memory || 指标数据缺失 | 采集目标未暴露 /metrics | 检查 Service 端口和 annotation |
服务端阅读 05月27日 17:53

Prometheus 和 Zabbix、Nagios 监控系统有什么区别?

Prometheus 与 Zabbix、Nagios 是运维领域最常用的三类监控系统,但它们的设计哲学和适用场景截然不同。选错工具不仅浪费团队精力,还可能在故障发生时错过关键告警。本文从架构模型、数据存储、告警机制、适用场景四个维度拆解差异,帮你做出正确选择。架构模型:Pull vs Push 的根本分歧Prometheus 采用 Pull 模式,主动从目标拉取指标数据。这意味着每个被监控的服务只需暴露一个 /metrics 端点,Prometheus server 定时抓取即可。这种设计天然适合 Kubernetes 等动态环境——新 Pod 上线后自动被服务发现纳入监控,无需手动配置。Zabbix 支持 Push 和 Pull 混合模式。Agent 主动上报数据,也支持 Server 主动查询。这种灵活性使其能覆盖网络交换机、打印机等无法运行 Agent 的设备,通过 SNMP、ICMP 等协议被动采集。Nagios 以被动检查为核心,依赖插件执行检查命令并返回状态码(OK/WARNING/CRITICAL)。架构上属于"检查执行器"而非"数据采集器",不存储历史指标曲线,只记录状态变更事件。核心区别:Prometheus 关注"指标值是多少",Nagios 关注"状态是否正常",Zabbix 两者兼顾。数据存储:时序数据库 vs 关系型数据库Prometheus 内置时序数据库(TSDB),数据以时间序列形式压缩存储,查询效率极高,但单机存储容量有限(默认保留15天)。长期存储需配合 Thanos 或 Cortex 扩展。Zabbix 使用 MySQL/PostgreSQL 等关系型数据库,支持海量历史数据存储,适合需要长期趋势分析的场景。但数据量增长后查询性能会下降,需要定期做数据分区和归档。Nagios 不存储指标时序数据,仅保留状态变更日志。如果需要历史曲线,必须额外集成 PNP4Nagios 或 Graphite 等组件。查询与告警能力Prometheus 的 PromQL 是专为时序数据设计的查询语言,支持丰富的聚合、运算和预测函数。例如预测磁盘何时写满可以用 predict_linear() 函数,这在 Zabbix 和 Nagios 中难以实现。告警通过 Alertmanager 管理,支持告警分组、抑制、静默和路由分发。Zabbix 内置告警引擎,触发器表达式灵活,支持邮件、短信、Webhook 等多种通知渠道。可视化也内置,无需额外部署 Grafana。但查询能力相对有限,复杂分析需要写脚本或导出数据。Nagios 告警依赖插件返回值,配置依赖文件定义,修改告警阈值需要编辑配置文件并重载服务。缺少统一的告警管理平台,在大规模部署下维护成本高。可视化与服务发现Prometheus 自带简易 UI,生产环境普遍配合 Grafana 使用。服务发现原生支持 Kubernetes、Consul、DNS 等多种机制,新服务自动注册,无需人工干预。Zabbix 内置仪表板和拓扑图,开箱即用,对非技术用户更友好。自动发现功能也较为成熟,但配置复杂度高于 Prometheus 的声明式配置。Nagios 可视化能力最弱,界面风格停留在早期 Web 风格。社区有替代方案如 Check MK,但增加了技术栈复杂度。如何选择:基于场景的决策如果你的环境以 Kubernetes 和容器为主,Prometheus 是唯一的选择。CNCF 生态的监控组件(如 kube-state-metrics、node-exporter)都以 Prometheus 格式输出指标,集成零成本。如果需要监控传统数据中心——物理服务器、网络设备、存储阵列——Zabbix 更合适。它的 SNMP/IPMI 支持和内置可视化能显著降低运维门槛。如果你的监控需求简单,只需要知道"服务是否存活",Nagios 足够应对。它轻量、稳定,插件生态成熟,但扩展性差,不适合大规模或动态环境。混合方案在实践中很常见:Prometheus 负责容器和云原生应用的指标监控,Zabbix 负责传统基础设施,两者通过 Grafana 统一展示面板,Alertmanager 和 Zabbix 分别处理各自领域的告警。这种组合在中小团队中广泛使用。
服务端阅读 05月27日 17:51

Prometheus 安全认证怎么配置?Basic Auth 与 RBAC 实战

Prometheus 默认不启用认证,9090 端口一旦暴露,任何人都能访问 /metrics 和 /api/v1/query,造成监控数据泄露甚至配置被篡改。下面从 scrape 认证、服务端防护、K8s RBAC 三个层面说明如何配置 Prometheus 的安全认证和访问控制。一、Scrape 侧认证:让 Prometheus 访问受保护的 Target1.1 Basic Auth当 Target(如 pushgateway 或其他 exporter)启用了 Basic Auth 时,Prometheus 抓取时需携带用户名密码:scrape_configs: - job_name: 'pushgateway' basic_auth: username: admin password: <your-password> static_configs: - targets: ['localhost:9091']若需从文件读取密码,使用 password_file 替代 password,避免密钥明文写入配置。1.2 Bearer Token在 Kubernetes 环境中,Prometheus 使用 ServiceAccount Token 访问 kube-apiserver:scrape_configs: - job_name: 'kubernetes-apiservers' bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt kubernetes_sd_configs: - role: endpoints1.3 TLS 双向认证当 Target 要求客户端证书时:scrape_configs: - job_name: 'etcd' scheme: https tls_config: ca_file: /etc/prometheus/tls/ca.crt cert_file: /etc/prometheus/tls/client.crt key_file: /etc/prometheus/tls/client.key insecure_skip_verify: false二、服务端防护:保护 Prometheus 自身的 UI 和 APIPrometheus 从 2.24 版本开始支持内置 Basic Auth 和 TLS,通过 web.config.file 参数加载。2.1 生成 bcrypt 密码哈希# 安装工具apt-get install -y python3-bcrypt# 生成哈希python3 -c "import bcryptprint(bcrypt.hashpw(b'your-secure-password', bcrypt.gensalt()).decode())"# 输出类似:$2b$12$Wxn...2.2 编写 web-config.ymlbasic_auth_users: admin: '$2b$12$Wxn...' # 上一步生成的哈希tls_config: cert_file: /etc/prometheus/tls/cert.pem key_file: /etc/prometheus/tls/key.pem验证配置文件语法:promtool check web-config web-config.yml2.3 启动时加载prometheus --config.file=/etc/prometheus/prometheus.yml --web.config.file=/etc/prometheus/web-config.yml启动后访问 http://localhost:9090 将弹出 Basic Auth 登录框,未认证请求返回 401。2.4 Docker 部署示例# docker-compose.ymlservices: prometheus: image: prom/prometheus:v2.53.0 command: - '--config.file=/etc/prometheus/prometheus.yml' - '--web.config.file=/etc/prometheus/web-config.yml' volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./web-config.yml:/etc/prometheus/web-config.yml ports: - '9090:9090'2.5 Nginx 反向代理认证(适用 kube-prometheus-stack)当前 kube-prometheus-stack 不直接支持内置 Basic Auth,可通过 Ingress + Nginx 实现:apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: prometheus-ingress annotations: nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: prometheus-basic-auth nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'spec: rules: - host: prometheus.example.com http: paths: - path: / pathType: Prefix backend: service: name: prometheus-operated port: number: 9090创建对应的 Secret:htpasswd -c auth adminkubectl create secret generic prometheus-basic-auth --from-file=auth -n monitoring三、Kubernetes RBAC:限制 Prometheus 的访问范围3.1 创建 ServiceAccountapiVersion: v1kind: ServiceAccountmetadata: name: prometheus namespace: monitoring3.2 定义 Role(最小权限)apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: prometheus namespace: monitoringrules: - apiGroups: [''] resources: ['pods', 'services', 'endpoints'] verbs: ['get', 'list', 'watch']仅授权 monitoring 命名空间下的资源读取,不授予写权限和跨命名空间权限。3.3 绑定 Role 和 ServiceAccountapiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: prometheus namespace: monitoringroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: prometheussubjects: - kind: ServiceAccount name: prometheus namespace: monitoring3.4 ClusterRole(如需跨命名空间监控)apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: prometheusrules: - apiGroups: [''] resources: ['nodes', 'pods', 'services', 'endpoints'] verbs: ['get', 'list', 'watch'] - nonResourceURLs: ['/metrics'] verbs: ['get']---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: prometheusroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheussubjects: - kind: ServiceAccount name: prometheus namespace: monitoring四、Grafana 对接待认证的 Prometheus配置 Basic Auth 后,Grafana 数据源需同步修改:# grafana datasources configapiVersion: 1datasources: - name: Prometheus type: prometheus url: http://prometheus:9090 basicAuth: true basicAuthUser: admin secureJsonData: basicAuthPassword: your-secure-password editable: true五、安全加固清单| 措施 | 说明 ||------|------|| 启用 Basic Auth | 防止未授权访问 UI 和 API || 启用 TLS | 防止传输层窃听 || 网络策略隔离 | Kubernetes NetworkPolicy 限制 Pod 间访问 || 防火墙规则 | 仅允许 Grafana/Alertmanager 所在网段访问 9090 || 密钥不硬编码 | 使用 password_file 或 Kubernetes Secrets || 定期轮换密钥 | 每 90 天更换 Basic Auth 密码和 TLS 证书 || 审计日志 | 通过请求日志监控异常访问模式 || 及时更新版本 | 关注 Prometheus 安全公告,修补已知漏洞 |
服务端阅读 05月27日 17:50

Prometheus Recording Rules 和 Alerting Rules 怎么选?

Prometheus 支持两种规则类型:Recording Rules 和 Alerting Rules。两者都通过 PromQL 表达式定期评估,但用途完全不同。Recording Rules 用于预计算并持久化查询结果,Alerting Rules 用于监控指标并在满足条件时触发告警。理解两者的区别与联动方式,是写出高质量 Prometheus 规则的前提。Recording Rules:预计算提升查询性能Recording Rules 的核心作用是将复杂或高频的 PromQL 表达式预先计算好,把结果存为新的时间序列。这样仪表盘和查询直接读取预计算结果,无需每次实时计算。为什么需要 Recording Rules当 Dashboard 每隔几秒刷新一次,而背后是一个涉及大量时间序列聚合的 PromQL 表达式时,每次实时计算会给 Prometheus 带来明显压力。Recording Rules 把这种计算从每次查询时提前到定期评估时,查询变成简单的指标读取,速度大幅提升。另一个典型场景是跨团队共享指标。基础设施团队可以把基础聚合结果录制成新指标,应用团队直接基于这些录制指标构建自己的查询,避免重复计算。配置示例groups: - name: api_recording_rules interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: job:request_errors:rate5m expr: sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))interval: 30s 表示该组规则每 30 秒评估一次。每条规则的 record 字段指定新指标的名称,expr 字段定义计算表达式。评估完成后,job:http_requests:rate5m 和 job:request_errors:rate5m 就像普通指标一样可以被查询和引用。命名规范Recording Rules 的命名直接影响可读性和可维护性。推荐遵循 level:metric:operations 的格式:level:聚合维度,如 job、instance、clustermetric:原始指标名称,如 http_requests、request_errorsoperations:计算方式,如 rate5m、sum、avg例如 job:http_requests:rate5m 表示按 job 聚合的 HTTP 请求 5 分钟速率。保持一致的命名规范,能让团队快速理解每条录制指标的含义。Alerting Rules:监控指标触发告警Alerting Rules 用于定义告警条件。当 PromQL 表达式的结果满足条件时,Prometheus 会生成告警并推送到 Alertmanager,由 Alertmanager 负责分组、抑制、静默和通知路由。配置示例groups: - name: api_alerting_rules rules: - alert: HighErrorRate expr: job:request_errors:rate5m / job:http_requests:rate5m > 0.05 for: 5m keep_firing_for: 10m labels: severity: critical annotations: summary: "High error rate on {{ $labels.job }}" description: "Error rate is {{ $value | humanizePercentage }}"关键字段说明:alert:告警名称,需在同一个 group 内唯一expr:PromQL 表达式,结果非零即视为触发。这里引用了前面 Recording Rules 生成的指标for:条件持续满足多久后才真正触发告警。比如 for: 5m 意味着错误率连续 5 分钟超过 5% 才告警,避免瞬时抖动导致的误报keep_firing_for:告警触发后,即使条件恢复,仍继续保持 firing 状态一段时间,防止告警频繁闪烁labels:附加到告警的标签,用于 Alertmanager 的路由和分组annotations:告警的描述信息,支持模板变量,在通知中展示与 Alertmanager 的集成Prometheus 自身只负责生成告警,通知的分组、去重、路由由 Alertmanager 完成。在 prometheus.yml 中配置 Alertmanager 地址:alerting: alertmanagers: - static_configs: - targets: - 'alertmanager:9093'Alertmanager 根据告警的 labels 决定路由到哪个接收方(邮件、Slack、PagerDuty 等),并支持抑制(inhibit)和静默(silence)策略。核心区别| 特性 | Recording Rules | Alerting Rules ||------|----------------|----------------|| 目的 | 预计算查询结果,存为新时间序列 | 监控指标,满足条件时触发告警 || 输出 | 生成新的时间序列数据 | 生成告警记录,推送至 Alertmanager || 存储 | 结果写入 TSDB | 不写入新时间序列 || 性能影响 | 减少实时查询压力,提升 Dashboard 速度 | 规则评估本身有开销,但告警推送开销极小 || 典型消费者 | Dashboard、其他规则、Ad-hoc 查询 | Alertmanager、通知渠道 || 关键参数 | record、expr、interval | alert、expr、for、labels、annotations |一个容易忽略的区别:Recording Rules 的结果是持久化的时间序列,可以通过范围查询获取历史数据;Alerting Rules 的告警状态是瞬时的,一旦恢复就不再存在。两者联动:用 Recording Rules 为 Alerting Rules 服务这是实际生产中最常见的模式:先用 Recording Rules 预计算复杂指标,再让 Alerting Rules 引用这些预计算结果。这样做的好处:告警规则的 expr 更简洁易读,降低维护成本预计算结果可复用,同一组录制指标能支撑多个告警规则避免 Alerting Rules 在评估时执行复杂计算,减少评估超时风险录制指标同时可用于 Dashboard 展示,一套计算多处使用上面的示例中,HighErrorRate 告警规则直接引用了 job:request_errors:rate5m 和 job:http_requests:rate5m 两个录制指标,expr 只需做一次简单除法,清晰且高效。规则管理实践规则文件组织将 Recording Rules 和 Alerting Rules 分文件管理,便于维护:# prometheus.ymlrule_files: - /etc/prometheus/rules/recording/*.yml - /etc/prometheus/rules/alerting/*.yml同一 group 内的规则按顺序评估,共享相同的评估时间戳。不同 group 之间并行评估。语法验证部署前用 promtool 检查规则文件语法:promtool check rules /etc/prometheus/rules/*.yml这能捕获缩进错误、无效的 PromQL 表达式、缺少必填字段等常见问题。评估性能监控Prometheus 自身暴露了规则评估的指标,关注以下两个:prometheus_rule_evaluation_duration_seconds:规则评估耗时prometheus_rule_group_last_duration_seconds:规则组评估耗时如果某个 group 的评估耗时持续超过其 interval,需要考虑拆分 group 或优化表达式。级联告警设置为规则文件设置 limit,防止单个 group 产生过多序列或告警拖垮 Prometheus:groups: - name: api_alerting_rules limit: 100 rules: # ...当序列或告警数量超过 limit 时,该 group 内所有规则产出的数据都会被丢弃,这是一种保护机制,需要在监控中对 limit 触发进行告警。
服务端阅读 05月27日 17:49

Prometheus 生产环境怎么做?从架构到告警的实战经验

Prometheus 上线跑起来不难,难的是跑稳、跑快、不漏报不误报。这篇文章把生产环境踩过的坑和验证过的方案整理出来,覆盖架构选型、指标设计、告警治理、性能调优和安全加固五个方面。架构选型:单实例扛不住怎么办单实例 Prometheus 在万级指标以下完全够用,问题出在规模增长之后。内存通常先成为瓶颈——Prometheus 每 2 小时将内存中的 TSDB 数据落盘一次,落盘前所有数据都在内存里,查询范围越大内存占用越高。什么时候该拆分:当单个实例的 prometheus_tsdb_head_series 超过 500 万,或者 P95 查询延迟超过 2 秒,就该考虑分片或联邦了。长期存储方案对比:| 方案 | 适用场景 | 优点 | 缺点 ||------|----------|------|------|| Thanos Sidecar | 已有 Prometheus,需要全局查询 | 改动最小,兼容现有部署 | Sidecar 和 Prometheus 同生命周期,单点风险仍在 || Thanos Receive | 写入量大、需要实时远程写入 | 解耦采集和存储,支持多租户 | 架构复杂度高,依赖对象存储 || Cortex/Mimir | 超大规模、多租户 | 完全分布式,读写分离 | 运维成本最高,组件多 |生产环境中 Thanos 是最主流的选择,原因很简单:渐进式迁移——先加 Sidecar 实现全局查询,再按需加 Receive 和 Compactor,不需要一步到位。资源规划的经验值:每 100 万 active series 大约需要 4GB 内存和 2 核 CPU,预留 50% 余量应对突发。数据保留策略建议本地保留 15 天(--storage.tsdb.retention.time=15d),长期数据通过 Thanos 写入对象存储。指标设计:90% 的问题出在标签上指标命名和标签设计直接决定查询效率和告警质量。命名三原则:下划线分隔、包含应用名、用标准单位后缀(_bytes、_seconds、_total)。例如 http_request_duration_seconds_sum 比 request_time 清晰得多。标签的最大坑是高基数。什么是高基数?一个标签的取值超过 1 万种就是高基数。最常见的踩坑:用 user_id 做标签——每个用户一个标签值,10 万用户就是 10 万 cardinality把请求参数放进标签——path="/api/user/123" 每个路径一个值用 container_id 做标签——容器重建后 ID 变化,基数无限增长高基数标签不会让 Prometheus 崩溃,但会让它变慢。prometheus_tsdb_head_series 暴涨、查询超时、OOM killer 介入,往往根因就是一个高基数标签。检测方法:用 topk(10, count by (__name__) ({__name__=~".+"})) 找出基数最高的指标,逐个排查是否该用日志而非指标来承载。四种指标类型的选择逻辑:Counter(只增不减):请求总数、错误总数。查询时用 rate() 或 increase() 计算速率Gauge(可增可减):当前内存、CPU 使用率、队列深度。直接查询当前值Histogram(客户端分桶):延迟、响应大小。用 histogram_quantile() 计算 P50/P95/P99Summary(客户端计算分位数):和 Histogram 类似但分位数在客户端预计算,不可聚合。生产环境优先用 Histogram一个常见误区:对 Counter 用 avg() 而不是 rate()——Counter 是累计值,直接取平均毫无意义。告警治理:少即是多告警过多比没有告警更危险,因为人们会对噪音免疫,真正严重的问题被淹没。分级策略:P0/Critical:需要立即响应,比如服务完全不可用、数据丢失。通知方式:电话 + PagerDutyP1/Warning:需要关注但不致命,比如磁盘使用率 80%、延迟上升 50%。通知方式:Slack/飞书P2/Info:仅记录,不需要人工干预。通知方式:日志抑制规则:同一实例的 Critical 告警应该抑制 Warning 告警,避免雪崩式通知:inhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'instance']告警路由的实用配置:route: group_by: ['alertname', 'cluster'] group_wait: 30s # 首次告警等待 30s 聚合同组 group_interval: 5m # 同组新告警间隔 5 分钟 repeat_interval: 4h # 未恢复的告警 4 小时重发一次 receiver: 'slack-default' routes: - match: severity: critical receiver: 'pagerduty' repeat_interval: 1h # Critical 告警 1 小时重发告警规则的常见反模式:用绝对值而非比率做阈值——memory_usage_bytes > 8589934592 在不同规格机器上完全不同,应该用 memory_usage_bytes / memory_limit_bytes > 0.9缺少 for 持续时间——瞬时抖动就触发告警,应该加 for: 5m 确认问题持续告警条件太紧——for: 1m 加上 99% 阈值,每周都在误报性能调优:让 Prometheus 跑得快采集优化:采集间隔不要一刀切。基础设施指标 15s 采集一次足够,业务指标可以 30s 甚至 60s用 metric_relabel_configs 在采集端过滤不需要的指标,而不是在查询时过滤:scrape_configs: - job_name: 'myapp' metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*' action: drop # 丢弃所有 go_ 开头的运行时指标Recording Rules 预计算高频查询,把耗时 10 秒的 PromQL 变成毫秒级:groups: - name: api_performance interval: 30s rules: - record: api:request_duration_seconds:p99 expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, method))查询优化:限制查询时间范围——不要在 Grafana 面板里默认查 30 天,大范围查询用 Thanos Query用标签过滤缩小数据集——http_requests_total{method="GET"} 比 http_requests_total 快得多避免 group_left 和大范围 rate() 组合查询,这是内存杀手存储优化:--storage.tsdb.wal-compression 开启 WAL 压缩,减少磁盘占用定期用 promtool tsdb analyze 检查高基数指标和标签本地 SSD 是底线,机械硬盘上 Prometheus 基本不可用监控 Prometheus 自身:# 是否存活up{job="prometheus"}# 采集延迟——超过 0 说明采集不过来prometheus_target_scrape_pool_sync_length_seconds:quantile# 规则评估耗时——超过 1 秒需要优化prometheus_rule_evaluation_duration_seconds:quantile# WAL 重放耗时——重启后恢复时间prometheus_tsdb_wal_replay_duration_seconds安全加固:默认配置是裸奔Prometheus 默认没有任何认证机制,直接暴露在网络上等于裸奔。基本认证:用 basic_auth_users 配置用户名密码,密码用 bcrypt 哈希而不是明文:# 生成 bcrypt 哈希htpasswd -nbBC 10 admin '' | tr -d ':\n' | sed 's/$2y/$2a/'basic_auth_users: admin: '$2a$10$...' # bcrypt 哈希,不是明文密码TLS 加密:Prometheus 原生不支持 TLS,需要用反向代理(Nginx/Envoy)或者在 Prometheus 前面加一个 oauth2-proxy。网络隔离:Kubernetes 环境下用 NetworkPolicy 限制只有 Grafana 和 Alertmanager 能访问 Prometheus不要把 Prometheus 端口暴露到公网使用 Service Mesh(如 Istio)的 mTLS 自动加密内部流量配置管理:所有配置文件纳入 Git 版本控制,变更走 PR 审核流程用 Prometheus Operator 或 kube-prometheus-stack 管理配置,而不是手动修改 YAMLSecret 不要提交到 Git,用 Sealed Secrets 或 External Secrets Operator 管理写在最后生产环境的 Prometheus 运维核心就三件事:控制基数、治理告警、监控自身。架构选型根据规模渐进式升级,不要一上来就上 Thanos 全家桶;指标设计阶段就要想清楚标签的取值范围,高基数标签事后改的成本远高于事前规划;告警宁缺毋滥,P0 告警必须是"需要立刻爬起来处理"的级别。
服务端阅读 05月27日 17:49

Prometheus Remote Write 和 Remote Read 怎么配置?

Prometheus 自带的本地 TSDB 存储有容量和时效的限制,当数据量增长或需要跨集群查询时,就要借助 Remote Write 和 Remote Read 将数据外接到远程存储。这两个接口是 Prometheus 官方定义的标准协议,所有兼容的存储后端都能无缝对接。Remote Write 的工作流程Prometheus 采集到指标后,样本先写入本地 WAL(预写日志),Remote Write 组件从 WAL 中批量读取新样本,经过队列缓冲和分片后,以 Protobuf 编码通过 HTTP POST 发送到远端端点。整个过程是异步的,即使远端暂时不可用,队列也会按退避策略重试,不会阻塞采集。典型配置:remote_write: - url: "http://remote-storage:9201/api/v1/write" basic_auth: username: "user" password: "pass" queue_config: capacity: 10000 max_shards: 50 min_shards: 1 max_samples_per_send: 1000 batch_send_deadline: 5s min_backoff: 30ms max_backoff: 100ms write_relabel_configs: - source_labels: [__name__] regex: 'expensive_.*' action: drop队列参数解析:capacity — 内存队列中缓存的样本上限,超出会阻塞写入max_shards / min_shards — 动态分片的上下限,分片越多吞吐越高,但内存和 CPU 开销也越大max_samples_per_send — 每次请求发送的最大样本数,值越大吞吐越高,但单次失败代价也更大batch_send_deadline — 即使样本数未达到 max_samples_per_send,超过此时间也会强制发送min_backoff / max_backoff — 发送失败后的重试退避范围,避免雪崩式重试write_relabel_configs 可以在发送前过滤或改写标签,比如丢弃 expensive_ 前缀的高基数指标,减少远程存储的压力。Remote Read 的工作流程当用户在 Prometheus UI 或 Grafana 发起查询时,如果本地 TSDB 没有对应数据,Prometheus 会向 remote_read 端点发送 Protobuf 编码的查询请求,远端存储返回匹配的时间序列样本,Prometheus 将其与本地数据合并后返回给用户。典型配置:remote_read: - url: "http://remote-storage:9201/api/v1/read" read_recent: true basic_auth: username: "user" password: "pass"read_recent: true 表示优先从本地读取近期数据,只向远端请求本地没有的历史数据,能显著降低查询延迟。设为 false 则所有数据都从远端获取。主流远程存储后端| 后端 | 特点 ||------|------|| Thanos | 基于 OSS/S3 的长期存储,支持全局查询视图,社区活跃 || VictoriaMetrics | 高性能压缩,兼容 Remote Write 协议,部署简单 || Cortex | 多租户架构,适合大规模集群,依赖组件较多 || M3DB | 分布式时序数据库,Uber 开源,擅长高基数场景 || InfluxDB | 生态成熟,支持多种写入协议 || TimescaleDB | 基于 PostgreSQL,适合已有 PG 运维经验的团队 |常见问题与最佳实践内存增长:开启 Remote Write 后 Prometheus 内存通常增加约 25%,因为需要在内存中缓存序列 ID 到标签值的映射。建议监控 prometheus_remote_storage_queue_length,如果队列持续积压,需要调大 max_shards 或检查网络。数据丢失风险:如果 Prometheus 异常退出且 WAL 未及时同步,极端情况下可能丢失少量数据。确保 queue_config 中的重试和退避参数合理配置。性能监控指标:prometheus_remote_storage_queue_length — 当前队列深度prometheus_remote_storage_failed_samples_total — 发送失败累计次数prometheus_remote_storage_succeeded_samples_total — 发送成功累计次数prometheus_remote_storage_highest_timestamp_in_seconds — 已成功发送的最大时间戳标签过滤:始终用 write_relabel_configs 过滤不需要的高基数指标,避免远程存储膨胀和查询变慢。网络压缩:Remote Write 默认使用 Snappy 压缩,部分后端也支持 gzip。确保远端支持对应压缩格式以减少带宽占用。配置思路总结先确定长期存储后端(VictoriaMetrics 入门最简单)配置 remote_write,用 write_relabel_configs 过滤高基数指标按实际吞吐调整 queue_config,从小分片起步逐步扩容配置 remote_read 并开启 read_recent: true在 Grafana 中添加 Remote Write 相关的面板,持续监控队列和失败率
服务端阅读 05月27日 17:44

Prometheus 指标类型怎么选?Counter Gauge Histogram Summary 区别与用法

Prometheus 监控的核心不是采集数据,而是用对指标类型。选错了类型,要么查询结果不准,要么聚合不出你想要的维度。Counter、Gauge、Histogram、Summary 四种类型各有明确的使用场景,搞清楚它们的差异是写出可靠监控规则的第一步。Counter:只增不减的累计值Counter 最简单的理解:出租车计价器。数字只会往上走,不会倒退(除非进程重启归零)。典型场景:HTTP 请求总数、错误发生次数、任务完成数。这些值天然只会累加。# 计算 QPS——最近 5 分钟每秒平均请求数rate(http_requests_total[5m])# 计算增长量——最近 1 小时新增了多少请求increase(http_requests_total[1h])Counter 的查询几乎离不开 rate() 或 increase()。直接看 Counter 的原始值意义不大——"总共处理了 100 万个请求"本身不能告诉你系统现在健不健康,但"每秒处理 500 个请求"就能。定义 Counter 指标的方式:var httpRequestCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests", }, []string{"method", "path", "status"},)注意 NewCounterVec 的 Vec 后缀——它支持按 label 拆分维度(按方法、路径、状态码分别统计),这在实战中非常常用。容易踩的坑:把本该用 Gauge 的值(比如当前在线用户数)定义成 Counter。在线用户数会减,Counter 不能减,强行用 Counter 记录会导致 rate() 计算出负值,图表出现锯齿状异常。Gauge:可增可减的瞬时值Gauge 像温度计——当前多少就是多少,可以升高也可以降低。典型场景:当前内存使用量、CPU 使用率、队列深度、在线连接数。这些值的"当前值"本身就有意义,不需要算变化率。# 直接看当前值node_memory_available_bytes# 看过去 1 小时的峰值max_over_time(node_memory_available_bytes[1h])# 看过去 30 分钟的平均值avg_over_time(node_cpu_seconds_total[30m])Gauge 用 max_over_time、min_over_time、avg_over_time 这类函数。注意:不要对 Gauge 用 rate()——rate() 计算的是增长率,而 Gauge 的值波动本身不表示增长趋势,算出来的结果没有意义。var tempGauge = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "room_temperature_celsius", Help: "Current room temperature", }, []string{"room"},)// 设置值tempGauge.WithLabelValues("server-room").Set(23.5)Gauge 可以用 Set() 直接设置任意值,也可以用 Inc() / Dec() 增减,这是和 Counter 的本质区别。Histogram:服务端算分位数的分布当你需要知道"95% 的请求在多长时间内完成"时,就需要 Histogram。Histogram 的工作原理:定义一组 bucket(桶的边界),每个观测值落入对应的桶中。比如配置 bucket 为 [0.1, 0.5, 1, 2.5, 5, 10],一个耗时 0.3 秒的请求会同时被计入 le=0.5、le=1、le=2.5… 所有比它大的桶里。var requestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "HTTP request duration", Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10}, }, []string{"method"},)Histogram 会自动产生三个指标:http_request_duration_seconds_bucket{le="0.5"} — 落入各桶的累计计数http_request_duration_seconds_sum — 所有观测值之和http_request_duration_seconds_count — 观测总次数用 histogram_quantile 在服务端计算分位数:# P95 延迟histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))Histogram 的关键优势:可以聚合。多个实例的 bucket 数据可以在 Prometheus 服务端合并计算,适合分布式系统。代价是分位数值是近似值(受 bucket 粒度影响),bucket 配得太粗会导致误差大。bucket 怎么配:先观察数据的实际分布,再设定桶边界。初始可以用 Prometheus 默认的桶(适合 Web 请求延迟),上线后根据 P99 的实际范围调整。桶的数量建议 8-12 个,太少了精度差,太多了存储膨胀。Summary:客户端算分位数的精确值Summary 和 Histogram 解决同样的问题——算分位数,但计算位置不同:Summary 在客户端(你的应用里)直接算好分位数再上报。var rpcDuration = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Name: "rpc_duration_seconds", Help: "RPC call duration", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }, []string{"service"},)Objectives 定义了你关心哪些分位数及可接受的误差。0.9: 0.01 表示 P90 的误差在 1% 以内。Summary 产出的指标:rpc_duration_seconds{quantile="0.9"} — P90 延迟(客户端已算好)rpc_duration_seconds_sum — 总和rpc_duration_seconds_count — 总次数直接查询即可:# P99 延迟,无需 histogram_quantilerpc_duration_seconds{quantile="0.99"}但 Summary 有个致命限制:不能跨实例聚合。你有 10 个 Pod,每个 Pod 自己算了 P99,你没法把这 10 个 P99 合并成全局 P99。数学上,分位数的分位数不是总体的分位数。所以 Summary 只适合单实例场景,或者你只关心每个实例自己的延迟分布。Histogram 还是 Summary?这是面试和实战中最高频的问题,核心差异就一点:分位数在哪里算。| 对比项 | Histogram | Summary ||--------|-----------|---------|| 分位数计算位置 | Prometheus 服务端 | 应用客户端 || 可聚合 | 可以(跨实例合并计算) | 不可以 || 精确度 | 近似值(受 bucket 粒度影响) | 可控误差(由 Objectives 设定) || 客户端开销 | 低(只计数) | 较高(需要维护分位数流式计算) || 适用场景 | 分布式系统、多实例 | 单实例、需要精确分位数 |选择逻辑很简单:多实例分布式系统用 Histogram,单实例或需要精确值用 Summary。大多数生产环境选 Histogram,因为可聚合是刚需。四种类型怎么选决策路径:先问"这个值是只增不减的吗"——是就用 Counter,不是就问"当前瞬时值本身有意义吗"——有意义用 Gauge,没意义再问"需要算分位数吗"——需要就问"要多实例聚合吗"——要聚合用 Histogram,不要用 Summary。举几个实战中的对应关系:API 请求总数 → Counter(只增不减)当前活跃连接数 → Gauge(可增可减,当前值有意义)请求延迟分布 → Histogram(需要分位数 + 聚合)单进程内部方法耗时 → Summary(单实例精确分位数)Prometheus 服务端本身其实不区分这四种类型——所有时间序列在服务端都是一样的存储格式。类型信息只存在于客户端库中,目的是约束你用正确的 API 记录数据、用正确的函数查询数据。选对类型,查询才有意义;选错了,数据采集了也用不上。
服务端阅读 05月27日 17:42

Prometheus 性能优化与水平扩展怎么做?

Prometheus 单实例在百万级时间序列下会出现采集延迟、查询超时、OOM 等问题。本文从采集、存储、查询三个维度给出调优手段,再对比 Thanos、Mimir、VictoriaMetrics 三种水平扩展方案的选型建议。采集优化分级采集间隔根据业务重要性对 Target 设置不同的采集间隔。核心服务 15 秒,普通服务 30-60 秒,批量任务 120 秒。将全局 scrape_interval 从 15s 放宽到 30s,采样量直接减半。global: scrape_interval: 30sscrape_configs: - job_name: critical-service scrape_interval: 15s - job_name: batch-jobs scrape_interval: 120s过滤高基数指标高基数标签(如 userid、containerid)会令时间序列数量爆炸。用 metricrelabelconfigs 在采集端丢弃不需要的指标,从源头控制数据量。metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*|process_.*' action: drop - source_labels: [__name__] regex: 'container_.*_seconds_.*' action: drop同时设置 samplelimit 和 targetlimit 作为安全阀,防止误配置导致指标暴增。scrape_configs: - job_name: api sample_limit: 10000 target_limit: 100水平分片采集当单实例采集能力不足时,用 hashmod relabel 将 Target 分散到多个 Prometheus 实例,每个实例只抓取一部分。global: external_labels: scraper: '1'scrape_configs: - job_name: sharded relabel_configs: - source_labels: [__address__] modulus: 4 target_label: __tmp_hash action: hashmod - source_labels: [__tmp_hash] regex: '1' action: keepmodulus 设为 4 表示 4 个分片,每个实例 regex 匹配自己的编号。配合 external_labels 标识来源,后续通过 Thanos 或 Mimir 聚合全局视图。存储优化TSDB 调优开启 WAL 压缩和内存映射写入,降低磁盘 I/O 压力。--storage.tsdb.wal-compression=true--storage.tsdb.wal-segment-size=20MB--storage.tsdb.memory-map-on-write=true控制数据保留期,避免磁盘写满。生产环境通常 15-30 天,长期数据交给对象存储。--storage.tsdb.retention.time=15d--storage.tsdb.retention.size=80GBRemote Write 异步写入将数据异步写入远端存储,Prometheus 本地只保留短期热数据。建议在 Remote Write 前加一层缓存队列(如 Prometheus本身的 queue 配置),避免远端慢写入拖垮采集。remote_write: - url: http://thanos-receive:19291/api/v1/receive queue_config: max_samples_per_send: 10000 capacity: 50000 max_shards: 50查询优化Recording Rules 预计算将高频、耗时的 PromQL 查询预计算为 Recording Rule,Dashboard 和告警直接查询预计算结果,查询耗时从秒级降到毫秒级。groups: - name: precompute interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: namespace:cpu:usage expr: sum by (namespace) (rate(container_cpu_usage_seconds_total[5m]))查询参数调优--query.max-samples=50000000--query.timeout=2m--query.lookback-delta=5mmax_samples 防止单次查询扫过多数据;lookback-delta 控制无数据时的回看窗口,调小可减少扫描量。水平扩展方案选型单实例垂直扩展有上限。当时间序列超过 200 万、查询 QPS 超过 50、存储保留期超过 30 天时,需要引入水平扩展方案。方案对比| 维度 | Thanos | Grafana Mimir | VictoriaMetrics ||------|--------|---------------|-----------------|| 架构模式 | Sidecar 附加现有 Prometheus | 独立分布式集群 | 单节点或集群模式 || 迁移成本 | 最低,Sidecar 无侵入 | 需要部署完整微服务栈 | 中等,改 remote_write 地址即可 || 长期存储 | 支持 S3/GCS 等对象存储 | 支持 S3/GCS/Azure | 支持 S3/GCS || 多租户 | 基本支持 | 强隔离,适合平台团队 | 有限支持 || 全局查询 | Querier 聚合多实例 | Query Frontend + Distributor | vmselect 聚合 || 运维复杂度 | 中等(4-5 个组件) | 高(10+ 微服务) | 低(1-3 个组件) || 适用规模 | 中大型,已有 Prometheus | 大型企业,需多租户 | 中小型到中大型 || 存储效率 | 2-4 倍压缩 | 中等 | 5-10 倍压缩,最省磁盘 |Thanos 方案Thanos 通过 Sidecar 组件附着在现有 Prometheus 上,将 TSDB 块上传到对象存储实现长期保留,Querier 提供跨实例全局查询。适合已有 Prometheus 部署、希望低摩擦迁移的团队。核心组件:Sidecar(上传数据块)、Store Gateway(查询历史数据)、Compactor(压缩和降采样)、Querier(全局查询)、Query Frontend(查询缓存和分片)。Grafana Mimir 方案Mimir 是 Cortex 的继任者,提供完全分布式的多租户 Prometheus 兼容存储。2025 年底发布 v3.0,引入 Kafka 解耦架构。适合有平台团队、需要服务多业务线的大企业。注意:新部署不应选择 Cortex,直接用 Mimir。VictoriaMetrics 方案VictoriaMetrics 兼容 Prometheus API,单二进制部署即可运行。存储效率比原生 Prometheus 高 5-10 倍,资源占用低。500 节点 K8s 集群仅需 2-4 GB 内存,而 Prometheus 需要 8-16 GB。适合追求简单高效、中小规模到中大规模的团队。联邦架构联邦是 Prometheus 原生功能,用于层级化监控。子 Prometheus 采集各区域数据,父 Prometheus 通过 /federate 端点拉取聚合指标。scrape_configs: - job_name: federate scrape_interval: 15s honor_labels: true metrics_path: /federate params: match[]: - '{__name__=~"job:.*"}' static_configs: - targets: - prometheus-region-a:9090 - prometheus-region-b:9090联邦适合多数据中心、多区域的层级聚合场景,不建议用作主要水平扩展手段——它只拉取聚合结果,原始数据仍在子实例上。自身监控监控 Prometheus 自身的关键指标,及时发现瓶颈:# 采集速率rate(prometheus_tsdb_head_samples_appended_total[5m])# 查询延迟histogram_quantile(0.9, rate(prometheus_query_duration_seconds_bucket[5m]))# WAL 写入滞留rate(prometheus_tsdb_wal_writes_failed_total[5m])# 磁盘使用prometheus_tsdb_storage_blocks_bytes建议对这些指标设置告警:采集速率突降、P90 查询超过 10 秒、WAL 写入失败、磁盘使用超 80%。容量规划参考| 规模 | 活跃时间序列 | 内存 | CPU | 磁盘(30天) | 建议 ||------|-------------|------|-----|-------------|------|| 小型 | 1000 万 | 分布式 | 分布式 | 对象存储 | Mimir 分布式集群 |要点总结采集端通过分级间隔、过滤高基数指标和水平分片控制数据入口;存储端通过 WAL 压缩、内存映射和 Remote Write 分离冷热数据;查询端用 Recording Rules 预计算并调优查询参数。超出单实例能力时,中小团队首选 VictoriaMetrics 或 Thanos,大型企业选 Mimir。联邦架构用于多区域层级聚合,不替代水平扩展。持续监控 Prometheus 自身指标,在瓶颈出现前提前扩容。
服务端阅读 05月27日 17:41

Prometheus 支持哪些服务发现机制?如何配置?

Prometheus 支持多种服务发现机制,用于在动态环境中自动发现监控目标,无需手动维护目标列表。下面逐一说明各机制的原理、配置方式与适用场景。静态配置静态配置是最简单的方式,直接在 prometheus.yml 中写死目标地址,适合小规模或目标固定的场景:scrape_configs: - job_name: 'node' static_configs: - targets: ['192.168.1.10:9100', '192.168.1.11:9100'] labels: datacenter: 'dc1'目标变动时需要重启或 reload Prometheus,不适合频繁变化的动态环境。基于文件的服务发现filesdconfigs 通过读取外部 JSON 或 YAML 文件获取目标列表,Prometheus 会监听文件变化并自动更新:scrape_configs: - job_name: 'file' file_sd_configs: - files: - '/etc/prometheus/targets/*.json' refresh_interval: 5mJSON 文件格式示例:[ { "targets": ["10.0.0.1:9100", "10.0.0.2:9100"], "labels": {"env": "prod"} }]这种方式适合由外部脚本或 CMDB 系统定期生成目标列表的场景,Prometheus 不需要重启。Kubernetes 服务发现Kubernetes 是 Prometheus 最常用的部署环境,通过 kubernetessdconfigs 直接调用 Kubernetes API 发现集群资源:scrape_configs: - job_name: 'kubernetes-pods' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__meta_kubernetes_pod_ip] action: replace target_label: __address__ replacement: $1:9104Kubernetes 角色类型| 角色 | 发现资源 | 典型场景 ||------|---------|---------|| pod | 所有 Pod | 直接采集 Pod 指标 || service | 所有 Service | 采集 Service 背后端点 || endpoints | Service 的 Endpoints | 最常用,配合注解使用 || endpointslices | EndpointSlices | K8s 1.21+ 替代 Endpoints || node | 所有 Node | 采集节点级指标 || ingress | 所有 Ingress | 监控 Ingress 延迟 |通过注解控制采集在 Pod 模板中添加注解即可被 Prometheus 自动发现:metadata: annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/metrics"RBAC 权限Prometheus 需要 ClusterRole 权限才能访问 K8s API:rules: - apiGroups: [""] resources: - nodes - services - endpoints - pods verbs: ["get", "list", "watch"]命名空间过滤限制发现范围,只采集特定命名空间的目标:kubernetes_sd_configs: - role: pod namespaces: names: - production - stagingPrometheus Operator 的 CRD 方式在 Kubernetes 环境中,Prometheus Operator 提供了更声明式的方式,通过 ServiceMonitor、PodMonitor 和 Probe 三种 CRD 定义采集目标,无需直接编写 relabel 配置:apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: my-appspec: selector: matchLabels: app: my-app endpoints: - port: metrics path: /metrics interval: 30sOperator 自动将这些 CRD 转换为 Prometheus 的 scrape_configs,是 Kubernetes 环境下的推荐方式。Consul 服务发现Consul 是常用的服务注册中心,Prometheus 通过 consulsdconfigs 查询 Consul Catalog 获取服务实例:scrape_configs: - job_name: 'consul' consul_sd_configs: - server: 'consul.example.com:8500' services: ['web', 'api'] tag_separator: ',' scheme: http token: 'your-acl-token'Consul 服务实例的元数据通过 __meta_consul_service_metadata_* 标签暴露,可以在 relabel 阶段提取为 Prometheus 标签。适合已有 Consul 基础设施的微服务架构。DNS SRV 服务发现通过 DNS SRV 记录发现目标,无需额外依赖,适合支持 SRV 记录的内网环境:scrape_configs: - job_name: 'dns' dns_sd_configs: - names: ['_prometheus._tcp.example.com'] type: SRV port: 9100也支持 A/AAAA 记录查询,将域名解析为 IP 列表。配置简单但不适用于频繁变化的场景。HTTP 服务发现Prometheus 2.28+ 引入的 httpsdconfigs,通过 HTTP 端点获取目标列表,返回格式与 file_sd 相同的 JSON:scrape_configs: - job_name: 'http' http_sd_configs: - url: 'http://config-server/targets' refresh_interval: 1m这是对文件服务发现的动态替代,适合通过自建配置中心或 CMDB API 提供目标的场景。云平台服务发现AWS EC2scrape_configs: - job_name: 'ec2' ec2_sd_configs: - region: us-east-1 access_key: AKIA... secret_key: xxx... filters: - name: tag:Environment values: [production]可以通过 filters 按 EC2 标签过滤实例,元标签 __meta_ec2_tag_* 提供 EC2 标签信息。Azure VMscrape_configs: - job_name: 'azure' azure_sd_configs: - subscription_id: 'your-subscription-id' tenant_id: 'your-tenant-id' client_id: 'your-client-id' client_secret: 'your-client-secret'GCEscrape_configs: - job_name: 'gce' gce_sd_configs: - project: 'my-gcp-project' - zone: 'us-central1-a'Relabel 配置详解Relabel 是服务发现的核心能力,在目标进入采集队列前对标签进行加工和过滤:| 动作 | 作用 | 常用场景 ||------|------|---------|| keep | 保留匹配的目标 | 只采集带特定注解的 Pod || drop | 丢弃匹配的目标 | 排除 kube-system 命名空间 || replace | 替换标签值 | 修改 address 加端口 || labelmap | 批量映射标签 | 将元标签映射为业务标签 || hashmod | 哈希取模 | 分片采集,多实例分担负载 || labeldrop | 删除标签 | 清理不需要的 __ 前缀标签 || labelkeep | 保留匹配的标签 | 只保留特定模式的标签 |常用元标签Kubernetes 环境中最常用的元标签:__meta_kubernetes_pod_name — Pod 名称__meta_kubernetes_namespace — 命名空间__meta_kubernetes_pod_annotation_prometheus_io_scrape — 采集注解__meta_kubernetes_pod_annotation_prometheus_io_path — 指标路径注解__meta_kubernetes_pod_annotation_prometheus_io_port — 端口注解__meta_kubernetes_service_name — Service 名称Consul 环境中的元标签:__meta_consul_service_metadata_* — 服务元数据__meta_consul_tags — 服务标签__meta_consul_dc — 数据中心调试服务发现当目标未按预期出现时,可以通过以下方式排查:在 Prometheus UI 的 Status → Targets 页面查看已发现的目标和标签。通过 API 查询当前服务发现结果:curl -s http://prometheus:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, discoveredLabels: .discoveredLabels}'查看服务发现过程中丢弃的目标(DISCOVERED 状态):curl -s http://prometheus:9090/api/v1/targets | jq '.data.droppedTargets | length'如何选择服务发现方式选择依据主要看部署环境:Kubernetes 集群内:优先用 kubernetessdconfigs 或 Prometheus Operator CRD有 Consul 注册中心:用 consulsdconfigs云平台原生:用对应的云 SD(ec2/azure/gce)有 CMDB 或配置中心:用 httpsdconfigs 或 filesdconfigs小规模静态环境:直接用 static_configs支持 SRV 的内网:可用 dnssdconfigs实际生产中,Kubernetes 环境占绝大多数,Kubernetes SD + Relabel 是最核心的组合。
服务端阅读 05月27日 17:40

Prometheus 故障排查怎么做?7 类常见问题逐个击破

Prometheus 线上跑着跑着突然 OOM 崩了,告警静悄悄没触发,采集数据大面积缺失——这些问题你可能都遇到过。下面按照实际排障频率,从最常见到最棘手,逐一拆解。Prometheus 无法启动启动失败通常是配置错误或资源冲突,按以下顺序排查:第一步:检查配置文件语法promtool check config /etc/prometheus/prometheus.ymlpromtool 是 Prometheus 自带的诊断工具,它会逐行解析配置并报出具体的行号和错误原因。常见的配置错误包括 YAML 缩进不一致、scrapeinterval 写成了字符串而非 duration 格式、以及 relabelconfigs 中引用了不存在的标签。第二步:检查端口占用lsof -i :9090ss -tlnp | grep 90909090 端口被其他进程占用是部署时的高频问题,尤其是在容器环境中未正确配置网络命名空间时。如果端口被占用,可以修改 --web.listen-address 参数指定其他端口。第三步:查看服务日志journalctl -u prometheus -f --no-pager -n 200日志中常见的关键错误信息:cannot load TSDB:TSDB 数据目录损坏或权限不足cannot create directory:数据目录权限问题,检查 Prometheus 进程的运行用户是否对数据目录有写权限cannot initialize notification queue:Alertmanager 地址不可达数据采集失败(Target Down)采集失败是日常运维中最频繁遇到的问题,核心排查思路是确认"Prometheus 能否访问到 Target 的 /metrics 端点"。确认 Target 状态在 Prometheus Web UI 的 /targets 页面可以看到每个 Target 的健康状态和最后错误信息。也可以用 PromQL 查询:up{job="your-job"}up == 1 表示正常,up == 0 表示采集失败。结合 scrape_samples_scraped 可以判断是否采集到了数据但数据为空。排查网络连通性curl -v http://target:port/metrics重点关注:连接超时:防火墙规则或安全组未放通403/401:认证配置不匹配404:metrics 路径不是默认的 /metrics,需要在 job 配置中指定 metrics_pathSSL 错误:证书过期或自签名证书未在 Prometheus 侧配置 tls_config检查认证配置scrape_configs: - job_name: 'secure-app' basic_auth: username: admin password: <secret> tls_config: ca_file: /etc/prometheus/certs/ca.crt insecure_skip_verify: false bearer_token: <secret>认证问题容易被忽略的细节:Bearer Token 过期后不会自动刷新,需要配合密钥管理工具轮转;insecure_skip_verify: true 虽然能解决证书校验失败,但不要在生产环境使用。查询性能慢复杂查询把 Prometheus UI 卡死,或者 Grafana 面板加载超时,这是数据量增长后的典型问题。优化查询语句的原则用标签过滤缩小范围,避免全量扫描:http_requests_total{job="api",method="GET"} 远优于 http_requests_total控制时间范围,尤其是 rate() 和 histogram_quantile() 这类聚合函数,范围越大计算量指数级增长避免 or 和 on 的复杂联合查询,拆分成多个简单查询分别执行用 Recording Rules 预计算将高频查询的中间结果提前算好,查询时直接读预计算结果:groups: - name: api_performance interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: job:http_requests:availability expr: | sum by (job) (rate(http_requests_total{code=~"2.."}[5m])) / sum by (job) (rate(http_requests_total[5m]))关键点:interval 要和查询的时间窗口匹配,太短浪费算力,太长数据延迟大。监控查询自身的性能# 查询耗时分布histogram_quantile(0.9, rate(prometheus_engine_query_duration_seconds_bucket[5m]))# 规则评估耗时topk(10, prometheus_rule_evaluation_seconds)如果发现某条规则评估耗时持续偏高,说明这条规则的表达式需要优化或者拆分。内存使用过高Prometheus 内存占用与活跃时间序列数量直接相关。一条经验值:每百万活跃序列大约需要 3-4 GB 内存。调整数据保留时间# 启动参数方式--storage.tsdb.retention.time=15d# 配置文件方式(Prometheus 2.40+)storage: tsdb: retention.time: 15d retention.size: 50GB同时设置 retention.time 和 retention.size,以先达到的阈值为准。这在磁盘空间有限的环境中尤为重要。过滤不必要的指标在数据源头过滤掉不需要的指标,比事后查询时过滤高效得多:scrape_configs: - job_name: 'app' metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*' action: drop - source_labels: [__name__] regex: 'http_requests_.*' action: keepaction: drop 在前,action: keep 在后——先排除无用指标,再保留需要的,减少处理量。还可以用 labeldrop 去掉高基数标签来降低序列数。监控内存指标# 进程实际内存占用process_resident_memory_bytes# 活跃时间序列数——内存的核心驱动因素prometheus_tsdb_head_series# Head GC 耗时,频繁 GC 说明内存压力大rate(prometheus_tsdb_head_gc_duration_seconds_sum[5m])当 prometheus_tsdb_head_series 持续增长且接近历史峰值时,就要提前扩容或裁剪指标了。磁盘空间不足磁盘问题通常和内存问题相伴出现——数据量大,内存压力大,磁盘也容易满。检查数据目录大小du -sh /var/lib/prometheus/du -sh /var/lib/prometheus/chunks_head/ls -lhS /var/lib/prometheus/chunks_head/ 是写入最频繁的目录,通常也是增长最快的。如果 wal/ 目录异常大,说明 Head Compaction 没有正常执行。配置保留策略storage: tsdb: retention.time: 15d retention.size: 50GB当磁盘使用率超过 80% 时 Prometheus 会进入"节流模式",拒绝写入新数据。所以保留策略要留出安全余量。清理指定时间段的旧数据# 查看数据块信息ls -la /var/lib/prometheus/# 删除指定时间范围的数据块promtool tsdb delete-blocks /var/lib/prometheus/ --min-time=1704067200000 --max-time=1704153600000注意:delete-blocks 操作不可逆,执行前务必确认时间范围。生产环境建议先用 snapshot 备份再清理。告警不触发告警配置了却没触发,这是最危险的情况——你以为系统正常,其实只是监控系统本身出了问题。检查告警规则语法promtool check rules /etc/prometheus/rules/*.yml常见错误:for 持续时间设置过长(比如 for: 24h),导致短暂故障恢复后告警未触发;expr 中的标签选择器与实际指标标签不匹配。检查告警状态# 告警是否进入 Pending 或 Firing 状态ALERTS{alertname="your-alert"}# 告警触发次数ALERTS_FOR_STATE{alertname="your-alert"}在 Prometheus UI 的 /alerts 页面可以直接看到每条规则的状态。如果状态一直是 Inactive,说明表达式条件从未满足;如果是 Pending 但不进入 Firing,检查 for 持续时间是否过长。检查 Alertmanager 连接# 告警通知队列积压prometheus_notifications_queue_length# 告警发送失败次数rate(prometheus_notifications_errors_total[5m])# Alertmanager 连接状态prometheus_notifications_alertmanagers_discovered如果队列积压持续增长,说明 Alertmanager 处理速度跟不上,可能需要增加 Alertmanager 实例或检查下游通知渠道。Alertmanager 侧排查# 查看 Alertmanager 日志journalctl -u alertmanager -f# 检查静默规则amtool silence query --alertmanager.url=http://localhost:9093# 检查路由配置amtool config routes --alertmanager.url=http://localhost:9093容易被忽略的原因:路由配置中 match 或 match_re 写错导致告警被丢弃;静默规则覆盖了预期告警;inhibit_rules 抑制了低级别告警。TSDB 损坏TSDB 损坏通常发生在异常断电、磁盘故障或 OOM Kill 之后。诊断 TSDB 状态promtool tsdb analyze /var/lib/prometheus/输出包含:序列数、标签数、数据块数量。如果某个数据块目录为空或索引文件缺失,说明该块已损坏。修复 TSDBpromtool tsdb repair /var/lib/prometheus/repair 会尝试恢复损坏的索引和元数据文件,但不会恢复已丢失的原始数据。执行前建议先备份。备份与恢复# 创建快照(需要开启 --web.enable-admin-api)curl -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot# 备份到远程rsync -avz /var/lib/prometheus/snapshots/ /backup/prometheus/# 恢复数据——直接替换数据目录systemctl stop prometheusrm -rf /var/lib/prometheus/cp -r /backup/prometheus/latest/ /var/lib/prometheus/chown -R prometheus:prometheus /var/lib/prometheus/systemctl start prometheus预防措施# 定期检查 TSDB 完整性(加入 cron)0 3 * * * promtool tsdb analyze /var/lib/prometheus/ >> /var/log/tsdb-health.log 2>&1常见错误信息速查| 错误信息 | 原因 | 处理方式 ||---------|------|----------|| context deadline exceeded | 查询超过 query.timeout 限制 | 优化查询语句或增大超时时间 || out of order sample | 同一序列中出现了时间戳更早的样本 | 检查是否有多个实例用相同标签写入同一条序列 || duplicate series | 多个 Target 暴露了完全相同标签的序列 | 在 metric_relabel_configs 中添加唯一标识标签 || too many samples | 单次采集的样本数超过 sample_limit | 增大 sample_limit 或过滤低价值指标 || storage capacity exceeded | 磁盘空间不足 | 调整保留策略或扩容磁盘 || label name too long | 标签名超过限制 | 在 metric_relabel_configs 中重命名或丢弃过长标签 |日常运维检查清单将以下检查项纳入日常巡检,可以在问题影响业务前提前发现:监控 Prometheus 自身:用另一个 Prometheus 实例或 Thanos Sidecar 监控主实例的 up 指标和资源使用率配置变更审计:每次修改配置前用 promtool check 验证,修改后确认 Target 状态正常磁盘和内存容量规划:设置 process_resident_memory_bytes 和磁盘使用率的告警阈值在 80%告警可用性验证:定期发送测试告警(amtool alert add)确认通知链路畅通数据完整性校验:每周执行一次 promtool tsdb analyze,关注异常增长或衰减的序列数备份验证:备份不仅要做,还要定期从备份恢复到测试环境,确认数据可用
服务端阅读 05月27日 17:39

PromQL 常用函数怎么选?一文讲透用法与避坑

PromQL 是 Prometheus 的核心查询语言,掌握常用函数是写出高效监控告警规则的基础。本文按实际使用场景分类梳理 PromQL 函数,并标注每种函数的适用指标类型和常见陷阱。变化率与增量函数变化率类函数是 PromQL 中使用频率最高的函数,但也是最容易用错的一类。rate(v range-vector):计算 Counter 类型指标在时间窗口内的平均每秒增长率。适合绘制趋势图和配置告警规则,结果会被平滑处理。例如 rate(http_requests_total[5m]) 计算过去 5 分钟内 HTTP 请求的每秒平均增长率。irate(v range-vector):计算 Counter 的瞬时增长率,仅使用时间窗口内最后两个数据点。适合绘制细粒度波动图,但不适合告警(抖动太大)。例如 irate(http_requests_total[5m])。increase(v range-vector):计算 Counter 在时间窗口内的总增量,等价于 rate() * 窗口秒数。返回值是整数趋势但实际可能是小数(外推导致)。例如 increase(http_requests_total[1h]) 表示过去 1 小时新增了多少请求。delta(v range-vector):计算 Gauge 类型指标的差值,仅用于 Gauge。例如 delta(cpu_temp_celsius[1h]) 表示温度变化量。关键区别:rate 和 irate 只能用于 Counter,delta 用于 Gauge。increase 虽然概念上返回整数,但由于外推机制,结果可能非整数。如果需要精确整数,用 floor(increase(...))。时间窗口选择:窗口太短会导致数据稀疏时出现 NaN,太长会掩盖波动。一般建议窗口至少是采集间隔的 4 倍。聚合函数聚合函数用于将多个时间序列合并为更少的结果序列,是构建大盘面板的基础。sum():求和。最常用的聚合,例如 sum(rate(http_requests_total[5m])) 计算总 QPS。avg():求平均值。注意平均值容易受极端值影响,监控延迟场景更推荐用分位数。max() / min():最大值和最小值。适合找出峰值或谷值。count():计数。统计时间序列的数量,例如 count(up == 0) 统计宕机实例数。count_values():按值分组计数。例如 count_values("status", http_requests_total) 统计各状态码出现次数。topk(k, …):返回值最大的 k 个时间序列。适合找出负载最高的实例,例如 topk(5, rate(http_requests_total[5m]))。bottomk(k, …):返回值最小的 k 个时间序列。quantile(φ, …):计算分位数。例如 quantile(0.95, rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])) 估算 P95 延迟。分组聚合:使用 by 子句按标签分组,例如 sum(rate(http_requests_total[5m])) by (method) 按请求方法分组求和。使用 without 排除指定标签,sum(...) without (instance) 按 instance 维度聚合。时间窗口聚合函数(overtime 系列)这类函数对时间窗口内每个时间序列的数据点做聚合,与上面聚合函数的区别是:聚合函数跨序列合并,overtime 系列在单个序列的时间维度上聚合。avgovertime(x[5m]):5 分钟内平均值,适合平滑 Gauge 指标。maxovertime(x[5m]):5 分钟内最大值,常用于找出峰值。minovertime(x[5m]):5 分钟内最小值。sumovertime(x[5m]):5 分钟内求和。countovertime(x[5m]):5 分钟内数据点数量,可用于检测数据缺失。quantileovertime(0.9, x[5m]):5 分钟内分位数计算。stddevovertime(x[5m]):5 分钟内标准差,衡量波动程度。stdvarovertime(x[5m]):5 分钟内方差。典型用法:max_over_time(node_load15[1h]) 找出过去 1 小时最大负载。数学与取整函数abs(v):绝对值。ceil(v):向上取整,例如 ceil(cpu_usage) 将 72.3 变为 73。floor(v):向下取整。round(v, nearest):四舍五入到最近的整数或指定精度。round(3.14159, 0.01) 结果为 3.14。sqrt(v):平方根。exp(v):指数函数 e^v。ln(v) / log2(v) / log10(v):自然对数、以 2 为底和以 10 为底的对数。clamp(v, min, max):将值限制在指定范围内。clamp(cpu_percent, 0, 100) 确保百分比不超出 0-100。clampmax(v, max) / clampmin(v, min):单侧限制。预测与统计函数predict_linear(v range-vector, t):基于 2 小时(默认)的线性回归预测 t 秒后的值。常用于磁盘空间预警:predict_linear(node_filesystem_avail_bytes[1h], 3600*24) < 0 预测 24 小时后磁盘是否耗尽。deriv(v range-vector):计算 Gauge 指标的瞬时导数(变化率),适合 Gauge 趋势分析。holt_winters(v range-vector, sf, tf):基于 Holt-Winters 双指数平滑进行平滑和预测。sf 是平滑因子(0-1),tf 是趋势因子(0-1)。适合有周期性波动的指标。标签操作函数labelreplace(v, dstlabel, replacement, src_label, regex):基于正则从源标签提取值写入目标标签。例如 label_replace(up, "host", "$1", "instance", "(.*):.*") 从 instance 标签提取主机名写入 host 标签。labeljoin(v, dstlabel, separator, srclabel1, srclabel2, …):将多个源标签拼接后写入目标标签。例如 label_join(up, "endpoint", "-", "job", "instance") 将 job 和 instance 用短横线拼接。其他实用函数changes(v range-vector):计算时间窗口内值变化的次数。适合检测配置变更或重启次数:changes(process_start_time_seconds[1d]) 检测一天内重启次数。absent(v):如果传入的向量没有数据点则返回 1,有数据则返回空。常用于检测指标消失:absent(up{job="myapp"}) 在 myapp 完全无数据时触发告警。time():返回当前 Unix 时间戳。常用于相对时间计算。timestamp(v):返回向量中每个样本的时间戳。sort(v) / sort_desc(v):升序/降序排列。histogram_quantile(φ, v):从直方图桶中计算分位数,是延迟监控的核心函数。例如 histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) 计算 P99 延迟。注意 by (le) 不可省略,le 是桶边界标签。实战示例计算 QPS 并按服务分组:sum(rate(http_requests_total[5m])) by (service)计算内存使用率百分比:sum(container_memory_usage_bytes) by (container) / sum(container_spec_memory_limit_bytes) by (container) * 100计算 P95 请求延迟:histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, method))预测 24 小时后磁盘是否耗尽:predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[1h], 3600*24) < 0检测服务重启:changes(process_start_time_seconds[1d]) > 0检测指标缺失:absent(up{job="critical-service"})常见陷阱对 Gauge 用 rate:rate/irate/increase 只适用于 Counter(只增不减的累计值)。Gauge 用 delta 或 deriv。窗口过短:采集间隔 15 秒却用 [30s] 窗口,容易得到 NaN。窗口建议至少 4 倍采集间隔。increase 结果非整数:由于外推机制,increase 可能返回小数。需要精确整数时加 floor()。histogram_quantile 忘记 by(le):le 标签是分位数计算必需的,省略会导致结果错误。topk 不稳定:topk 结果随数据波动变化大,不适合直接用于告警。absent 的触发条件:absent 返回 1 表示无数据,告警规则应写 absent(...) == 1 而非 absent(...) > 0。
服务端阅读 05月27日 17:39

如何实现 Prometheus 的高可用和联邦架构?

Prometheus 本身是单机架构,不内置集群能力。生产环境中,单点 Prometheus 面临两个核心风险:实例宕机导致监控盲区,单机存储和采集能力无法支撑大规模集群。解决思路分两条线——高可用保证不丢数据不中断,联邦架构保证能横向扩展。高可用方案:从简单到可靠多副本冗余最直接的方式是部署两个或更多 Prometheus 实例,配置完全相同的目标列表。它们各自独立采集、独立存储、独立告警。查询时通过负载均衡(如 Nginx、HAProxy)将请求分散到不同实例。优点是部署简单,缺点是每个实例都存全量数据,存储成本翻倍,且两个实例的告警可能同时触发造成重复通知。需要配合 Alertmanager 的 cluster 模式做告警去重。Thanos 方案:高可用的标准答案Thanos 在 Prometheus 之上增加四个核心组件,解决长期存储和全局查询问题:Sidecar:与 Prometheus 部署在同一 Pod,每隔 2 小时将 TSDB 块上传到对象存储(S3/OSS),同时提供 StoreAPI 让 Query 组件直接读取 Prometheus 的近端数据Store Gateway:从对象存储中读取历史数据块,响应 Query 的查询请求Query:聚合多个数据源(Sidecar + Store Gateway),对外提供统一的 PromQL 查询接口,自动去重 HA 副本的数据Compact:对对象存储中的历史块做降采样和合并,减少存储占用和查询扫描量数据流:Prometheus 采集 → Sidecar 上传到对象存储 → Query 同时查询 Sidecar(热数据)和 Store Gateway(冷数据)→ 返回合并结果。Query 的 --deduplicate 参数会基于 replica label 自动去重,解决多副本数据重复问题。关键配置:每个 Prometheus 实例必须设置不同的 external_labels(如 replica: A / replica: B),这是 Query 去重的依据。联邦架构:横向扩展的分层设计联邦是 Prometheus 原生的数据汇总机制。每个 Prometheus 实例暴露 /federate 端点,上级 Prometheus 可以像抓取 Exporter 一样从该端点拉取指标。层级联邦典型三层架构:边缘层(Edge):每个集群部署独立的 Prometheus,采集本集群所有目标的详细指标,scrape_interval 设为 15s区域层(Regional):从多个边缘实例拉取聚合后的指标(通过 recording rules 预聚合),scrape_interval 放宽到 30-60s全局层(Global):汇总所有区域的关键指标,用于跨集群看板和全局告警关键点:上级不需要拉取全量指标,通过 match[] 参数只拉取需要的指标,大幅降低数据量。联邦配置示例scrape_configs: - job_name: 'federate' scrape_interval: 30s honor_labels: true metrics_path: '/federate' params: 'match[]': - '{job="prometheus"}' - '{__name__=~"job:.*"}' static_configs: - targets: - 'edge-prometheus-cluster1:9090' - 'edge-prometheus-cluster2:9090'honor_labels: true 确保源实例的标签不被覆盖,避免数据来源混淆。match[] 只拉取 job="prometheus" 和 recording rules 产生的聚合指标,而非全量 raw metrics。跨服务联邦另一种场景:不同的 Prometheus 分别监控不同业务域(基础设施、中间件、业务应用),通过联邦在全局实例上汇聚跨域指标,做关联分析和统一看板。与层级联邦不同,这里不是按地域分层,而是按功能域分片。Thanos vs Cortex vs VictoriaMetrics:如何选择Thanos适合中大规模(10-100 个集群),核心优势是与原生 Prometheus 无缝集成——现有 Prometheus 不需要改动,加 Sidecar 即可。依赖对象存储做长期持久化,Query 组件天然支持 HA 去重。缺点是组件多、运维复杂度高,Query 查询冷数据时延迟较大(需要从对象存储加载块)。Cortex适合多租户场景(SaaS 平台、大型组织内部多团队共用)。完全分布式架构,数据写入和查询都可以水平扩展,通过分布式 kv-store(Consul/etcd)做成员管理。支持多租户隔离,每个租户有独立的速率限制和存储策略。但部署和运维门槛最高,需要依赖多个基础设施组件。VictoriaMetrics适合性能优先、资源有限的场景。单二进制即可部署(也支持集群模式),兼容 Prometheus 的查询和采集协议,可以直接替换 Prometheus。写入和查询性能优于 Prometheus,内存占用更低。缺点是生态不如 Thanos 成熟,高级特性(如多租户)需要在集群版中才能使用。决策参考| 规模 | 推荐方案 | 理由 ||------|---------|------|| 1-5 个集群 | 多副本 + 负载均衡 | 运维最简,够用 || 5-50 个集群 | Thanos | 生态成熟,HA + 长期存储一体化 || 多租户需求 | Cortex | 原生多租户支持 || 性能优先/资源紧张 | VictoriaMetrics | 低资源高吞吐 |生产环境注意事项数据持久化:Prometheus 默认数据存在本地磁盘,实例销毁数据即丢失。必须配置远程写入(remote_write)到外部存储,或使用 Thanos Sidecar 定期上传到对象存储。监控的监控:Prometheus 自身的健康状态需要有独立的监控方案。常见做法是用一个轻量级 Prometheus 或 Grafana Agent 监控主 Prometheus 的 up 指标和采集延迟。告警去重:多副本部署下,两个实例会触发相同告警。必须配置 Alertmanager 集群模式,并设置 group_by 包含告警名称和关键标签,确保同一告警只通知一次。联邦的性能开销:每增加一层联邦,上级实例的内存增加约 5%。全局 Prometheus 的资源规划要预留余量,特别是当边缘集群数量超过 50 个时。逐步上线:先在测试环境验证联邦配置和 match[] 规则是否符合预期,确认数据量和延迟在可接受范围内,再推广到生产环境。
服务端阅读 05月27日 17:39

Prometheus Exporter 有哪些常用类型?选型与配置实战

Prometheus 本身只负责数据采集和存储,真正的指标来源要靠 Exporter。Exporter 是一个独立进程,它把各种系统、数据库、应用的内部指标转换成 Prometheus 能够抓取的格式暴露出来。理解每类 Exporter 的定位和关键配置,是搭建可观测体系的基础。系统层 ExporterNode Exporter 是部署量最大的 Exporter 之一,几乎所有 Prometheus 环境都会跑它。它采集 Linux/Unix 主机的 CPU 使用率、内存余量、磁盘 I/O、网络吞吐和文件系统挂载状态,暴露的指标如 nodecpusecondstotal 和 nodememoryMemAvailablebytes 是容量规划和告警的基础数据源。部署方式很简单,在每台主机上以 systemd 服务或者 DaemonSet 运行,默认监听 9100 端口。Windows Exporter 通过 WMI 接口采集 Windows 服务器指标,覆盖 CPU、内存、磁盘、网络以及 Windows 服务和 IIS 性能计数器。如果你的环境里混合了 Windows 节点,它是 Node Exporter 的对等替代。数据库 ExporterMySQL Exporter 连接到 MySQL 或 MariaDB 实例后,暴露连接数、查询吞吐、InnoDB 缓冲池命中率、慢查询计数和主从复制延迟等指标。关键指标 mysqlglobalstatusthreadsconnected 能帮你判断连接池是否够用,mysqlglobalstatusslowqueries 则是慢查询巡检的入口。配置时通过 DATASOURCENAME 环境变量传入 DSN,建议使用只读账号。PostgreSQL Exporter 采集活动连接数、事务速率、缓存命中率、锁等待和复制状态。它支持通过查询 pgstatstatements 扩展来暴露 SQL 级别的性能数据,需要在 postgresql.conf 中提前开启该扩展。Redis Exporter 暴露内存使用量、键空间命中率、连接客户端数、驱逐键数和命令统计。对于 Redis Cluster 模式,它可以逐节点采集,帮助你定位热 key 和内存不均衡问题。应用层 ExporterBlackbox Exporter 不采集指标,而是主动探测外部端点的可用性。它支持 HTTP/HTTPS 请求、TCP 连接、DNS 解析和 ICMP Ping。你用它来验证服务是否可达、SSL 证书是否过期、DNS 解析是否正常。配置时在 blackbox.yml 中定义模块,然后在 Prometheus 的 scrape_configs 里用 relabel 把探测目标注入。JMX Exporter 是 Java 应用的监控桥梁。它通过 JMX 获取 JVM 堆内存、GC 暂停、线程数以及应用层的 MBean 指标。Kafka、Tomcat、Cassandra 等中间件都能通过它暴露指标。有两种运行模式:Java Agent 模式随应用启动,独立进程模式单独采集。容器与云平台 ExportercAdvisor 采集容器的 CPU、内存、网络和文件系统使用量,在 Kubernetes 里已经内嵌到 Kubelet,不需要额外部署就能拿到容器级别的资源数据。Kube-State-Metrics 关注的是 Kubernetes 资源对象的状态而非资源用量。它暴露 Pod 的生命周期阶段、Deployment 的副本数偏差、Job 的完成状态和 PVC 的绑定情况。和 cAdvisor 互补,一个看资源消耗,一个看对象健康。选型决策选 Exporter 的核心原则是只部署你真正需要监控的组件。每个 Exporter 都会消耗目标系统的资源,MySQL Exporter 的查询会给数据库带来额外负载,JMX Exporter 的 MBean 采集会占用 JVM 堆外内存。建议先梳理业务关键路径,沿着路径部署对应的 Exporter,而不是一股脑全装。对于 Kubernetes 环境,Node Exporter + cAdvisor + Kube-State-Metrics 三件套是起步配置,再根据业务组件加 MySQL Exporter、Redis Exporter 等。对于传统 VM 环境,Node Exporter + 业务相关的数据库 Exporter 即可覆盖基础监控。配置示例以下是一个涵盖系统、数据库和探测的典型配置:scrape_configs: - job_name: 'node' scrape_interval: 15s static_configs: - targets: ['node1:9100', 'node2:9100'] labels: env: 'production' - job_name: 'mysql' scrape_interval: 30s static_configs: - targets: ['mysql-exporter:9104'] - job_name: 'blackbox-http' scrape_interval: 60s metrics_path: /probe params: module: [http_2xx] static_configs: - targets: - https://api.example.com/health relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: blackbox-exporter:9115注意 scrape_interval 的设置策略:系统级指标变化快,15 秒采集一次能及时捕捉异常;数据库指标相对稳定,30 秒足够;探测类任务频率可以降到 60 秒,避免对目标造成压力。常见问题Exporter 本身挂了怎么办?Prometheus 的 up 指标会变成 0,配合 alertmanager 规则 for 1m 即可触发告警。建议对每个 Exporter 都配置 up == 0 的告警。指标太多导致 Prometheus 存储暴涨?用 metricrelabelconfigs 过滤掉不需要的指标。例如 Node Exporter 默认暴露数百个指标,大部分文件系统指标如果不需要可以丢弃。MySQL Exporter 连接报错?检查 DSN 格式是否正确,确认账号有 PROCESS 和 REPLICATION CLIENT 权限,网络策略是否放通了 Exporter 到数据库的连接。
服务端阅读 05月27日 17:39

Prometheus 工作原理是什么?从架构到数据流转全解析

Prometheus 是 CNCF 毕业的开源监控告警系统,以拉取(Pull)模式采集指标、TSDB 存储时序数据、PromQL 查询语言为核心,成为云原生可观测性的事实标准。本文从架构组件到数据流转,系统讲解其工作原理。核心架构与组件Prometheus 的架构围绕数据采集、存储、查询和告警四个环节展开,各组件协同完成从指标暴露到告警通知的完整链路。Prometheus Server 是整个系统的核心,承担三个职责:通过 Scraper 定期从 Target 拉取指标数据,将数据写入本地 TSDB 存储,并对外提供 PromQL 查询接口。Server 本身是单节点部署,不依赖分布式存储,这也是它轻量高效的原因。Exporter 是指标转换桥梁。各类第三方系统(MySQL、Redis、Node、Nginx 等)本身不暴露 Prometheus 格式的指标,Exporter 负责将它们的内部指标转换为标准格式,供 Server 拉取。每个 Exporter 暴露一个 /metrics HTTP 端点。Pushgateway 解决短期任务的指标上报问题。批处理任务、Cron Job 等短生命周期进程可能在 Server 拉取前就已结束,Pushgateway 作为中间缓存接收这类任务主动推送的指标,再由 Server 从 Pushgateway 拉取。Alertmanager 接收 Server 推送的告警,负责去重、分组、路由和静默,最终通过邮件、Slack、钉钉、Webhook 等方式通知。它支持抑制规则避免告警风暴,支持多路由将不同级别告警分发到不同渠道。数据流转全链路理解 Prometheus 工作原理的关键是弄清数据从产生到消费的完整链路。第一步:指标暴露。 被监控服务自身或通过 Exporter 在 /metrics 端点暴露指标,每个指标由指标名称和一组键值对标签唯一标识,例如 http_requests_total{method="GET",path="/api"} 。第二步:服务发现与拉取。 Prometheus Server 通过静态配置或服务发现机制(Kubernetes SD、Consul SD、DNS SRV 等)找到 Target 地址,按 scrape_interval(默认 15 秒)周期性发送 HTTP 请求拉取指标数据。服务发现让 Prometheus 能自动感知 Kubernetes 中 Pod 的增删,无需手动更新配置。第三步:TSDB 存储与压缩。 拉取的样本数据以时间序列形式写入本地 TSDB。TSDB 采用列式存储,每个时间线独立存放,并通过压缩算法大幅降低存储占用。默认保留 15 天数据,可通过 --storage.tsdb.retention.time 调整。对于长期存储需求,可配置 Remote Write 将数据写入 Thanos、GreptimeDB 等远端存储。第四步:PromQL 查询与聚合。 用户通过 PromQL 查询时序数据,支持即时查询和范围查询。常用操作包括速率计算 rate(http_requests_total[5m])、聚合 sum by (method)(rate(http_requests_total[5m]))、预测 predict_linear(node_filesystem_free[1h], 3600) 等。第五步:规则评估与告警触发。 Server 根据 alert.rules 中定义的规则持续评估指标,当条件满足且持续 for 指定时长后,将告警推送到 Alertmanager。Pull 模式的设计考量Prometheus 选择 Pull 而非 Push 模式并非偶然。Pull 模式下 Server 掌握拉取节奏,避免被监控端突发流量冲垮;Server 可感知 Target 是否存活——拉取失败即意味着目标不可达;同时无需在被监控端配置 Server 地址,降低耦合。对于必须 Push 的场景(短任务、Federation 跨集群),通过 Pushgateway 和 Remote Write 提供补充。关键配置项解析Prometheus 的主配置文件 prometheus.yml 控制着采集行为:scrape_interval:全局拉取间隔,默认 15 秒,可按 Job 覆盖evaluation_interval:规则评估间隔,默认 15 秒scrape_timeout:单次拉取超时,默认 10 秒,不得超过 scrape_intervalmetric_relabel_configs:拉取后对指标标签进行重写、过滤,减少无用时间线合理的配置调优能显著降低 TSDB 的时间线基数,提升查询性能。告警配置实战一条完整的告警规则包含条件、持续时间和标签:groups: - name: node-alerts rules: - alert: HighMemoryUsage expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9 for: 5m labels: severity: critical annotations: summary: "节点 {{ $labels.instance }} 内存使用率超过 90%"for 字段确保瞬时波动不会触发告警,只有持续超阈值才正式触发。Alertmanager 收到后按路由规则分发。版本演进与最新特性Prometheus 3.0 于 2024 年底发布,是七年来最大版本更新。截至 2026 年 5 月,最新版本为 v3.12,LTS 版本为 v3.5.x。主要进展包括:原生直方图(Native Histogram)在 v3.8 达到稳定状态,提供更高效的桶式聚合Remote Write 2.0 协议提升远端写入性能和可靠性UI 全面现代化,内置更直观的查询界面OTLP 写入支持,可直接接收 OpenTelemetry 指标这些演进使 Prometheus 在保持轻量架构的同时,逐步补齐长期存储和多协议兼容的短板。适用场景与局限Prometheus 在云原生容器监控、微服务可观测性、基础设施指标采集等场景下表现优异,尤其与 Kubernetes 的深度集成使其成为集群监控的首选。需要注意的是,Prometheus 不适合需要 100% 精度的计费场景(采样间隔内可能丢失数据),原生 TSDB 的存储周期有限(长期存储需搭配远端方案),且不擅长日志和链路追踪的采集——这部分通常由 Loki 和 Tempo 补充,共同构成完整的可观测性体系。
服务端阅读 05月27日 17:38

如何优化MariaDB查询性能?

查询性能是数据库系统的生命线。一条低效的SQL可能拖垮整个应用,而一次精准的优化能让响应时间从秒级降到毫秒级。这篇文章从诊断、索引、写法、配置四个层面,给出经过生产验证的优化方法。用 EXPLAIN 定位性能瓶颈优化之前,先要找到问题。EXPLAIN 是最直接的诊断工具:EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';输出中有四个字段值得重点关注:type — 访问类型,从差到优依次为 ALL → index → range → ref → eq_ref → const。出现 ALL 意味着全表扫描,必须优化key — 实际使用的索引。如果为 NULL,说明索引未被命中rows — 预估扫描行数。数字越大,查询越慢Extra — Using filesort 表示额外排序,Using temporary 表示使用了临时表,两者都应尽量避免一个简单的判断标准:type 不是 ALL 且 Extra 没有 Using filesort/Using temporary,查询基本合格。索引:最有效的加速手段建立合适的复合索引单列索引在多条件查询时往往不够用。复合索引遵循最左前缀原则,把区分度高的列放前面:-- 假设查询条件为 WHERE user_id = ? AND status = ?-- user_id 区分度远高于 status,放前面CREATE INDEX idx_user_status ON orders(user_id, status);用覆盖索引避免回表当查询的列全部包含在索引中时,引擎无需回表读取数据行,性能提升显著:-- 索引 idx_user_status(user_id, status) 无法覆盖此查询(需要 amount 列)SELECT user_id, status, amount FROM orders WHERE user_id = 100;-- 建立覆盖索引后,直接从索引读取所有数据CREATE INDEX idx_user_status_amount ON orders(user_id, status, amount);避免索引失效的常见写法以下写法会导致索引无法命中:对索引列使用函数:WHERE YEAR(created_at) = 2025 改为 WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'隐式类型转换:WHERE varchar_col = 123 改为 WHERE varchar_col = '123'前缀模糊查询:WHERE name LIKE '%John' 改为 WHERE name LIKE 'John%'使用 OR 连接不同索引列:改用 UNION ALL 拆分查询写法的优化技巧只查需要的列SELECT * 是性能杀手。它强制读取所有列的数据,增加 I/O 和内存开销,还可能破坏覆盖索引:-- 不推荐SELECT * FROM users WHERE id = 1;-- 推荐:只查业务需要的列SELECT id, name, email FROM users WHERE id = 1;用 JOIN 替代子查询MariaDB 优化器对子查询的处理不如 JOIN 高效,特别是 IN 子查询:-- 不推荐SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);-- 推荐SELECT u.id, u.name, u.email FROM users uINNER JOIN orders o ON u.id = o.user_idWHERE o.amount > 1000;UNION ALL 替代 UNIONUNION 会对结果去重,需要额外的排序操作。如果确定结果集无重复,用 UNION ALL 省掉去重开销:-- 不需要去重时SELECT name FROM customers WHERE region = 'east'UNION ALLSELECT name FROM suppliers WHERE region = 'east';深分页的两种优化方案OFFSET 值很大时,数据库需要扫描并跳过前面的所有行:-- 传统写法:跳过 10 万行,极其缓慢SELECT * FROM orders ORDER BY id LIMIT 100000, 10;-- 方案一:游标分页(要求排序字段连续且有索引)SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 10;-- 方案二:延迟关联(先查主键再回表,减少扫描列数)SELECT o.* FROM orders oINNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 100000, 10) tmpON o.id = tmp.id;JOIN 优化被驱动表的连接列必须有索引小结果集驱动大表,减少循环次数当优化器选错连接顺序时,用 STRAIGHT_JOIN 强制指定:SELECT * FROM small_table sSTRAIGHT_JOIN large_table l ON s.id = l.small_id;配置层面的调优InnoDB 缓冲池这是影响 InnoDB 性能最重要的参数,建议设为物理内存的 50%-70%:innodb_buffer_pool_size = 4Ginnodb_buffer_pool_instances = 4排序和连接缓冲sort_buffer_size = 4M -- 每个连接的排序缓冲join_buffer_size = 4M -- 每个无索引连接的缓冲read_rnd_buffer_size = 4M -- MRR 读取缓冲临时表大小tmp_table_size = 256Mmax_heap_table_size = 256M超过此大小的临时表会写到磁盘,导致性能骤降。关于查询缓存注意:MariaDB 10.6 起默认禁用查询缓存,后续版本已移除该功能。如果使用 10.6+,不要配置 querycachesize,而是关注应用层缓存(如 Redis)。监控慢查询开启慢查询日志,定期分析并优化:SET GLOBAL slow_query_log = ON;SET GLOBAL long_query_time = 1; -- 超过 1 秒记录SET GLOBAL log_queries_not_using_indexes = ON; -- 记录未使用索引的查询结合 pt-query-digest 工具分析慢查询日志,找出最需要优化的 SQL:pt-query-digest /var/lib/mysql/slow.log优化决策路径面对一个慢查询,按以下顺序排查:先用 EXPLAIN 查看执行计划,确认是否走了索引如果走了索引仍然慢,考虑建立覆盖索引或调整索引列顺序如果索引没有问题,检查查询写法是否有优化空间(避免 SELECT *、子查询改 JOIN、深分页优化)如果单条 SQL 已最优,考虑配置调优(缓冲池、排序缓冲、临时表大小)配置也调不动了,考虑架构层面优化(读写分离、分库分表、引入缓存)每个阶段都有明确的检查点和动作,避免盲目调参。