
Prometheus把指标收回来了然后呢很多人第一次部署完Prometheus打开自带的9090页面敲一条PromQL看到那张临时曲线图第一反应是“就这”——它确实能出图能临时查指标但不能把很多指标组合成一张固定大盘不能给团队其他人一个天天开着看的入口也不能做权限、分享和多数据源联动。Grafana补的就是这层干不了的活把Prometheus后端里的指标变成业务方、运维方、值班人员每天真正会看的东西。这篇是“监控系统-Prometheus”系列的第五篇围绕Grafana讲清楚从部署到数据源接入、从面板构建到告警联动、再到各种报错排查的完整链路适合已经跑通Prometheus采集、正被可视化这层卡住的人。1. 先想清楚一个问题Prometheus都出图了为什么还要Grafana1.1 9090页面能干的事和它干不了的事Prometheus自带的Web UI并不是不能用。它的Graph页面支持PromQL输入能画趋势线能查看当前告警规则。很多人刚开始学监控就是靠这个页面验证各种表达式比如node_memory_MemTotal_bytes能不能查到rate(node_cpu_seconds_total[5m])出的曲线对不对。调试阶段它完全够用甚至可以说很方便因为它不需要额外部署任何东西装好Prometheus就自带。可一旦把监控真正交给团队使用这几个限制就很要命图表不持久化。你在Graph页面调好一个查询截图发到群里第二天同事想继续看得重新输入表达式没有人会把每个指标的手工查询都保存起来。面板组合能力为零。页面主要围绕单条PromQL展开多个指标并排对比、叠加显示、业务视角分层它完全不具备这个能力。没有用户体系。谁都能访问谁都不能被区分更别说给开发看应用指标、给DBA看数据库指标这种按角色的隔离。没有跨数据源能力。Prometheus的UI只看Prometheus自己的数据而现实里的监控往往还有日志、云厂商指标、MySQL慢查询等一堆来源。所以Grafana不是“再画一遍图”它补的是从“能查数据”到“能交给别人用”之间的一大段距离。1.2 Grafana在监控链路里到底负责哪一层先看一条典型的监控数据链路exporter或各类Agent把指标暴露出来Prometheus定期去拉取并存储Grafana再去Prometheus里面查询并渲染成图表。在这个链路上Prometheus负责的是采集、存储、去重和一部分告警计算Grafana负责的是呈现、交互、告警可视化和权限管理。很多人有一个误区觉得Grafana是Prometheus的一部分。其实两者是两个独立开源项目Grafana最开始也不是只给Prometheus用它支持的数据源类型非常多Loki日志、Mimir指标、Elasticsearch、MySQL甚至CloudWatch都能接进来。这意味着你可以在同一个页面里上面看CPU曲线下面查应用日志旁边再放一个数据库关键指标这种“一站式观测”才是Grafana真正的价值。站在整个监控体系的角度讲Prometheus是监控的“数据库”Grafana是“报表层”。数据库再好缺了报表层数据就无法被业务方理解。这个定位想清楚后面做面板、做告警的时候你才知道每个功能应该用到什么程度而不是一味堆图表。2. 部署组合拳Grafana和Prometheus放一起跑的推荐姿势2.1 选型二进制还是容器化部署Grafana的方式无非两种官方二进制包跑成系统服务或者容器化部署。如果你所在的公司基础设施还在用Ansible、SaltStack维护一堆物理机二进制方式没问题配一个systemd unit就能管理。但如果是新项目我强烈建议直接用Docker Compose把Prometheus和Grafana放在同一个编排文件里环境隔离、迁移方便、数据卷持久化清清楚楚后面想要加Node Exporter采集器或者加Alertmanager改动一个YAML文件就行。版本方面有两条经验。Grafana的大版本不要追得太激进我做监控这一年多遇到过从8.x升到9.x时部分面板的查询模型被强制升级的兼容性问题所以生产环境建议选当前稳定主线里比较成熟的版本没必要首次部署就上最新版。Prometheus版本只需要注意一点v2.x和v3.x对Grafana的数据源接口基本兼容选择哪个更多取决于你自己的采集和remote write需求就可视化而言没有本质区别。2.2 docker-compose一把拉起下面这份Compose文件是我目前比较常用的最小化组合把Prometheus和Grafana一起编排起来。注释部分改成你自己的配置跑起来就能用。version: 3.8 services: prometheus: image: prom/prometheus:v2.54.1 container_name: prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prom_data:/prometheus ports: - 9090:9090 restart: unless-stopped grafana: image: grafana/grafana:11.2.0 container_name: grafana ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_USERadmin - GF_SECURITY_ADMIN_PASSWORDadmin123 - GF_USERS_ALLOW_SIGN_UPfalse volumes: - grafana_data:/var/lib/grafana restart: unless-stopped volumes: prom_data: grafana_data:对应的prometheus.yml最小配置是这样global: scrape_interval: 15s scrape_configs: - job_name: prometheus static_configs: - targets: [localhost:9090]这里有个很关键的细节Prometheus在Compose容器里运行如果它要抓取的是宿主机上的Node Exportertargets里绝对不能写localhost:9100因为容器内的localhost指向的是容器自己。要么写宿主机局域网IP要么在Compose里加上extra_hosts: - host.docker.internal:host-gateway然后targets写成host.docker.internal:9100。我第一次搭的时候就是在这个上面卡了半小时Prometheus日志里全是connect refused。2.3 第一次打开Grafana基础配置Compose起来之后浏览器访问http://localhost:3000首次登录账号是admin密码也是admin进入之后系统会强制让你修改密码。如果你在环境变量里已经指定了GF_SECURITY_ADMIN_PASSWORD那就直接用这个变量值登录。这里有一个再强调一遍都不为过的坑GF_SECURITY_ADMIN_PASSWORD环境变量只对第一次初始化生效。如果你的grafana_data数据卷已经存在说明已经初始化过这时候再修改Compose里的环境变量是没用的旧密码依然有效。想重置只能清掉数据卷重新初始化或者用grafana-cli操作。所以刚开始调试环境可以随便一点正式环境最好在初始化时就把密码定好。第一次登录之后我建议顺手做三件事一是把时区改成北京时间不然以后新建面板看到的时间全是UTC会跟钉钉、工单系统的时间对不上二是在Server Admin里确认一下匿名访问是关闭状态否则你的监控大屏可能被局域网里任何人都看到三是创建一个专门用来接入数据源的服务账号别用admin去做所有日常操作权限隔离做早不做晚。3. 数据源接入让Grafana认识你的Prometheus3.1 Add data source的操作和URL该填什么Grafana本身不存监控数据所以第一步就是把Prometheus数据源接进来。路径是左侧菜单Connections → Data sources → Add data source类型选择Prometheus。最重要的一个配置项就是URL。这里的填写规则取决于你的网络拓扑Grafana和Prometheus都在同一套Docker Compose网络里URL写http://prometheus:9090这是Compose服务名Grafana容器能直接解析。Prometheus跑在宿主机上、Grafana在容器里Linux下要给Grafana容器加extra_hosts: - host.docker.internal:host-gatewayURL写http://host.docker.internal:9090macOS和Docker Desktop环境下host.docker.internal默认可用。两者都在宿主机上直接是同一个进程体系URL写http://localhost:9090即可。如果Grafana和Prometheus不在同一台机器URL写http://192.168.x.x:9090但要保证双方端口可达中间有防火墙的话记得放行。填完URL之后下面的Scrape interval建议设置成和prometheus.yml里的global.scrape_interval一致比如15s。这个值决定了面板在时间范围拉长之后默认的数据步长如果设置得比采集间隔还大短时间窗口内看到的数据点会变得很稀疏。最后点Save test看到绿色提示“Successfully queried the Prometheus API”就说明通了。3.2 Save Test失败时按什么顺序排查保存时报错就三种情况最多。第一种是Bad Gateway基本是网络问题Grafana容器访问不到填写的URL。依次检查Compose网络、宿主机防火墙、Prometheus是否真的在监听9090端口可以在Grafana容器里执行wget http://prometheus:9090/-/healthy试试连通性。第二种是Unauthorized说明你的Prometheus前面挂了Nginx之类的反代并且加了Basic Auth需要在数据源配置里填上对应的用户名密码。第三种是not found或connection refused多半是URL写错了协议或端口把http://和9090都对一遍。排查的时候先分清报错类型再动手比瞎猜省时间。我在实际操作中见过有人把Prometheus的9090端口误写成Grafana的3000端口报错硬是看了十分钟才反应过来。3.3 OTel Collector转发来的数据在Grafana里怎么呈现现在很多团队已经用OpenTelemetry Collector统一收指标再转发给Prometheus存储。这个链路下Grafana侧不需要任何额外配置因为OTel Collector要么把指标以remote write方式写到Prometheus要么在本地暴露/metrics让Prometheus抓取最终落到Prometheus里的还是普通时间序列Grafana照常当Prometheus数据源来查就行。唯一要注意的是指标命名。OTel转换过来的指标名经常带命名空间前缀和你手动写的exporter指标名不一样。写PromQL之前先去Prometheus的Status → Targets页面确认指标是否正常采集再用count by (__name__)({__name__~.})之类的查询把指标名列出来看一眼避免在Grafana里写半天Query结果返回空数据。4. 构建第一块面板从空Query到一张可交差的大盘4.1 面板类型怎么选新建Dashboard之后添加面板时会让你选Visualization常见的类型就这么几种我按使用频率排个序Time series默认的折线趋势图适合展示CPU使用率、网络流量、请求QPS这类随时间变化的数据。Stat只显示一个或少数几个关键数字比如当前内存使用率、今天的告警总数、服务实例数适合放在大盘顶部做概览。Gauge仪表盘样式适合内存水位、磁盘使用率这种有明确上下限、需要“一眼判断是否快满”的场景。Bar gauge横向或纵向的进度条适合把几十台机器的同一指标拉在一起对比比如所有节点的磁盘使用率。Table表格形式适合展示多维度的明细数据比如按instance和job分组的资源列表。拿不准的时候默认用Time series基本不会错。需要强调一点不要每个指标都硬套Gauge面板的作用是表达数据规律不是做得越花哨越好。我见过有人把几十个指标全做成仪表盘页面跟汽车驾驶舱一样真到排查问题的时候反而一个有用信息都抓不到。4.2 常用PromQL示例和可视化参数面板的Query编辑器本质上还是写PromQL。给两个最常用的例子先跑通流程再说。内存使用率按百分比展示100 * (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))CPU使用率按实例分组100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100)新手经常犯的一个错误是直接画Counter类型指标比如node_cpu_seconds_total不套rate()结果图上一条一直在涨的斜线看起来完全没意义。原因很简单Counter是累计值只增不减必须用rate()或irate()把它变成“每秒变化速率”才有监控价值。这个道理搞明白很多面板数据不对的问题就迎刃而解了。Query写完之后还要注意两个细节。第一个是Legend显示默认情况下图例只显示查询名称建议在面板的Options里把Legend格式改成{{instance}}这样每个数据点能对应到具体机器不然多条曲线根本分不清谁是谁。第二个是Unit设置在Standard options里找到Unit选择bytes或percent不设置的话内存数值就是一长串字节数字阅读体验极差。Grafana的Unit很聪明选了bytes之后会自动按KB/MB/GB换算不用自己手动除1024。4.3 用变量把一个面板变成N台机器的大盘如果你的Node Exporter部署在几十台机器上不可能每台机器建一个面板用变量就能解决。做法是进入Dashboard Settings → Variables → Add variable添加两个常用变量。第一个变量instance查询类型选择Query查询语句写label_values(node_boot_time_seconds, instance)这会把所有上报过该指标的主机实例自动拉到下拉列表里。第二个变量job同理用label_values(node_boot_time_seconds, job)。然后把你想要复用的面板里的Query改成100 - (avg by (instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100)用右上角的下拉框切到某一台机器图表范围会自动替换成这台机器的数据。这样一张面板就变成了全机器大盘。更有意思的是变量之间还能联动比如先选job再选instance实例列表就只显示当前job下的机器这个效果在监控面板里非常实用。5. 和Alertmanager联动把告警从命令行变成可视化事件5.1 两条路线怎么选告警接入这块经常有人搞混其实有两条清晰的路线。路线A是你已经用Prometheus原生的rule文件定义告警规则触发之后推给Alertmanager再由Alertmanager做分组、抑制和通知。Grafana在这个链路里只负责展示结果不去管评估逻辑。这套方案适合已经有Alertmanager部署基础、告警量比较大、需要复杂路由规则的团队。路线B是直接用Grafana自带的Alerting功能在Grafana里创建告警规则Grafana周期性去Prometheus查数据并评估条件触发后直接走Contact point通知。适合中小团队少部署一套组件面板和告警在同一个界面里维护学习成本低。我的个人建议是除非你已经有成熟的Alertmanager体系否则新项目优先走路线B。Grafana的告警引擎这几年已经很成熟了支持for时长、静默、分组配套的通知渠道也够用没必要为了“用Alertmanager”而刻意多维护一个组件。5.2 在Grafana里创建一条最小告警用Grafana Alerting创建一个最简单的告警看实例是否存活。路径是Alerting → Alert rules → New alert rule。Data source选择PrometheusQuery A填up 1这个表达式的含义是目标抓取状态为0也就是采集失败。在告警条件里设置当Query A的计算结果不为0时触发即存在任何一个实例的up值为0。Evaluate every设为1mfor设置为5m意思是这个异常状态持续5分钟才真正触发告警避免瞬时抖动误报。通知渠道在Contact points里配置选择你实际使用的Webhook、钉钉、企业微信或邮件。我实测过最简单的验证方式停掉一个exporter等一个评估周期告警状态会从Normal变成Pending持续超过for时长后变成Firing通知就会发出去。整套流程都跑通之后再把采集端拉起恢复等状态回到Normal整个闭环就验证完成了。5.3 把Alertmanager本身也画进大盘如果你走的是路线AAlertmanager自身也暴露监控指标可以把它也加入Prometheus采集然后在Grafana里做一块“告警总览”面板。比较常用的查询有几个alertmanager_alerts当前Alertmanager里所有告警的数量。sum by (alertstate) (alertmanager_alerts)按firing、suppressed等状态分组统计。changes(alertmanager_alerts[5m]) 05分钟内告警数量有变化表示有新告警产生或恢复。把firing数量放到一个Stat面板把告警明细放到一个Table面板用label_selectors当作标签列来展示就能做一个“告警驾驶舱”。这里有一个很关键但容易踩的坑如果你想在Prometheus里查ALERTS{alertstatefiring}这个指标前提是你的告警规则定义在Prometheus的rule文件里。如果走的是Grafana Alerting路线这个指标在Prometheus里根本不存在因为Grafana的告警评估发生在Grafana进程内部Prometheus完全不知道。做告警大盘前先确认你的告警到底在哪条链路上否则查出来的面板永远是空的。6. 踩坑速查数据源报错、面板碎线、时区错乱的完整排查链路6.1 failed to upgrade legacy queries datasource找不到是什么回事这个报错在键入了“grafana failed to upgrade legacy queries datasource im7_otuvz was not found”之后被搜得非常多我猜你大概率也遇上了。它的出现场景通常是从旧版Grafana升级到新版或者导入了一个别人分享的旧DashboardGrafana在加载面板时发现里面的数据源引用方式是旧格式尝试自动升级转换但在当前实例里找不到对应的数据源UID于是报这个错。对比一下新旧格式就明白了。新版面板JSON里一个prometheus数据源的引用是这样datasource: { type: prometheus, uid: P0A1B2C3D4E5F6G7H8 }而旧格式可能是裸的datasource: prometheus或只有name没有uid升级器把它转换之后拿着一个不存在的UID在当前实例查找自然就失败了。排查步骤我建议这样做打开报错面板的Dashboard Settings → JSON Model在面板的targets里搜索datasource字段。看你当前的字段是不是uid以及UID的值是否有效。去Connections → Data sources页面找到Prometheus数据源的详情页地址栏URL里/datasources/edit/后面那一串字符串就是真实UID。把面板JSON里的uid替换成这个真实UID保存刷新。如果面板里大量引用都用了错误UID可以先把Dashboard JSON导出在支持正则替换的编辑器里统一处理再导回Grafana。这个报错本身不复杂但它暴露的是Grafana面板数据源UID管理的基本逻辑搞懂一次之后后面再遇到任何“datasource not found”都不会慌。6.2 面板显示空白或时间线断裂面板没数据不要急着怀疑Grafana坏了按顺序排查。第一步去Prometheus的9090页面用和Grafana面板里相同的PromQL手动查询。如果这里都没数据问题在采集端或者PromQL写法先修Prometheus侧。如果Prometheus里有数据但Grafana面板空白看第二步。第二步检查Grafana右上角的时间范围。默认是Last 6 hours如果查询的是rate(process_cpu_seconds_total[5m])这种依赖窗口的指标时间范围太小可能有效数据点很少甚至没有。把时间范围拉到一天或者一周看是否有数据出现。第三步看Step或Resolution。Grafana在时间范围拉长时为了渲染性能会自动降采样把原始数据点聚合。如果面板设置里把最小步长Min step设得过大短促的波动会被抹平曲线看起来就是一条直线。把Min step改小到1s或者使用默认的Auto再来对比数据。“时间线断裂”的情况则更直接多个时间序列的instance标签不一致比如一台机器上报用的是内网IP另一台上报用的是主机名两条曲线中间就接不上或者是exporter发生了间歇性抓取失败在Prometheus的Status → Targets页面里能看到红色的失败状态。先排除这些基础设施问题再回来调面板。6.3 时区、单位、数据精度这些“看着小但很影响使用”的细节时区这个坑我提过一次但值得再强调。Grafana默认显示UTC时间你截一张CPU使用率的图发到工单系统里同事看到的时间比实际慢了8小时很容易被质疑数据有问题。设置方法是User preferences → Time zone改成Local browser time或者直接选Asia/Shanghai。这个配置对每个用户生效如果是团队共用账号也要在账号级别改一次。单位设置前面也提过这里给一个更具体的例子。内存指标node_memory_MemTotal_bytes的值大概是16777216000这种长数字如果不设置Unit面板上显示的就是一长串字节没人能一眼看出是16GB。在Standard options → Unit里选bytes(IEC)Grafana会自动换算成GiB显示。这个配置在Stat面板和Gauge面板里尤其重要因为这类面板原本就是给人快速做判断的。最后是数据精度。用rate()做速率计算时窗口时长不要小于采集间隔的2倍。比如Prometheus每15s抓一次rate(xxx[5m])很平滑但如果写成rate(xxx[30s])窗口里可能只有一两个数据点曲线就会跳来跳去。这个经验值是窗口越大曲线越平滑但实时性变差需要按自己场景权衡。7. 进阶技巧面板拷贝、Dashboard导入导出和团队协作7.1 整个面板怎么拷贝拷贝面板这个需求大多数人是想把自己做的图表复用到另一个Dashboard里。最简单的方式是面板标题右侧的三个点菜单 → More → Duplicate复制后的副本出现在当前Dashboard里改标题再调整一下查询就能给不同场景用。跨Dashboard复用推荐用Library Panel。选中面板 → More → Library panel把它注册为一个共享面板然后在其他Dashboard里通过Add → Library panel引回来。这样设计的好处是改一处源码所有引用的地方同步更新以后调整指标逻辑不用去每个Dashboard里改。缺点是如果你导出某个DashboardLibrary Panel的内容不会完整包含在Dashboard JSON里换环境时需要单独处理共享面板。最不推荐的做法是手动从Dashboard JSON里复制panel对象再粘到另一个文件这很容易漏掉datasource引用和一些内部ID结果就是面板文件导入之后报各种找不到数据源的错误。除非你非常熟悉Grafana的JSON结构否则不要走这种极端路径。7.2 Dashboard JSON的导入导出与数据源映射Grafana社区最大的资产就是一堆现成Dashboard模板导入功能在左侧Dashboards → New → Import输入模板ID或上传JSON文件即可。比如经典的Node Exporter Full模板ID是1860虽然指标名对应老版本node_exporter但布局结构仍然很有参考价值我每次搭新大盘都会打开看两眼。导入时最常遇到的就是数据源映射问题。模板作者用的是他自己环境里的数据源UID你的环境里没有导入页面会让你把模板里的数据源映射到当前实例。如果导入后提示“datasource was not found”说明映射没做好回到数据源配置页核对UID重新导入或者按第6章的方法改JSON。对模板的态度我一直是布局可以借鉴查询一定要自己重写。因为模板作者用的exporter版本、指标名、标签规范和你的环境几乎不可能完全一致直接拿来跑大概率一堆空面板。把模板当成一种“设计稿”根据自己真实的指标名去改Query得到的才是属于你自己的大盘。7.3 用文件夹和Tag管理团队监控资产当监控面板多起来之后没有组织机制的话Dashboards页面会变成一团乱麻。我的习惯是至少建三层文件夹基础设施层、应用层、业务层。基础设施放Node Exporter、网络设备、中间件监控应用层放各业务服务的请求量、延迟、错误率业务层放和具体业务KPI相关的报表比如订单量、转化率这种。文件夹配合权限控制是团队协作的关键。在Administration → Teams里建好团队比如“infra-team”、“app-team”给对应文件夹配置权限。Viewer只能看Editor可以改Admin管理一切。这样开发想调自己的应用面板不用每次都找运维改违规操作在权限层就被拦住了。分享Dashboard时要注意如果开启了匿名访问任何拿到链接的人都能看到面板数据监控里的很多指标其实属于内部信息绝对不要把有匿名访问的链接随便发到外部群。用带时间限制的Snapshot链接可以在坚持分享需求的同时控制暴露面。再补一个我自己的习惯每个Dashboard的JSON文件定期导出一份放到Git仓库里。Grafana实例万一挂了重新部署之后从Git拉下来批量导入几分钟恢复界面。数据源和面板用代码管理是团队监控体系走向规范化的一个低成本起点。