一套数据监控方案Prometheus/Grafana 与 SkyWalking/Elasticsearch 的选型与搭建
一、先说清楚这四个东西到底是什么
写之前先把概念摆正,不然后面配置容易串。这四个组件里其实只有两种角色:采集/存储,和展示。用四张卡片一次看明白:
Prometheus
CNCF 毕业项目,定位是「指标(metrics)监控 + 时间序列数据库」。核心工作方式一句话:主动去抓(scrape)。配置里写好一批目标地址,它每隔 15 秒自己跑过去把 /metrics 接口的数据拉回来,存进自己的时序数据库(TSDB)。数据模型是「指标名 + key-value 标签」,查询用 PromQL,告警规则也是 PromQL 写的。Go 静态编译的单文件,部署几乎零依赖。
⚠️ 默认本地存 15 天(可配),单机为主;集群高可用得靠 Thanos、Cortex。它不擅长存日志和链路。
Grafana
纯可视化层,自己不采集、不存储任何数据。把各种数据源(Prometheus、MySQL、Elasticsearch、InfluxDB……)接进来,画成仪表盘。关键点:只要数据源支持,什么都能画——接 Prometheus 画指标,接 MySQL 写 SQL 查业务表,接 ES 做日志检索面板,全行。所以跟 Prometheus 是「黄金搭档」,但两者不绑定,本文里它同时出现在两个组合中就是这个道理。
SkyWalking
Apache 顶级项目,APM(应用性能监控)+ 分布式链路追踪。盯的东西比 Prometheus 更「应用层」:一次请求穿过 网关 → 订单 → 支付 → 数据库,整条调用链各节点耗时、哪里慢、哪里报错,它能还原出来。架构分三块:
- Agent 探针:挂应用进程里,gRPC 上报,Java 用
-javaagent,无需改代码; - OAP:接收端,分析聚合后再写存储;
- UI + 存储:自带 Booster UI,存储可插拔(推荐 ES7/8)。
Elasticsearch
本质是搜索引擎(分布式、倒排索引、近实时),在监控方案里通常当存储引擎用:写快、按时间建索引、聚合强。配 Logstash + Kibana 叫 ELK。但在「SkyWalking + ES」里 Kibana 不是必须的——SkyWalking UI 直接读 ES,链路图、拓扑图都是它自己画的,ES 只是安静存数据。
⚠️ 7.11 起改用 SSPL/Elastic License 2.0,与 Apache 2.0 不兼容。有顾虑可选 7.11 前版本或换 OpenSearch。
二、两个组合怎么选:一张表看明白
| 比较项 | Prometheus + Grafana | SkyWalking + Elasticsearch |
|---|---|---|
| 核心定位 | 基础设施 + 业务指标监控 | 应用性能 + 分布式链路追踪 |
| 数据采集方式 | Pull,服务端主动去抓 /metrics |
Push,Agent 探针主动上报 gRPC |
| 采集端形态 | Exporter(node / mysqld / cadvisor 等) | 各语言 Agent(Java/Go/Python…)+ 插件生态 |
| 数据类型 | 指标(metrics)为主,时间序列数值 | 指标 + 链路 trace + 日志 + 拓扑关系 |
| 存储 | Prometheus 自带 TSDB(本地磁盘) | SkyWalking OAP 写入 Elasticsearch |
| 查询语言 | PromQL,灵活强大,能算速率、分位、聚合 | OAL/MAL(内部聚合),UI 直接出图,一般不直接写查询 |
| 可视化能力 | Grafana 仪表盘生态成熟,模板海量,可叠加多数据源 | SkyWalking 自带 UI,拓扑图、服务地图、trace 瀑布图开箱即用 |
| 链路追踪 | 需配合 OpenTelemetry/Tempo/Jaeger,本身不强项 | 原生强项,自动还原调用链,慢 SQL、瓶颈一目了然 |
| 服务拓扑 | 需手动配 + 依赖 Service Mesh 元数据 | 自动生成服务依赖拓扑 |
| 告警 | 内建 Alertmanager,PromQL 规则,路由分组抑制齐全 | 需外接(Webhook、Prometheus 兼容格式或自身告警规则) |
| 接入成本 | 低,加个 Exporter + 配 scrape 即可 | 中高,需挂载 Agent、调字节码增强、规划 ES 集群 |
| 资源开销 | 单进程几百 MB 起步,很轻 | ES 是内存大户,需 bootstrap.memory_lock、vm.max_map_count 调优,OAP 也要 JDK |
| 典型场景 | CPU/内存/磁盘、MySQL 连接数、Redis、Docker、K8s 资源监控 | 微服务调用链排查、接口耗时分析、慢 SQL 定位、服务依赖梳理 |
| 一句话总结 | 「系统健不健康」的指标大盘 | 「请求为什么慢」的链路诊断 |
三、Prometheus + Grafana 搭建
3.1 环境说明
- 操作系统:Ubuntu Server 22.04 / 24.04
- 容器:Docker + Docker Compose V2(
docker compose,不是旧版docker-compose) - 版本:
prom/prometheus:v3.14.0、grafana/grafana:12.x、prom/mysqld-exporter:v0.15.x
前置就一条:sudo apt install docker.io docker-compose-plugin,然后把当前用户加进 docker 组。
3.2 工作流程图
箭头方向的三个要点:
1. Pull 架构:Prometheus 是主动方,Exporter 被动暴露 HTTP 接口,这是它和 SkyWalking 最大的架构差异;
2. mysqld-exporter → MySQL:它不是被抓的,而是去查 MySQL 的 performance_schema,把结果转成 metrics 再被 Prometheus 抓,所以要先建监控账号(见 3.6);
3. Grafana 代理模式(access: proxy):浏览器不直接连 Prometheus,只暴露 3000 端口,运维友好。
3.3 目录结构
~/monitoring/
├── docker-compose.yml
├── prometheus/
│ ├── prometheus.yml
│ └── rules/
│ └── alert.yml
└── grafana/
└── provisioning/
├── datasources/
│ └── prometheus.yml
└── dashboards/
└── provider.yml
3.4 docker-compose.yml
version: "3.8"
services:
prometheus:
image: prom/prometheus:v3.14.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/rules:/etc/prometheus/rules:ro
- prom_data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=30d
- --web.enable-lifecycle
networks: [mon]
mysqld-exporter:
image: prom/mysqld-exporter:v0.15.1
container_name: mysqld-exporter
restart: unless-stopped
ports:
- "9104:9104"
environment:
DATA_SOURCE_NAME: "exporter:ExporterPass123@(192.168.1.20:3306)/"
command:
- --collect.info_schema.innodb_metrics
- --collect.info_schema.processlist
- --collect.info_schema.tables
depends_on: [mysql]
networks: [mon]
# MySQL 不在本机可删掉这一段,只把上面 IP 改成实际地址即可
mysql:
image: mysql:8.0
container_name: mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: RootPass123
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks: [mon]
grafana:
image: grafana/grafana:12.0.0
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- grafana_data:/var/lib/grafana
depends_on: [prometheus]
networks: [mon]
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
networks: [mon]
networks:
mon:
driver: bridge
volumes:
prom_data:
grafana_data:
mysql_data:
3.5 prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'docker'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'mysql'
static_configs:
- targets: ['mysqld-exporter:9104']
labels:
instance: 'main-mysql'
3.6 MySQL 监控账号(必做)
mysqld-exporter 需要专门权限,用 root 会报权限错误,而且建议限流:
CREATE USER 'exporter'@'%' IDENTIFIED BY 'ExporterPass123' WITH MAX_USER_CONNECTIONS 10;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%';
GRANT SELECT ON performance_schema.* TO 'exporter'@'%';
FLUSH PRIVILEGES;
MAX_USER_CONNECTIONS 10 这条很重要——监控抓取在高并发下可能把数据库连爆,这是生产环境踩过的坑。
3.7 Grafana 数据源配置(provisioning)
用 provisioning 的好处是:数据源写进文件,容器重启不丢,还能进 Git 版本管理。
grafana/provisioning/datasources/prometheus.yml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
jsonData:
timeInterval: 15s
httpMethod: POST
如果还想把 MySQL 业务库直接挂进来做报表(文章要求里提到了 MySQL 数据源),再追加一段:
- name: MySQL-Business
type: mysql
access: proxy
url: mysql:3306
database: orders
user: grafana_ro
secureJsonData:
password: GrafanaRoPass
jsonData:
maxOpenConns: 10
grafana/provisioning/dashboards/provider.yml:
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards
注意:provisioning 的数据源在 UI 里是锁定的(editable: false),想改要去改文件。另外 provisioning 目录挂载时只挂载到 provisioning 这一层,不要覆盖整个 /etc/grafana,否则默认配置丢了 Grafana 起不来。
3.8 启动与验证
cd ~/monitoring
docker compose up -d
# 看状态
docker compose ps
docker compose logs -f prometheus
# 验证 exporter 有数据输出
curl http://localhost:9104/metrics | grep mysql_up
浏览器打开 http://host:3000,默认账号 admin/admin123,左侧 Connections → Add new connection → Prometheus → Save & test,看到 Successfully queried the Prometheus API 就通了。
仪表盘建议直接导入官方模板:MySQL 用 ID 7362,Node Exporter 用 ID 1860,在 Dashboards → Import 里填数字即可。自己写面板时 PromQL 常用套路:
# CPU 使用率(node-exporter)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# MySQL 当前连接数
mysql_global_status_threads_connected
# 慢查询率
rate(mysql_global_status_slow_queries[5m])
到这里 Prometheus + Grafana 这条线就跑通了:指标采集 → 存储 → 查询 → 图表 → 告警 全链路闭环。
四、SkyWalking + Elasticsearch 搭建
4.1 工作流程图
对比 Prometheus 的三个关键差异:
1. 数据流是 Push:Agent 主动往 OAP 推,不是 OAP 来抓。网络策略上是应用 → 服务端开放 11800;
2. Agent 挂在应用进程内:Java 是字节码增强,无需改代码,但会占用一定堆内存,JIT 预热期有轻微性能损耗;
3. ES 是核心依赖:OAP 本身不存数据(默认 H2 仅用于试玩),所有 trace/metric/log 都写进 ES 索引,命名规则如 sw_metrics-all-YYYYMMDD、sw_segment-YYYYMMDD、sw_log-YYYYMMDD。
4.2 版本兼容(必看,踩坑重灾区)
| SkyWalking | Elasticsearch | 说明 |
|---|---|---|
| 10.x | ES 7.x / 8.x | 统一用 selector: elasticsearch,自动识别版本,不用再区分 es7/es8 安装包 |
| 9.x 及更早 | ES 6 用 selector: elasticsearch;ES 7 用 selector: elasticsearch7,下载时也要选对应 -es7 包 |
4.3 docker-compose.yml
version: "3.8"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
container_name: es01
restart: unless-stopped
environment:
- discovery.type=single-node
- xpack.security.enabled=true
- xpack.security.http.ssl.enabled=false
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
networks: [sw]
kibana:
image: docker.elastic.co/kibana/kibana:8.15.0
container_name: kibana
restart: unless-stopped
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://es01:9200
depends_on: [elasticsearch]
networks: [sw]
oap:
image: apache/skywalking-oap-server:10.4.0
container_name: oap
restart: unless-stopped
environment:
- SW_STORAGE=elasticsearch
- SW_STORAGE_ES_CLUSTER_NODES=http://es01:9200
- SW_ES_USER=elastic
- SW_ES_PASSWORD=ElasticPass123
- SW_STORAGE_ES_SSL_VERIFICATION=false
- SW_NAMESPACE=skywalking
ports:
- "11800:11800" # gRPC,Agent 上报
- "12800:12800" # HTTP,UI 查询
depends_on: [elasticsearch]
networks: [sw]
ui:
image: apache/skywalking-ui:10.4.0
container_name: skywalking-ui
restart: unless-stopped
environment:
- SW_OAP_ADDRESS=http://oap:12800
ports:
- "8080:8080"
depends_on: [oap]
networks: [sw]
networks:
sw:
driver: bridge
volumes:
es_data:
4.4 OAP 存储配置(application.yml 关键段)
容器方式其实不需要手动改配置文件——上面 compose 里的环境变量会覆盖默认值。但用二进制部署或想看清结构时,核心就是这一段:
storage:
selector: ${SW_STORAGE:elasticsearch}
elasticsearch:
namespace: ${SW_NAMESPACE:"skywalking"}
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
user: ${SW_ES_USER:""}
password: ${SW_ES_PASSWORD:""}
sslVerification: ${SW_STORAGE_ES_SSL_VERIFICATION:true}
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1}
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1}
bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:1000}
flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10}
concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2}
namespace:所有索引加前缀,如skywalking_sw_metrics-all-20260115,多环境隔离有用;indexShardsNumber/indexReplicasNumber:单节点 ES 必须把副本设为 0,否则集群状态永远 Yellow;bulkActions=1000:每 1000 条批量写一次 ES,吞吐与实时性的折中;sslVerification:测试环境false,生产配合法 CA。ES 8.x 默认开 SSL,开了xpack.security.http.ssl.enabled=true就要改https://es01:9200并配证书。
4.5 应用接入 Agent(Java 示例)
这是 SkyWalking 真正发挥价值的地方。以 Spring Boot 为例:
# 1. 下载 agent 包,解压得到 skywalking-agent/ 目录
wget https://archive.apache.org/dist/skywalking/10.4.0/apache-skywalking-java-agent-9.5.0.tgz
tar -xzf apache-skywalking-java-agent-9.5.0.tgz
# 2. 启动应用时挂载 agent
java -javaagent:/path/to/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=192.168.1.10:11800 \
-jar order-service.jar
-javaagent:JVM 参数,必须放在 -jar 之前,放后面不生效,新手最容易犯的错;service_name:服务名,UI 拓扑图的分组依据,建议用业务名而不是主机名;backend_service:OAP 的 gRPC 地址(11800 不是 12800),12800 是给 UI 用的 HTTP 端口;- 容器化部署时把 agent 目录通过 volume 挂进 Pod,或用 Optional 插件按需启用。
其它语言:Go 用 go2sky,Python 用 skywalking-python,原理一样,都是应用内 SDK + gRPC 上报。
4.6 启动与验证
cd ~/skywalking
docker compose up -d
# 1. 确认 ES 健康(首次启动约 30 秒)
curl -u elastic:ElasticPass123 http://localhost:9200/_cluster/health
# 2. 看 OAP 是否开始建索引(出现 sw_ 开头索引就成功)
curl -u elastic:ElasticPass123 http://localhost:9200/_cat/indices | grep sw_
# 3. 查看 OAP 日志
docker compose logs -f oap
浏览器打开 http://host:8080,左侧菜单:Dashboard(服务概览)、Topology(拓扑图,微服务依赖自动画出)、Trace(链路列表)、Log(日志,需单独配置)。
验证数据通不通最简单:随便调几次业务接口,再去 Trace 页面刷新,能看到调用链就说明 Agent → OAP → ES 全链路 OK。Kibana 可选——想直接在 ES 查原始 trace 做定制分析,打开 http://host:5601,用索引模式 sw_segment-* 建 Data View 用 KQL 检索即可,日常监控用 SkyWalking UI 足够。
4.7 常见坑速查
| 现象 | 原因 | 解决 |
|---|---|---|
| ES 启动后自动退出 | max_map_count 未设置 | sysctl -w vm.max_map_count=262144 |
| ES 集群状态 Yellow | 单节点但副本数=1 | indexReplicasNumber: 0 |
| OAP 连不上 ES,报 SSL 错 | 8.x 默认开 SSL | xpack.security.http.ssl.enabled=false(测试)或配证书 |
| Agent 无数据上报 | gRPC 端口写错 | 确认是 11800,不是 12800 |
-javaagent 不生效 | 参数位置在 -jar 之后 | 移到 -jar 之前 |
| SkyWalking UI 空白 | OAP 未就绪 | 等 OAP 完全启动,日志出现 OAP starts up successfully |
五、落地建议
两套都搭完后,我建议的监控分层是这样:
优先级的经验之谈:
- 第一周:先把 Prometheus + Grafana 跑起来,接 node-exporter + mysqld-exporter,CPU/内存/磁盘/连接数/慢查询这五块先有数;
- 第二周:配告警规则,把「磁盘 > 85%」「MySQL 连接数打满」「服务 down」这几条最救命的规则先建上;
- 第三周:微服务上规模再加 SkyWalking,先把 Agent 接进 1-2 个核心服务试水,确认 ES 容量够再全量铺开;
- ES 容量要提前规划:链路全量采集数据量很大,SkyWalking 默认按天建索引,配合 ES 的 ILM 做热温冷架构,或干脆设采样率。中小团队 10%-20% 采样 + 错误/慢请求全采通常够用。