如果你手上管着三五台以上的服务器,无论是 Linux 虚机还是云主机,我猜你迟早会遇到同一个问题:系统出故障时,你根本不知道是哪台先出的问题,CPU 是在凌晨几点被打满的,磁盘又是什么时候悄悄写满的。传统做法是挨个 SSH 上去敲uptime、free -m、df -h,但机器一多,这套流程根本不现实。我在经历了一次半夜被磁盘告警电话叫醒、却花了二十分钟才定位到是哪台机器之后,下定决心把监控体系好好搭一遍,最终定型的就是 Prometheus + Grafana 这套组合。这篇文章会把我从零开始部署的完整过程、配置细节和踩过的坑全部写出来,适合刚接触监控体系的运维、后端开发,以及所有想给自己服务器加一道安全网的读者。
这套部署方案的核心就三件事:用 Prometheus 抓取和存储监控数据,用 Grafana 把数据变成直观的面板和图表,再用 Alertmanager 在指标异常时把告警推到你的手机上。下面我从选型逻辑开始讲,再逐步拆解二进制部署、exporter 接入、告警配置和容器化落地,全程照着操作就能复现。
1. 为什么我推荐 Prometheus + Grafana 这套组合
1.1 从"登录服务器敲命令"到"统一监控大盘"
监控体系的本质,是把分散在每台机器上的状态信息集中起来,解决两个问题:一是"现在有没有异常",二是"过去一段时间发生了什么"。只靠登录服务器敲命令,这两件事都做不到——你不可能在故障发生后还原几小时前的 CPU 曲线,也不可能同时盯住十几台机器的磁盘水位。
Prometheus 解决的是"怎么采集和存储",它把每台机器上的指标(CPU、内存、磁盘、网络等)按固定节奏抓过来,存成带时间戳的时序数据,并提供 PromQL 查询语言,让你能灵活地按时间范围和标签条件筛选数据。Grafana 解决的是"怎么看",它把 Prometheus 里的查询结果渲染成折线图、柱状图、仪表盘,还能多块图表拼成一个大盘,一屏看完所有机器的状态。
这套组合我用下来的最大感受是:排查故障的时候不用再猜了。以前是"感觉那台机器有问题",现在是"看曲线就知道问题从什么时候开始、持续了多久、影响面多大"。对于两三台机器的个人项目,它可能显得重;但只要是面向长期运行的服务,这套体系早晚都得建起来。
1.2 拉模型 vs 推模型:Prometheus 和 Zabbix 的本质差异
我早期也考虑过 Zabbix,毕竟它历史更久、中文资料也多。但在实际对比之后,还是选了 Prometheus。这里面的核心差异在数据采集方式上。
Zabbix 是典型的推模型加轮询,Agent 主动把数据推给 Server,Server 也可以反过来轮询设备,数据组织方式是树形的"监控项"。Prometheus 是拉模型,监控端定期主动去访问被监控端暴露的/metrics接口,抓取指标。数据组织方式是指标名加标签组成的多维矩阵。
这两种模式在动态环境下差别非常大。拉模型天然适合容器、云主机这种随时可能创建销毁的资源池:Prometheus 通过服务发现就能自动找到新出现的目标,不需要在监控端手工新增一台机器。推模型则需要 Agent 端主动注册或配置好上报关系,新增节点多了一道手工步骤。
| 维度 | Prometheus | Zabbix |
|---|---|---|
| 数据模型 | 指标名 + 标签的多维时序 | 树形组织的监控项 |
| 采集方式 | 主动拉取 /metrics 接口 | Agent 推送 + Server 轮询 |
| 服务发现 | 原生支持文件、Consul、K8s 等 | 依赖脚本和手工配置 |
| 告警能力 | Alertmanager 统一路由去重 | 自带触发器 |
| 可视化 | 配合 Grafana 生态强大 | 自带图表,定制能力弱 |
1.3 exporter 机制和生态优势
Prometheus 的拉模型能成立,靠的是 exporter 这套标准接口。官方和社区给几乎所有常用软件都写了 exporter:Linux 主机有node_exporter,MySQL 有mysqld_exporter,Redis 有redis_exporter,连 Nginx、MongoDB、Kafka 都有对应的现成方案。每个 exporter 暴露一个 HTTP 端口,输出格式统一的监控指标文本。这样 Prometheus 本身只需要学会一种协议,就能监控整个世界。
Grafana 这边的生态更是省心。官方模板库里几万块现成仪表盘,最常见的主机监控模板直接导入就能用,不需要你从零开始画图表。我在第一次部署的时候,从下载到看到完整的主机监控大盘,前后不到半个小时。对于一个需要快速见效的场景来说,这套组合的"上手速度"是传统监控方案很难比的。
2. 规划先行:服务器、端口、版本和时间同步
2.1 监控服务器的最低配置
很多人部署监控系统喜欢一步到位上高配,但实际没必要。我的建议是,Prometheus 和 Grafana 合装在一台 2 核 4G 的机器上,作为起步配置完全够用。磁盘则要看数据保留策略和采集规模,默认保留 15 天数据的情况下,监控几十台主机的常规指标,一天新增的数据量大概在几百 MB 到 1GB 之间,所以建议至少给监控数据目录分 100GB 空间,省得频繁清理。
被监控机器这边不需要装什么重量级 agent,只需要跑一个 exporter 进程,占用资源很小。node_exporter 的常驻内存大概 30MB 左右,对业务机器的性能影响可以忽略不计。
网络方面有一个原则必须记住:Prometheus 是拉模型,所以监控机的网络要能访问所有被监控目标的 exporter 端口。如果你有云防火墙,记得在安全组里放行这些端口,不然会看到 targets 页面上一片红。
2.2 端口规划表
部署前先把端口规划好,后面配置的时候思路会清晰很多。这是我常用的分配方式:
| 组件 | 端口 | 默认认证说明 |
|---|---|---|
| Prometheus | 9090 | 自带 UI 和 API,无登录认证 |
| Grafana | 3000 | 默认账户 admin/admin,首次登录强制改密 |
| node_exporter | 9100 | 指标接口,无认证 |
| mysqld_exporter | 9104 | 指标接口,无认证 |
| Alertmanager | 9093 | 管理 UI 和告警 API |
2.3 时间同步是隐藏的硬门槛
说它是"硬门槛",是因为时间不同步导致的问题极具迷惑性:图表上数据断断续续,告警触发时机不对,数据点对不上。TSDB 存储本身用的是 Unix 时间戳,理论上不受时区影响,但实际抓取过程中,如果目标和监控机之间时间差超过一定阈值,Prometheus 会认为数据点异常,甚至在查询时出现断层。
所以部署之前,所有参与监控的机器务必确认时间同步正常。Linux 上检查timedatectl,确保 NTP synchronized 状态是 yes,否则就配好 chrony 或 ntpdate。这个步骤 5 分钟就能做完,但能省掉后面几十个小时的排查痛苦。
2.4 版本选择原则
我的习惯是:不用 latest,选发布了一段时间的次新小版本。因为最新版本刚发布时可能带一些回归 bug,而太老的版本又缺少新功能。以我写这篇文章时为例,稳定的组合搭配是:
- Prometheus 2.53.0
- Grafana 11.3.0
- Alertmanager 0.28.0
- node_exporter 1.8.2
Prometheus 2.x 系列的配置格式大体保持稳定,YAML 配置在这几个小版本之间兼容性都很好。Grafana 11.x 的界面和 10.x 差别不大,data source 配置方式完全一致。选定一组版本后,后续所有机器的部署都用同一组版本,避免版本漂移带来的配置差异。
3. Prometheus 本体部署:二进制方式完整实操
3.1 下载解压与目录结构
二进制部署虽然比 Docker 多几步,但能让你把每个配置项的作用都搞清楚,我建议第一次部署一定用这种方式走一遍。到 GitHub 的 prometheus 官方仓库下载 tarball:
cd /opt wget https://github.com/prometheus/prometheus/releases/download/v2.53.0/prometheus-2.53.0.linux-amd64.tar.gz tar xvf prometheus-2.53.0.linux-amd64.tar.gz mv prometheus-2.53.0.linux-amd64 /usr/local/prometheus解压后的目录里有几个关键文件。prometheus是主程序;promtool是命令行工具,专门用来校验配置和规则文件,后面排错全靠它;prometheus.yml是主配置文件;console_libraries和consoles两个目录是老的模板方案遗留物,现在基本用不上,可以不用管。
先试着用默认配置跑起来看看:
cd /usr/local/prometheus ./prometheus --config.file=prometheus.yml启动后浏览器访问http://<监控机IP>:9090,看到 Prometheus 自带的查询页面就算第一步成功了。
3.2 prometheus.yml 核心配置逐行拆解
先把最精简的配置写出来,我再解释每一行的作用:
global: scrape_interval: 15s evaluation_interval: 15s alerting: alertmanagers: - static_configs: - targets: - localhost:9093 rule_files: - "/usr/local/prometheus/rules/*.yml" scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"]scrape_interval是全局抓取间隔,Prometheus 会每隔 15 秒访问一次每个 target 的指标接口。15 秒对主机监控足够,对延时敏感的业务可以调到 10 秒甚至 5 秒,但要权衡磁盘和 CPU 开销。evaluation_interval是告警规则评估间隔,Prometheus 每隔这么久检查一次规则文件里的条件是否满足。
alerting段配置 Alertmanager 地址,暂时可以先放着,等第七节部署完告警组件再启用。rule_files声明告警规则文件的路径,支持通配符。scrape_configs是核心,每个job_name代表一组监控目标,static_configs下面列出目标地址。
3.3 用 systemd 把 Prometheus 托管起来
生产环境不能靠前台进程跑,一定要用 systemd 托管,这样开机自启、崩溃自动拉起都省心。先创建专用用户,安全考虑不用 root 跑监控服务:
useradd -M -s /usr/sbin/nologin prometheus mkdir -p /usr/local/prometheus /data/prometheus chown -R prometheus:prometheus /usr/local/prometheus /data/prometheus然后创建/etc/systemd/system/prometheus.service:
[Unit] Description=Prometheus Server After=network.target [Service] User=prometheus Group=prometheus Type=simple ExecStart=/usr/local/prometheus/prometheus \ --config.file=/usr/local/prometheus/prometheus.yml \ --storage.tsdb.path=/data/prometheus \ --storage.tsdb.retention.time=15d Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target--storage.tsdb.path指定时序数据存放目录,务必放到磁盘空间充足的路径。--storage.tsdb.retention.time=15d是数据保留时间,超过 15 天的数据自动清理。
启动并验证:
systemctl daemon-reload systemctl enable --now prometheus systemctl status prometheus curl http://localhost:9090/-/healthy返回Prometheus is Healthy就说明服务正常。/-/ready接口则能检查数据是否 ready。
3.4 用 promtool 校验配置
在修改配置之后,养成用 promtool 先校验的习惯可以避免很多低级错误:
/usr/local/prometheus/promtool check config /usr/local/prometheus/prometheus.yml如果配置有语法错误,promtool 会精确定位到某个 YAML 行号。确认无误后执行systemctl reload prometheus热加载配置,Prometheus 支持在不重启的情况下刷新配置,非常方便。
4. 把监控目标接进来:exporter 部署与抓取配置
4.1 用 file_sd_configs 做动态目标管理
静态配置适合就一两台机器的情况,机器一多,直接在prometheus.yml里写 targets 列表会越写越乱。我推荐用的是file_sd_configs文件发现方式:把目标列表单独放在一个 YAML 文件里,Prometheus 定期读取这个文件,改文件不需要重载主配置。
在prometheus.yml的scrape_configs里加一个 job:
- job_name: "linux-node" file_sd_configs: - files: - "/usr/local/prometheus/sd/node.yml" refresh_interval: 30s目标文件/usr/local/prometheus/sd/node.yml这样写:
- targets: - "192.168.1.10:9100" - "192.168.1.11:9100" labels: env: prod这里加的env: prod标签会附加到这些目标抓取的所有指标上,后面在 Grafana 里按环境筛选就很方便。每次加新机器,只需要往这个文件里追加一条,30 秒内 Prometheus 会自动感知。
4.2 relabel 与标签管理的最佳实践
标签是 Prometheus 多维查询的基石。默认情况下每个指标自带job和instance两个标签,instance是"IP:端口"格式,有时候不够友好。可以用 relabel 规则提取成新的标签:
relabel_configs: - source_labels: [__address__] regex: "(.*):9100" target_label: "node_ip" replacement: "${1}"这段的意思是:从__address__(原始地址)里用正则提取出 IP 部分,写到新标签node_ip上。配置好以后,PromQL 里就能用node_ip来筛选具体机器。
这里有一条重要原则:不要加高基数的标签。标签的每个取值组合都会生成独立的时序数据,比如把用户的 ID、请求的 URL 这种高变化值当成标签,时序数量会爆炸式增长,内存和磁盘都扛不住。标签只应该用于区分有限几个维度:环境、机房、角色、版本这些。
4.3 扫描 target 状态的方法
配置完成后,在 Prometheus 页面的 Status 菜单下的 Targets 页面,能看到每个 target 的健康状态。UP 表示最近一次抓取成功,DOWN 表示失败。点开某个 target 还能看到抓取耗时、上次抓取时间、详细的错误信息。
如果某个 target 是 DOWN,最常见的几个原因:安全组没放行端口、exporter 没启动、IP 或端口写错。按照这个顺序排查基本都能解决。
5. Grafana 部署与数据源接入:从安装到第一块面板
5.1 安装 Grafana
Grafana 官方提供了 RPM 和 DEB 包,安装方式很直接。RHEL/CentOS 系:
sudo yum install -y https://dl.grafana.com/oss/release/grafana-11.3.0-1.x86_64.rpm systemctl daemon-reload systemctl enable --now grafana-serverDebian/Ubuntu 系:
sudo apt-get install -y adduser libfontconfig1 musl wget https://dl.grafana.com/oss/release/grafana_11.3.0_amd64.deb sudo dpkg -i grafana_11.3.0_amd64.deb安装完成后访问http://<监控机IP>:3000,默认账号 admin,默认密码 admin,首次登录会强制要求修改密码。Grafana 的配置文件在/etc/grafana/grafana.ini,大部分默认配置够用,我通常只改时区设置。
5.2 添加 Prometheus 数据源
登录后进入 Administration -> Data sources -> Add data source,选 Prometheus。关键配置只有一项:HTTP URL 填http://localhost:9090(如果 Grafana 和 Prometheus 在同一台机器)。
其他选项保持默认就好,然后点底部的 Save & test,看到绿色的 "Datasource is working" 提示就说明数据链路已经通了。这个步骤如果报错,优先检查网络连通性和端口,用curl http://localhost:9090/-/healthy验证一下 Prometheus 是否正常。
5.3 导入现成模板:半小时见到完整仪表盘
自己从零画面板效率太低,直接导入模板社区现成的方案。在 grafana.com/dashboards 里搜索 node exporter,最经典的模板 ID 是 1860(Node Exporter Full),还有 8919、11074 等常见选择。
导入方法是:Dashboards -> New -> Import,输入模板 ID,选好数据源,点 Import 即可。导入完成后就能看到一整面包含 CPU、内存、磁盘、网络、文件系统等状态的主机监控大盘。
这里有一个容易踩的坑:很多模板默认指标名是node_cpu_seconds_total,如果你的 exporter 版本较新,指标会带一些额外标签,图可能显示不出数据。解决办法是把模板里的 PromQL 查询和当前指标的标签对一下,或者干脆改用和我上面提到版本匹配的模板。1860 这个模板经历过多轮更新,对新版本 node_exporter 的兼容性较好。
5.4 手写一个最简单的 CPU 使用率面板
模板能解决 80% 的需求,但自己会写查询才能应对剩下的 20%。我拿最常用的 CPU 使用率做例子。PromQL 查询如下:
100 - avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100含义拆解:node_cpu_seconds_total是 CPU 累计运行时间计数器,mode="idle"筛出空闲状态,irate(...[5m])计算最近 5 分钟的瞬时速率,avg(..) by (instance)按实例聚合平均,最后用 100 减去空闲率得到使用率。
创建一个新的 Dashboard,选 Add visualization,数据源选 Prometheus,把查询粘进去,一个实时 CPU 使用率面板就好了。掌握几个基础函数rate、irate、sum、avg、by的用法之后,大部分指标查询都能自己写出来。
6. node_exporter 与 mysqld_exporter:实战接入两个监控目标
6.1 node_exporter 部署流程
主机监控靠 node_exporter。在每台被监控机器上执行:
cd /opt wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz tar xvf node_exporter-1.8.2.linux-amd64.tar.gz mv node_exporter-1.8.2.linux-amd64 /usr/local/node_exporter创建用户和 systemd service:
useradd -M -s /usr/sbin/nologin node_exporter[Unit] Description=Node Exporter After=network.target [Service] User=node_exporter Group=node_exporter Type=simple ExecStart=/usr/local/node_exporter/node_exporter --web.listen-address=:9100 Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target启动后访问http://<被监控机IP>:9100/metrics,能看到一长串文本形式的指标。这就是 Prometheus 拉取的数据源。node_exporter 默认启用了所有 collector,包括 CPU、内存、磁盘、文件系统、网络等。某些 collector 用不上的可以在启动参数里加--collector.disable-defaults再手动启用需要的,不过初期直接用默认配置就行。
最后把目标加到/usr/local/prometheus/sd/node.yml,Prometheus 会在 30 秒内自动发现新主机。
6.2 mysqld_exporter 部署
监控 MySQL 的方法类似,但多了一步授权。先在 MySQL 里创建专用监控账号:
CREATE USER 'exporter'@'%' IDENTIFIED BY 'exporter_pass'; GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%';mysqld_exporter 通过 DSN 连接 MySQL,在启动时用环境变量传入:
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz tar xvf mysqld_exporter-0.15.1.linux-amd64.tar.gz mv mysqld_exporter-0.15.1.linux-amd64 /usr/local/mysqld_exporter创建 systemd service,注意 Environment 里的 DSN 格式:
[Unit] Description=MySQL Exporter After=network.target [Service] User=mysqld_exporter Group=mysqld_exporter Type=simple Environment=DATA_SOURCE_NAME=exporter:exporter_pass@tcp(localhost:3306)/ ExecStart=/usr/local/mysqld_exporter/mysqld_exporter --web.listen-address=:9104 Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target然后把IP:9104加入 Prometheus 的抓取目标文件里,配好 job_name 为mysql。之后在 Grafana 里导入 MySQL 监控模板(比如 ID 7362),就能看到连接数、慢查询、QPS 等指标了。
6.3 验证抓取链路是否完整
接入完成之后,一定要回 Prometheus 的 Targets 页面确认所有目标都是 UP 状态,Scrape Duration正常是一个好的信号,说明拉取没有超时。
接着到 Graph 页面随便查一个指标,比如up,看返回是否正常。up是 Prometheus 自动生成的特殊指标,值为 1 表示目标在线,0 表示离线。通过up{job="mysql"} == 0这样的查询,就能快速判断某个 job 下的哪些实例挂了。
7. 告警闭环:Alertmanager 部署与通知接入
7.1 监控不加告警等于白搭
这是我反复强调的观点:只搭监控不配告警,等于给别人展示你家的窗户有多大,却没有装报警器。Prometheus 本身负责判断"指标是否异常",Alertmanager 负责"异常了怎么通知人"。两者配合,才能形成一个完整的监控闭环。
7.2 编写告警规则文件
创建/usr/local/prometheus/rules/node.yml:
groups: - name: node-alerts rules: - alert: NodeDown expr: up{job="linux-node"} == 0 for: 1m labels: severity: critical annotations: summary: "{{ $labels.instance }} 已宕机" description: "实例 {{ $labels.instance }} 已经超过 1 分钟无法访问" - alert: HighCpuUsage expr: 100 - avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100 > 90 for: 5m labels: severity: warning annotations: summary: "{{ $labels.instance }} CPU 使用率过高"for: 5m表示条件持续 5 分钟后才触发告警,这是为了过滤瞬时尖峰导致的误报。写好后用 promtool 校验:
/usr/local/prometheus/promtool check rules /usr/local/prometheus/rules/node.yml然后在prometheus.yml的rule_files里引用这个目录,我前面配置里已经写好了/usr/local/prometheus/rules/*.yml,所以直接把文件丢进去,reload 配置即可。
7.3 Alertmanager 安装与路由配置
下载并安装 Alertmanager:
cd /opt wget https://github.com/prometheus/alertmanager/releases/download/v0.28.0/alertmanager-0.28.0.linux-amd64.tar.gz tar xvf alertmanager-0.28.0.linux-amd64.tar.gz mv alertmanager-0.28.0.linux-amd64 /usr/local/alertmanagerAlertmanager 的核心是/usr/local/alertmanager/alertmanager.yml,它决定告警怎么路由、向哪里发送。一个最简配置:
route: group_by: ['alertname'] group_wait: 10s group_interval: 30s repeat_interval: 4h receiver: 'webhook' receivers: - name: 'webhook' webhook_configs: - url: 'http://127.0.0.1:8000/alert'几个时间参数值得细说。group_wait是同一组告警第一次触发后等待多久再发送,这样可以把短时间内同时触发的多条告警合并成一条。group_interval控制两组告警之间的发送间隔。repeat_interval是同一个告警未恢复时的重复通知间隔,我设成 4 小时,避免告警刷屏;你也可以按业务紧急程度改成 1 小时或更长。
同样用 systemd 托管运行。启动后访问9093端口能看到 Alertmanager 的 UI,里面可以管理静默规则、查看已接收的告警。
7.4 用极简 webhook 把告警推到钉钉
Alertmanager 原生支持邮件、Slack、企业微信,但钉钉需要额外转发。最简单的方案是写一个十几个行的小服务,接收 Alertmanager 的 webhook 请求,再转发到钉钉机器人。
我用 Python Flask 写过这样一个转发器,整个过程很快:
from flask import Flask, request import requests app = Flask(__name__) DINGTALK_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=你的token" @app.route("/alert", methods=["POST"]) def alert(): data = request.json alerts = data.get("alerts", []) for alert in alerts: status = alert.get("status") name = alert["labels"].get("alertname") instance = alert["labels"].get("instance") summary = alert["annotations"].get("summary", "") msg = {"msgtype": "text", "text": {"content": f"[{status}] {name} - {instance}: {summary}"}} requests.post(DINGTALK_WEBHOOK, json=msg) return "ok" if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)把脚本部署在一台能被 Alertmanager 访问的机器上,配置好钉钉机器人的 webhook token,然后在 alertmanager.yml 里把 webhook URL 指向这个服务,reload 后就能在手机上收到告警消息了。同样的思路也可以对接企业微信机器人或者飞书,核心只是换一个 webhook 地址。
7.5 告警状态的完整生命周期
理解告警状态流转,能让排错时少绕弯子。一个告警规则从触发到通知,会经历 inactive、pending、firing、resolved 四个状态。条件刚满足时进入 pending,持续超过for设定的时间后变成 firing,Alertmanager 这时才发送通知;条件恢复后进入 resolved,并发送恢复通知。
很多时候你觉得"告警没触发",其实它可能在 pending 状态等for窗口,或者已经被 Alertmanager 静默了。排查时先看 Prometheus 的 Alerts 页面,确认规则的状态,再去 Alertmanager 的 UI 里看是否被抑制或静默,这两个页面基本能定位 90% 的问题。
8. 容器化部署:Docker Compose 和 k8s 的落地姿势
8.1 用 Docker Compose 一键拉起整套监控栈
如果你已经习惯容器化运维,或者只是想在开发环境快速验证,Docker Compose 是最省事的方式。直接写一个docker-compose.yml:
services: prometheus: image: prom/prometheus:v2.53.0 container_name: prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./rules:/etc/prometheus/rules - prometheus-data:/prometheus ports: - "9090:9090" restart: always alertmanager: image: prom/alertmanager:v0.28.0 container_name: alertmanager volumes: - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml ports: - "9093:9093" restart: always grafana: image: grafana/grafana:11.3.0 container_name: grafana environment: - GF_SECURITY_ADMIN_PASSWORD=admin volumes: - grafana-data:/var/lib/grafana ports: - "3000:3000" depends_on: - prometheus restart: always volumes: prometheus-data: grafana-data:本地目录下的prometheus.yml和alertmanager.yml可以沿用前面所有配置,直接挂进容器即可。执行docker compose up -d,整套监控栈就起来了。注意容器内时区默认是 UTC,Grafana 显示时间会差 8 小时,需要在 Grafana 的 Settings 里把 Default timezone 改成 Asia/Shanghai。
8.2 容器部署时的数据持久化要点
容器是无状态的,数据必须放在 volume 里。上面的 compose 文件里,prometheus-data和grafana-data就是 named volume,容器删除重建后数据依然保留。
升级版本前,建议先停掉容器、备份数据目录,再换镜像启动。实际操作中遇到过用旧数据跑新版本导致 TSDB 启动失败的情况,所以备份这一步千万别省。备份 Prometheus 数据直接打包 volume 对应的目录就可以,Grafana 则主要备份配置文件和数据源配置,面板数据通常建议用 provisioning 方式统一管理而不是手动点出来的。
8.3 Kubernetes 部署的思路与注意点
在 k8s 集群内部署监控,就不建议手写 Deployment 了,直接用kube-prometheus-stack这个 Helm chart,它把 Prometheus、Grafana、Alertmanager 以及各类 exporter 都打包好,还带上了 ServiceMonitor 机制。
K8s 部署和裸机部署最大区别在于服务发现方式。裸机用 static_configs 或 file_sd_configs,k8s 里则通过kubernetes_sd_configs自动发现 Pod、Service、Node 等资源,配合 ServiceMonitor 声明"我要监控哪个服务的哪些指标"。Prometheus 会自动捕捉这些声明并生成抓取配置。
在 k8s 里部署的明显优势是:新增服务自动被发现,不需要手工改配置;缺点是需要理解 Operator、CRD 的概念,学习曲线比裸机部署陡一些。如果只是小集群(三五台节点),我的建议是先在节点上用二进制方式部署,把基础监控跑起来,后续再迁移到 Operator 方案。
9. 上线运行后最常踩的坑:六条排障经验
9.1 时间不同步导致数据断层和告警误判
这个问题我在前面说过,但值得再强调一次,因为它是我遇到过的"伪装性最强"的坑。现象是 Grafana 图表出现明显断层,Prometheus 查询出来的数据点错位,甚至告警在没有任何异常时反复触发。排查结果是某台被监控机器的系统时间慢了 3 分钟。
解决办法就是所有机器统一部署 chrony,并配置开机自启。监控机和被监控机都要做,不能只做一边。
9.2 TSDB 磁盘被写满
Prometheus 默认只清理超出保留期(我配的是 15 天)的数据,如果某段时间数据量暴涨,磁盘可能在保留期到达前就写满。我当时在一批高基数指标接入后,一天涨了 3GB,差点把监控机磁盘撑爆。
预防手段有三个:一是定期用du -sh /data/prometheus检查数据目录大小变化趋势;二是在 Grafana 上给监控机的磁盘使用率单独建一个告警;三是适当缩短--storage.tsdb.retention.time,比如改成 7 天。
9.3 抓取超时导致丢点
Prometheus 默认scrape_timeout是 10 秒。如果某个 exporter 响应慢,或者网络质量差,抓取就会超时,目标状态变成 DOWN 或抓取失败。在 Grafana 上看到曲线不是平滑的,而是有规律的缺口,基本就是这个原因。
可以在 job 级别单独覆盖抓取超时:
- job_name: "slow-exporter" scrape_timeout: 30s static_configs: - targets: ["192.168.1.20:9104"]但要记住,scrape_timeout 不能设置得比 scrape_interval 还大,否则会不断丢数据。
9.4 高基数标签导致内存暴涨
这是我吃过最大的一次亏。曾经在一套业务监控里,把一个包含用户 ID 的维度做成标签,结果指标数量从几万冲到几百万,Prometheus 内存占用飙到十几个 GB,直接把监控机拖垮。
判断一个标签是否"高基数",最简单的标准是:它的取值数量是否随业务量线性增长。机器名、环境名、服务名可以当标签;用户 ID、会话 ID、请求 URL 绝对不能当标签。如果已经很混乱了,可以通过promtool analyze分析 TSDB 的 series 数量分布,找出占比重最大的指标,然后调整采集端。
9.5 告警反复触发刷屏
告警配置完成后,最常遇到的问题就是通知太频繁。刚开始我把 repeat_interval 设成 1 小时,结果一次小故障,群里被刷了几十条同样的消息。
要解决这个问题,重点在于理解group_wait和repeat_interval的配合。group_wait设成 10s 左右,让同一时间触发的一组告警合并发送;repeat_interval设成 4 小时以上,保证一条告警至少 4 小时才重复一次。对于短时抖动类告警,还可以在规则里把for设置得更长,比如 5 分钟,让真正的持续故障才触发告警。
9.6 Grafana 图空白但 targets 状态正常
面板显示 No data 是最常见的 Grafana 排错场景。按这个顺序排查:先确认查询里用的指标名字是否存在,去 Prometheus 的 Graph 页面执行一遍同样查询,如果有数据,问题出在 Grafana 侧。常见原因有:面板的数据源选错(多数据源环境容易发生)、面板变量名和实际标签不匹配、查询时间范围太大导致性能问题。
另一个低调的坑是:模板导入后,默认查询的 job 名和你的实际 job 名对不上。比如模板写的job="node_exporter",而我配置的 job_name 是linux-node,结果整个面板都空白。解决办法是去 Prometheus 的 Targets 页面确认实际的 job 标签值,然后在 Grafana 面板的查询里全局替换,或者在模板 dashboard 设置里修改变量的默认值。
写在最后的一点体会
整个部署流程跑下来,我最深的感受是:监控系统不是一次搭完就结束的,它必须跟着业务一起演进。第一次部署只需要五个组件加三台机器,跑通主机和数据库监控;跑顺之后,再逐步把中间件、业务指标、k8s 集群纳进来,每次都加一小块,系统性地扩展。
还有一个建议:生产环境监控机的告警一定要做成多通道,钉钉群负责日常告警,邮件作为兜底。我在某个晚上遇到过 webhook 转发服务因为服务器重启而失效的情况,虽然监控在正常跑,但告警完全没发出去,第二天早上才发现。所以现在我会在监控机上加一个定时任务,定期探测告警链路是否可用,这个习惯帮我在之后的运行中避免了好几次"假安全"。
最后再分享一个小技巧:每次调整告警规则或者抓取配置之前,先执行一遍promtool check config和promtool check rules。这两条命令花不了 10 秒钟,但能省掉触发规则语法错误导致整个规则组不加载的麻烦。等你把整套体系跑起来,再回到"登录服务器敲命令"的老路上看一眼,大概率就再也不想回去了。