
监控这件事往往不是一开始就想做的而是等线上出了几次故障之后才意识到它的价值。Prometheus和Grafana这套开源组合目前几乎是服务器指标监控的事实标准一个负责数据的采集和存储一个负责把数据画成一眼能看懂的图表。我从零开始把这套东西完整跑了一遍现在把搭建、配置、告警和日常使用中踩过的坑整理成文。刚接触监控的运维、后端开发以及自己管理着几台服务器的个人站长都能直接照着操作。你会看到如何用Docker Compose拉起整个监控栈、如何配置抓取任务、如何用Grafana做可视化以及告警规则该怎么设计。所有配置我都贴出了完整内容并且标出了我在实际部署中反复踩过的坑。1. Prometheus与Grafana是什么为什么要一起用1.1 Prometheus核心原理与数据模型Prometheus是一个开源的监控系统和时间序列数据库最初在SoundCloud内部开发2012年开源后来加入云原生计算基金会CNCF成为继Kubernetes之后第二个毕业的项目。它的核心思路是pull模型被监控对象暴露一个HTTP接口Prometheus定期去这个接口抓取指标数据。这个设计跟传统的“agent主动推数据到服务端”完全不同好处在于采集目标可控服务端随时知道目标是不是活着因为抓取动作本身就是一次探活。数据模型上每一条指标由指标名称加一组标签label唯一定义比如node_cpu_seconds_total{cpu0,modeidle}。这种结构在查询时非常灵活你可以任意通过标签过滤、分组和聚合。指标类型主要有四种Counter只增不减适合记录请求总数Gauge可增可减适合当前内存使用量Histogram用于统计分布比如请求延迟的百分位Summary在客户端计算分位数适合业务场景。理解了这几种类型写PromQL就不会一头雾水这一点在后面的查询环节非常关键。PromQL是Prometheus的查询语言也是它的灵魂。它支持类似函数式的写法配合rate、sum、avg、topk这些内置函数可以组合出各种想要的监控维度。刚开始接触可能会不太习惯但熟练之后你会发现它比很多商业监控软件的聚合查询要灵活得多。1.2 Grafana的定位和核心能力Grafana是一个开源的可视化平台支持的数据源列表很长Prometheus、MySQL、PostgreSQL、Elasticsearch、Loki、InfluxDB、OpenTSDB等。它的核心价值是把各个数据源的数据集中到一个统一的看板上做成漂亮、可交互的图表。Prometheus自带的界面不支持复杂的仪表盘布局查询结果也相对朴素所以可视化这部分几乎都由Grafana承担。Grafana的另一个重要角色是统一入口。你完全可以在一个面板上同时接入服务器指标Prometheus和业务数据库指标比如MySQL数据源让运维和开发一起看同一个页面减少来回切换工具的麻烦。权限管理方面Grafana支持用户、团队、组织、面板分享和API Key这在公司内多人使用时会很有用。此外Grafana自身也具备告警能力能够在图表规则的基础上触发告警并推送到钉钉、企业微信、邮件、webhook等渠道。如果不想在Prometheus里写一堆告警规则或者只想针对特定面板做阈值判断可以在Grafana里配置操作门槛会更低。1.3 这套组合解决了什么问题单看Prometheus它能采集、存储、查询指标但它的界面并不擅长做大量时间范围的趋势展示也不支持把不同来源的数据拼在一起看。单看Grafana它又没有采集能力数据源离了Prometheus这类后端就是无米之炊。两者配合之后整个监控闭环才成立采集侧由Prometheus完成存储同样在Prometheus展示和告警入口则交给Grafana。我见过不少团队一开始用Zabbix后来因为云原生环境下容器实例频繁调度、动态发现需求太强逐渐转向Prometheus也见过直接用SaaS监控的因为成本和数据边界问题想迁移回来。如果你维护的是Kubernetes集群那Prometheus几乎是绕不开的选项因为kubelet、kube-state-metrics这些组件的指标都是天然按照Prometheus格式暴露的。2. 环境准备与部署2.1 部署方式的选择第一次上手到底用二进制、Docker Compose还是直接上kube-prometheus-stack我的建议很简单如果只是在几台机器上做基础监控用Docker Compose最省心配置文件一把梭升级也方便如果有一堆物理主机需要批量监控可以考虑二进制加systemd托管的方式如果本身就在Kubernetes集群里直接上kube-prometheus-stackPrometheus Operator是正解后续ServiceMonitor、PodMonitor自动发现目标体验完全不同。这篇文章以Docker Compose为主要方案原因是它能最快跑通完整链路同时保留Prometheus、node-exporter、Grafana、Alertmanager多个组件的独立性便于后面的扩展。需要注意Docker Compose的版本建议2.x具体命令为docker compose而不是docker-compose。部署前先确认机器上已经装好Docker和Docker Compose插件。整个监控的目录结构我习惯放在/opt/monitoring下面方便统一管理和备份。2.2 基于Docker Compose搭建Prometheus先创建目录并准备docker-compose.ymlmkdir -p /opt/monitoring cd /opt/monitoring vim docker-compose.yml下面是我一直在用的Compose配置version: 3.8 services: prometheus: image: prom/prometheus:latest container_name: prometheus restart: unless-stopped ports: - 9090:9090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus command: - --config.file/etc/prometheus/prometheus.yml - --storage.tsdb.path/prometheus - --storage.tsdb.retention.time30d - --web.enable-lifecycle node-exporter: image: prom/node-exporter:latest container_name: node-exporter restart: unless-stopped ports: - 9100:9100 volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - --path.procfs/host/proc - --path.sysfs/host/sys - --path.rootfs/rootfs grafana: image: grafana/grafana:latest container_name: grafana restart: unless-stopped ports: - 3000:3000 volumes: - grafana-data:/var/lib/grafana environment: - GF_SECURITY_ADMIN_PASSWORDadmin123 volumes: prometheus-data: grafana-data:这里解释几个关键点prometheus.yml用只读方式挂载:ro防止容器内误改数据卷单独声明确保重启容器指标不丢Grafana通过环境变量设定了管理员初始密码首次登录时便于使用但生产环境记得改掉或者去掉这个环境变量改用更安全的方式。command里加了web.enable-lifecycle之后可以通过curl -X POST http://localhost:9090/-/reload实现热加载配置不需要每次修改配置都重启Prometheus进程。2.3 配置Prometheus抓取指标prometheus.yml是这个监控体系里最核心的配置文件先给一份基础版本global: scrape_interval: 15s evaluation_interval: 15s external_labels: monitor: my-monitoring scrape_configs: - job_name: prometheus static_configs: - targets: [localhost:9090] - job_name: node-exporter static_configs: - targets: [127.0.0.1:9100]scrape_interval是抓取频率evaluation_interval是告警规则评估频率。如果监控的是线上高敏感业务抓取间隔可以设成每10秒或5秒但会对Prometheus所在主机的CPU和磁盘带来更多压力只是日常监控服务器负载15秒完全够用数据太多反而会让存储膨胀。启动服务并验证docker compose up -d docker compose ps curl http://localhost:9090/targets在浏览器打开http://localhost:9090/targets如果Prometheus和node-exporter两个target都显示UP说明抓取链路已经通了。这一步没通优先排查端口是否暴露、防火墙是否放行以及curl后返回的是不是Prometheus格式的指标文本。node-exporter是Prometheus官方提供的主机指标采集器暴露CPU、内存、磁盘、网络等系统指标。它在容器里采集宿主机指标需要挂载宿主机的/proc、/sys和根目录这是为了让容器内部的进程能看到宿主机的系统信息。很多新人在这一步会漏掉挂载导致采集到的指标全是容器自身视角数据不准这是一个很容易踩的坑。2.4 验证采集到的指标数据抓取成功后可以先用Prometheus自带的查询界面验证数据。访问http://localhost:9090/graph在查询输入框里输入up回车后会出现两条序列对应prometheus和node-exporter两个target值都是1表示在线。这个up指标是整个监控系统的“心跳”Grafana里很多健康类面板都依赖它。如果输入node_cpu_seconds_total或node_memory_MemTotal_bytes能看到返回数据说明主机层面的数据已经进入时序数据库。到这里Prometheus这一半算是跑通了。接下来就是Grafana的亮相时间。3. Grafana数据源接入与可视化3.1 添加Prometheus数据源访问http://localhost:3000使用admin/admin123登录如果你在Compose里配置了GF_SECURITY_ADMIN_PASSWORD就用你设置的那个首次登录会引导修改密码。进入主界面后左侧菜单找到Configuration - Data Sources点击Add data source选择Prometheus。最关键的一个配置项是URL。如果你Grafana和Prometheus运行在同一台宿主机的容器里可以直接填http://localhost:9090如果Grafana容器通过Compose和Prometheus在同一个自定义网络内更推荐的写法是http://prometheus:9090利用Docker内建的DNS解析到Prometheus容器。我在本机部署时习惯直接用http://localhost:9090因为端口映射到了宿主机所以也能通。填完后点Save Test看到绿色的“Data source is working”提示就说明通了。这个地方很多人会困惑为什么有的教程写localhost有的写prometheus有的写127.0.0.1原因是容器网络模式不同。如果是docker run --network host方式运行所有服务共享宿主机网络栈用localhost没问题如果用的是默认bridge网络Prometheus容器对Grafana容器来说并不是localhost需要走服务名或宿主机地址。理解了这一点你就不会盲目复制粘贴了。3.2 导入现成DashboardGrafana社区里有很多现成的模板输入ID就能一键导入。我常用的是1860Node Exporter Full和8919Linux主机基础监控。操作方法左侧菜单Dashboards - Import填入Dashboard ID点击Load选择刚才配置好的Prometheus数据源点Import即可。导入后你会立刻看到一组精心排布的图表CPU使用率、内存占用、磁盘IO、网络流量、系统负载、运行时间等等。这些模板是社区多年实践的产物对刚上手的人来说最好的用法是先导入一个完整的看板观察正常数据的形态再去改曲线样式或增加自己的指标不要一上来就追求从零画图。但要注意第三方Dashboard的版本兼容性有时候很烦。有些老模板基于旧版面板类型导入后在新版Grafana上会出现“legacy queries”之类的报错我后面在问题排查部分会细讲。3.3 自定义可视化面板如果官方模板不满足需求就需要自己新建Dashboard。点击左上角Dashboards - New Dashboard - Add visualization右侧面板会弹出一个数据源下拉框选择Prometheus后中间的Query编辑器会让你输入PromQL表达式。举个例子我想画一个CPU使用率的百分比曲线可以这样写100 - (avg by(instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100)这个表达式的含义先通过rate计算每秒钟空闲CPU时间的增量5分钟窗口内求平均再把所有CPU核的空闲率平均到instance级别最后用100减掉得到的就是使用率。PromQL括号嵌套比较多建议在Grafana的Explore页面先测试表达式确认曲线形态没问题再贴到面板里。面板右侧还可以设置单位、阈值、图例、坐标轴范围。比如把CPU使用率的单位设为percent阈值设成80和90面板上就会出现红黄绿的分段背景一眼能看到哪个临界点需要关注。3.4 常用PromQL查询语句这里分享几个我日常使用频率很高的PromQL覆盖主流监控场景# 单台主机CPU使用率 100 - (avg by(instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) # 可用内存排除buffers和cached node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes # 内存使用率 (1 - (node_memory_MemFree_bytes node_memory_Buffers_bytes node_memory_Cached_bytes) / node_memory_MemTotal_bytes) * 100 # 磁盘剩余空间以GB为单位 node_filesystem_avail_bytes{mountpoint/} / 1024 / 1024 / 1024 # 1分钟内系统负载 node_load1 # 系统已经运行的时间 time() - node_boot_time_seconds # 网络入口流量速率 rate(node_network_receive_bytes_total[5m])这些语句并非唯一写法你可以根据实例的标签条件进一步过滤比如只看eth0网卡rate(node_network_receive_bytes_total{deviceeth0}[5m])。重要的是理解指标有哪些标签可以用Prometheus查询界面的自动补全和标签列表来验证。4. 告警配置实战4.1 Prometheus告警规则配置详解有了数据、有了看板还差最后一步——告警。监控没有告警基本上等于白搭因为没有人会24小时盯着看板。Prometheus的告警规则写在独立的规则文件里在prometheus.yml中通过rule_files字段引用。先创建一个告警规则文件rules.ymlgroups: - name: host-alerts rules: - alert: HighCPUUsage expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) 85 for: 5m labels: severity: warning annotations: summary: Instance {{ $labels.instance }} CPU usage is high description: 当前CPU使用率超过85%持续5分钟然后修改prometheus.yml在global下方增加rule_files: - /etc/prometheus/rules.yml再把rules.yml挂载进Prometheus容器Compose的prometheus服务volumes部分要加一行- ./rules.yml:/etc/prometheus/rules.yml:ro重新创建容器或者用之前提到的热加载接口docker compose up -d curl -X POST http://localhost:9090/-/reloadfor字段是“持续时间”条件含义是指标必须连续超过阈值达到该时长后告警状态才会置为Firing。这个字段非常关键我强烈建议不要省掉。没有它一个瞬时抖动就可能触发通知设置成5分钟或10分钟能过滤掉绝大部分网络抖动和短时高负载导致的误报。在Prometheus界面从Status - Rules可以查看规则状态。告警状态一般有Inactive、Pending、Firing三种Inactive表示规则存在但未触发Pending表示满足了表达式但还没到for的持续时间Firing才是真正要通知的告警。4.2 Alertmanager接入与路由配置Prometheus本身只负责把告警发送给Alertmanager真正做消息聚合、路由和推送的是Alertmanager。在docker-compose里加入alertmanager服务alertmanager: image: prom/alertmanager:latest container_name: alertmanager restart: unless-stopped ports: - 9093:9093 volumes: - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml command: - --config.file/etc/alertmanager/alertmanager.ymlalertmanager.yml基础配置route: receiver: default-receiver group_by: [alertname] group_wait: 30s group_interval: 5m repeat_interval: 4h receivers: - name: default-receiver webhook_configs: - url: http://your-webhook-server:port/alert然后把Prometheus的告警推送地址配置到prometheus.ymlalerting: alertmanagers: - static_configs: - targets: [alertmanager:9093]Webhook是我最喜欢的调试方式先用一个能打印请求内容的小服务接收Alertmanager的POST请求确认链路通了再替换成钉钉、企业微信、飞书或自研通知系统。否则一头扎进配置外部通知渠道告警没触发都不知道问题出在哪。4.3 Grafana内置告警如果不想维护Prometheus rules和AlertmanagerGrafana 8之后提供了一套完整的统一告警系统。打开一个面板进入编辑状态右侧Alert选项卡可以设置阈值条件、评估频率和通知策略。Grafana告警的优势是直接在图表之上定义规则组件少、上手快适合中小场景。但它的缺点也很明显告警评估和Prometheus server是两条独立链路面板如果被改动或删除告警规则可能受影响复杂的报警路由、抑制、静默能力不如Alertmanager完善。我的经验是如果是系统中枢监控走Prometheus加Alertmanager稳定可靠如果是某个业务面板上的快速阈值提示用Grafana告警完全够。4.4 告警阈值设计经验阈值设计比配置工具更需要经验。我踩过几次坑之后总结了几条可复用的原则CPU使用率阈值不要一刀切如果机器是多核85%以上才需要预警因为多核机器短期冲到90%不代表处理能力不足磁盘空间告警要按容量分级比如超过80%的时候warning超过90%critical并且要提前考虑日志和容器镜像可能的爆增速度内存告警不能只看使用率还要关注swap使用量swap明显增长往往意味着物理内存已经吃紧。内存缓存在Linux下很常见直接用free看到的used并不代表真实压力PromQL里要把buffers和cached排除掉。告警文案也要写清楚包含哪个实例、哪个指标、当前值多少、持续多久、应该联系谁。否则半夜收到“CPU高”这种含糊告警值班人员根本无从下手。5. 常见问题与排查技巧5.1 面板报错datasource was not found这个报错在导入旧版Dashboard时很常见尤其是从Grafana 7或更老版本导出、再导入到新版Grafana的场景。报错原文一般类似“datasource queried by panel was not found”或“failed to upgrade legacy queries datasource im7_otuvz was not found”。原因很简单老面板把数据源ID固定绑定了导入到新环境时这个ID对应的数据源不存在或者数据源ID被新环境中的其他数据源占用了。解决办法有几种。如果你只有一个Prometheus数据源最简单的是把面板里的查询语句重新选一下数据源逐个panel修正如果面板数量多直接编辑Dashboard的JSON用当前数据源的UID替换掉旧UID。还有一种方式是将老面板转成新格式之后保存再重新导入。我在实际工作中遇到几十个面板的旧看板都是写脚本轮询找旧的datasource ID并替换效率最高。5.2 图表显示N数据点但曲线空白这个问题往往不是数据没有采集而是查询语句的写法问题。最常见的是你用了静态的指标名比如node_memory_MemFree_bytes它确实存在但当前时间范围内值可能全都是同一个数值曲线看起来就是一条水平线视觉上以为没数据。另一个高发场景是使用了rate()或irate()对Counter做计算但窗口范围小于抓取间隔导致计算出的速率要么为0要么有空洞。建议排查顺序在Explore页面执行同样的PromQL确认是否有数据返回如果返回了数据看时间范围是否覆盖到再看Legend格式是否需要指定标签最后看面板的查询选项里是否限制了Instant还是Range。大部分“空白曲线”问题都能定位到这三个环节。顺便说一句Grafana面板左上角的时间选择器很迷惑默认是last 6 hours如果Prometheus刚部署数据没有任何6小时前的历史空白是正常的把时间范围切到last 15 minutes就出来了。5.3 容器重启后数据丢失确认compose文件里是否声明了volumes。如果没有挂载prometheus-data和grafana-data容器一旦被删除TSDB和Grafana配置全部清空。尤其是docker compose up -d --force-recreate这类操作很容易让人误以为数据还在结果丢了。生产环境务必给Prometheus单独准备宿主机目录或NAS卷不要用默认的匿名卷因为匿名卷在容器重建后很难定位。5.4 告警不触发的排查顺序告警不触发我自己的排查顺序是先看Prometheus界面Status - Rules确认规则文件有没有被加载语法对不对再看告警状态是否一直Pending如果是看for是不是设置得太长同时指标波动频繁然后看Alertmanager界面http://localhost:9093确认是否收到告警没收到就看Prometheus的alerting配置是否指向了正确的Alertmanager地址收到了但没有消息通知问题就在Alertmanager的receiver和路由配置上尤其注意group_by会不会把同类告警合并得过多导致某些告警一直被吞掉。我遇到过最无语的一次是把webhook地址拼写错误Alertmanager后台一直报错但我只看了receiver列表没看日志排查了半个多小时。所以务必先看日志docker logs alertmanager是第一个命令。6. 进阶K8s集群监控、OpenTelemetry与SQL数据源6.1 在K8s集群中使用Prometheus一旦监控对象变成Kubernetes集群直接用static_configs写targets就不现实了因为Pod和节点的IP会动态变化。标准做法是安装kube-prometheus-stackPrometheus Operator它通过CRDCustomResourceDefinition提供服务发现能力。ServiceMonitor可以自动发现带有指定标签的Service并抓取其metrics端点PodMonitor则针对单个Pod的metrics端口做抓取。这类部署一般用Helm一条命令就能装好helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install kube-prometheus prometheus-community/kube-prometheus-stack装完之后Prometheus、Alertmanager、Grafana以及一系列Exporter全部自动化部署。K8s核心组件的指标比如API Server、kubelet、etcd、Controller Manager都会由默认的ServiceMonitor自动采集你几乎不需要手动写targets。更关键的是黑盒监控、自定义指标的扩展都非常方便因为CRD的定义方式本身就是声明式的。6.2 Prometheus与OpenTelemetry Collector的数据流转OpenTelemetryOTel现在越来越热门很多应用直接通过OTLP协议上报链路、日志和指标。但Prometheus本身并不原生接收OTLP数据这时候就需要一个中间层也就是OpenTelemetry Collector。它的工作模式可以理解为应用把指标发给CollectorCollector配置一个prometheus exporter将这些指标以Prometheus格式暴露成一个/metrics接口然后Prometheus再定期过来抓取。所以在架构上Prometheus并不是“直接”从OTel Collector收OTLP数据而是通过prometheus exporter把OTel指标转换成Prometheus的抓取目标。这个转换过程有几点要注意第一标签命名在转入Prometheus时建议规范化避免指标爆炸第二从OTel到Prometheus转换时某些类型需要显式配置桶的数量第三通过Collector中转时要关注Collector本身的性能毕竟所有应用的指标都要经过它聚合。明白了这个数据流你就能解释为什么Prometheus的targets列表里会多出一个OTel Collector的端点。6.3 Grafana接SQL数据源Grafana也不是只能接Prometheus。如果你有业务库里的数据想看趋势可以直接在Grafana里加MySQL或PostgreSQL数据源。添加方式和Prometheus数据源类似在Data Sources里选择MySQL填主机、端口、数据库名、用户名密码即可。之后可以在面板中使用类SQL查询例如SELECT created_at AS time, order_count FROM orders_daily WHERE $__timeFilter(created_at)$__timeFilter是Grafana提供的时间范围宏它会自动把面板左上角的时间选择器映射成SQL的where条件。这样可以一边看服务器指标一边看业务指标用于大盘分析非常方便。不过要注意把数据库直接暴露给Grafana有安全风险生产环境建议用只读账号并限制来源IP。这套Prometheus加Grafana的组合我从第一次搭建到现在中间踩了不少坑也总结了不少经验。现在每新起一台服务器第一件事就是装node-exporter并把它加入Prometheus然后Grafana上拉个模板5分钟就能出图。如果你也是刚开始搭建建议先把它跑通再慢慢根据自己的业务优化查询和告警不要一开始就追求复杂的架构简单可靠比什么都重要。最后再分享一个小技巧Prometheus的配置改完先不急着重启用promtool check config /etc/prometheus/prometheus.yml先在本地校验一遍能避免绝大多数因为缩进错误导致的启动失败。监控这个事情动手跑一遍比看十篇文档都有用。