概述
你有没有遇到过这种情况:用户反馈"系统好卡",你打开 Grafana 看了一堆面板,CPU 正常、内存正常、网络也正常,但用户就是说慢。这时候你需要的不是更多的指标面板,而是一条完整的请求链路——从用户点下按钮到数据库返回结果,每一跳花了多少时间,卡在哪一步。
这就是 APM(Application Performance Monitoring,应用性能监控)干的事。
简单说:日志告诉你发生了什么,指标告诉你系统是否健康,APM 告诉你为什么慢、慢在哪里、影响了谁。三者各管一摊,缺一不可。
本文从实际选型角度出发,拆解主流开源和商业 APM 工具的架构差异、适用场景和坑点。不吹不黑,每家都有适合的场景,关键看你的团队规模、技术栈和预算。
APM 要解决什么问题
先说清楚为什么需要 APM,而不是直接跳到工具对比。
微服务架构下的"链路黑盒"
单体应用时代,一个请求从入口到数据库都在一个进程里完成,打个断点就能调试。微服务拆分后,一个用户请求可能经过 API 网关 → 认证服务 → 订单服务 → 支付服务 → 消息队列 → 库存服务 → 数据库,中间还穿插着 Redis 缓存和第三方 API 调用。
任何一个环节变慢,整体就慢。但你看日志只能看到单个服务的视角,没法把整条链路串起来。这就像你在医院看病,内科查完说没问题让你去外科,外科查完说没问题让你去骨科——每个科室都说自己没问题,但你就是难受。APM 就是那个能把你所有科室检查结果串起来看的"全科医生"。
APM 的三个核心能力
| 能力 | 解决什么问题 | 类比 |
|---|---|---|
| 分布式追踪(Distributed Tracing) | 一个请求经过哪些服务、每跳花了多久 | 快递物流追踪,每个中转站都有时间戳 |
| 性能剖析(Profiling) | 某个函数执行慢,CPU 花在哪了 | 体检报告,精确到每个器官的指标 |
| 错误追踪(Error Tracking) | 异常发生在哪个服务的哪行代码 | 车辆故障码,直接定位到故障部件 |
分布式追踪是 APM 最核心的能力。它通过在请求入口生成一个唯一的 Trace ID,然后通过 HTTP Header 或 RPC 上下文传递到下游服务,每个服务在自己的处理过程中记录一个 Span(你可以理解为链路上的一个节点),最终拼出完整的调用树。
关键概念速览
| 术语 | 含义 | 说明 |
|---|---|---|
| Trace | 一次完整的请求链路 | 由多个 Span 组成的有向无环图(DAG) |
| Span | 链路上的一个操作节点 | 包含操作名、起止时间、标签、日志 |
| Context Propagation | 上下文传递 | Trace ID 通过 HTTP Header 在服务间传递 |
| Sampling | 采样 | 不可能记录所有请求,按策略采样部分 |
| Instrumentation | 探针/埋点 | 代码层面自动或手动注入追踪逻辑 |
采样策略很关键。线上流量大的时候,全量记录 Trace 会把存储和 CPU 吃干。常见做法是头部采样(Head-based Sampling)——在请求入口决定是否记录,要么整条链路全记,要么全不记。尾部采样(Tail-based Sampling)更精细——在链路结束时根据条件(比如耗时超过阈值、出现错误)决定是否保留,但实现复杂度高,需要中间层缓存完整链路。
开源 APM 工具全景对比
2026 年开源 APM 领域的格局已经比较清晰了。按照功能覆盖范围,可以分成三类:
- 一体化 APM:链路 + 指标 + 告警一站式,代表是 SkyWalking、Pinpoint
- 链路追踪专精:只管 Trace,需要配合 Prometheus + Grafana 做指标,代表是 Jaeger、Zipkin
- 可观测拼装栈:Prometheus + Grafana + Loki + Tempo(简称 LGTM 栈),灵活但集成成本高
五大开源方案速览
| 工具 | 定位 | 语言支持 | 存储后端 | 适合场景 |
|---|---|---|---|---|
| Apache SkyWalking | 一体式 APM | Java/Go/Python/Node.js/PHP 等 | ES/BanyanDB/H2 | Java 微服务为主、需要拓扑图 |
| Jaeger | CNCF 链路追踪 | 多语言(OTel SDK) | ES/Cassandra/Badger | 只需 Trace、已有 Prometheus 栈 |
| Zipkin | 轻量链路追踪 | 多语言 | ES/MySQL/Cassandra | 小规模服务、快速上手 |
| Grafana Tempo | 分布式追踪后端 | OTel/Jaeger/Zipkin 协议 | 对象存储(S3/GCS) | 已用 Grafana、想要低成本长期存储 |
| Pinpoint | Java 字节码 APM | 仅 Java | HBase | 纯 Java 微服务、无侵入埋点 |
Apache SkyWalking
SkyWalking 是 Apache 基金会顶级项目,国内使用率很高。它的核心卖点是开箱即用——装上 OAP(Observability Analysis Platform)服务端 + Agent 探针,自动生成服务拓扑图、链路追踪、指标监控和告警,不需要额外配置 Prometheus。
技术架构上,SkyWalking 用 Agent 在应用侧做字节码增强(Java 用 JavaAgent,其他语言用 gRPC 手动埋点),数据通过 gRPC/HTTP 发送到 OAP 服务端,OAP 负责聚合分析和存储。存储支持 Elasticsearch、BanyanDB(SkyWalking 自研的时序数据库)和 H2(仅测试用)。
优势:
- 拓扑图自动发现,服务依赖关系一目了然
- 一站式,不需要额外拼装组件
- 国内社区活跃,文档中文友好
- 支持 Service Mesh 场景(Istio/Envoy 数据面)
坑点:
- OAP 服务端对内存消耗不小,生产环境至少 8GB 起步
- ES 存储在高基数场景下性能会掉,BanyanDB 还在成熟期
- 非 Java 语言的 Agent 功能比 Java 弱不少
- 规则配置方式偏重 XML/YAML,学习曲线有点陡
一个典型的 SkyWalking 部署架构:
# docker-compose-skywalking.yml
version: '3.8'
services:
oap:
image: apache/skywalking-oap-server:10.1.0
ports:
- "11800:11800" # gRPC 接收 Agent 数据
- "12800:12800" # HTTP REST API
environment:
SW_STORAGE: elasticsearch
SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
SW_CORE_RECORD_DATA_TTL: 7 # 链路数据保留 7 天
SW_CORE_METRICS_DATA_TTL: 30 # 指标数据保留 30 天
depends_on:
- elasticsearch
restart: unless-stopped
ui:
image: apache/skywalking-ui:10.1.0
ports:
- "8080:8080"
environment:
SW_OAP_ADDRESS: http://oap:12800
depends_on:
- oap
restart: unless-stopped
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
volumes:
- es-data:/usr/share/elasticsearch/data
restart: unless-stopped
volumes:
es-data:
Java 应用接入只需要一行参数:
# 启动 Java 应用时挂载 SkyWalking Agent
java -javaagent:/path/to/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=oap:11800 \
-jar order-service.jar
Go 应用需要手动埋点(SkyWalking Go Agent 还在发展中),使用 OTel SDK + SkyWalking exporter:
package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// 初始化 tracer,将数据发送到 SkyWalking OAP
func initTracer() func() {
// 实际生产中配置 OTLP exporter 指向 SkyWalking OAP 的 OTLP 接收端口
// SkyWalking 9.x+ 原生支持 OTLP 协议
tp, err := initOTLPProvider("oap:11800", "order-service")
if err != nil {
log.Fatal(err)
}
otel.SetTracerProvider(tp)
return func() { tp.Shutdown(context.Background()) }
}
// 在 HTTP handler 中创建 span
func handleOrder(ctx context.Context, orderID string) error {
ctx, span := tracer.Start(ctx, "handleOrder")
defer span.End()
span.SetAttributes(attribute.String("order.id", orderID))
// 调用下游支付服务,trace context 自动透传
if err := callPaymentService(ctx, orderID); err != nil {
span.RecordError(err)
return err
}
return nil
}
Jaeger
Jaeger(德语"猎人")是 Uber 开源、后来捐给 CNCF 的分布式追踪项目。它只做一件事——Trace 存储、查询和可视化,不做指标和告警。
Jaeger 的定位很明确:如果你已经有 Prometheus + Grafana 做指标监控,只缺一个链路追踪后端,那 Jaeger 是最干净的选择。它不试图包揽一切,但 Trace 做得很扎实。
优势:
- CNCF 毕业项目,和 Kubernetes 生态融合好
- 原生支持 OpenTelemetry 协议(OTLP)
- UI 简洁,Trace 瀑布图可读性强
- 支持自适应采样(Adaptive Sampling),根据流量自动调整采样率
坑点:
- 只管 Trace,指标和日志要另搭
- 存储后端选型让人头疼——ES 太重,Cassandra 运维复杂,Badger 只适合单机
- 社区活跃度不如 SkyWalking
Jaeger 支持 OTLP 直连,用 OpenTelemetry SDK 埋点后直接发给 Jaeger:
# Jaeger all-in-one 部署(仅测试用)
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger
spec:
replicas: 1
selector:
matchLabels:
app: jaeger
template:
metadata:
labels:
app: jaeger
spec:
containers:
- name: jaeger
image: jaegertracing/all-in-one:1.60
ports:
- containerPort: 16686 # UI
- containerPort: 4317 # OTLP gRPC
- containerPort: 4318 # OTLP HTTP
env:
- name: COLLECTOR_OTLP_ENABLED
value: "true"
- name: SPAN_STORAGE_TYPE
value: badger
- name: BADGER_EPHEMERAL
value: "false"
- name: BADGER_DIRECTORY_VALUE
value: /data/values
- name: BADGER_DIRECTORY_KEY
value: /data/keys
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: jaeger-pvc
Zipkin
Zipkin 是 Twitter 开源的链路追踪系统,比 Jaeger 更早。它的设计理念是"够用就好"——功能不多但稳定,部署简单。
老实说,新项目我不太推荐 Zipkin 了。功能上 Jaeger 完全覆盖了 Zipkin 的能力,而且 Jaeger 原生支持 OTLP,生态更活跃。Zipkin 的优势在于历史悠久、SDK 生态广,但新项目直接上 Jaeger 或 Tempo 更好。
Grafana Tempo
Tempo 是 Grafana Labs 出的高性能 Trace 后端,主打一个卖点:用对象存储替代数据库。
传统 Trace 存储用 ES 或 Cassandra,成本高、运维重。Tempo 把 Trace 数据存在 S3/GCS/MinIO 这类对象存储上,成本降一个数量级,而且容量几乎无限。查询时通过 Trace ID 直接检索,不做全文检索(这是它和 Jaeger 的核心差异)。
优势:
- 存储成本极低,S3 一个月几美元就能存海量 Trace
- 和 Grafana 深度集成,Trace 指标日志三联动
- 架构简单,只有 ingester + querier + compactor 三个组件
坑点:
- 必须知道 Trace ID 才能查——不支持按服务名 + 时间范围搜索 Trace 列表(v2.0 后有 TraceQL 改善了不少)
- 依赖对象存储,本地部署需要 MinIO
Tempo 适合已经用 Grafana 全家桶的团队。如果你的指标用 Prometheus、日志用 Loki,那 Trace 用 Tempo 是顺理成章的选择:
# Tempo + MinIO 部署
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
ingester:
max_block_duration: 5m
compactor:
compaction:
block_retention: 48h
storage:
trace:
backend: s3
s3:
bucket: tempo-traces
endpoint: minio:9000
access_key: minioadmin
secret_key: minioadmin
insecure: true
Pinpoint
Pinpoint 是韩国 Naver 公司开源的 APM,特点是纯 Java、零侵入。它通过字节码增强自动埋点,Java 应用挂上 Agent 就能监控,不需要改一行代码。
如果你是纯 Java 技术栈,Pinpoint 的无侵入体验确实好。但一旦有 Go、Python、Node.js 服务,Pinpoint 就无能为力了。而且它的存储依赖 HBase,运维复杂度不低。现在新项目我会更推荐 SkyWalking——同样是字节码增强,但多语言支持和社区活跃度都更好。
商业 APM 工具对比
商业 APM 的核心优势是省心——不用自己运维存储后端,有专业团队做异常检测算法,集成度更高。但价格也不便宜。
| 维度 | Datadog | Dynatrace | New Relic |
|---|---|---|---|
| 定位 | 云原生监控平台 | AI 驱动全栈 APM | 开发者友好 APM |
| 部署模式 | 纯 SaaS | SaaS 为主,私有化受限 | SaaS 轻量化 |
| 自动发现 | Agent + 850+ 集成 | OneAgent 自动插桩 | OTel-native |
| AI 引擎 | Watchdog 异常检测 | Davis 因果 AI | Applied Intelligence |
| 价格参考 | ~$3000-5000/月(50 主机) | ~$69/主机/月 | ~$49/用户/月 |
| 数据合规 | 数据跨境 | 数据默认跨境 | 跨境存储 |
| 适合场景 | 云原生、K8s 重度用户 | 大型企业、需因果分析 | 中小团队、快速上手 |
Datadog 2025 年 Q3 季度营收 8.857 亿美元,市值约 402 亿美元,服务覆盖全球 95% 的财富 500 强企业。连续五年被 Gartner 评为可观测性平台魔力象限领导者。参考来源:2026 年可观测厂商选型指南
Datadog
Datadog 是目前最火的商业可观测平台,没有之一。它的杀手锏是集成广度——850+ 个开箱即用的集成,覆盖 AWS/GCP/Azure 所有主流云服务、Kubernetes、数据库、消息队列、APM、日志、安全,几乎你能想到的都有。
Datadog 的 APM 通过 dd-trace 库自动埋点,支持 Java/Go/Python/Node.js/.NET/PHP/Ruby。数据发送到 Datadog SaaS 后端,UI 里可以直接从指标下钻到 Trace 再到日志,三联动体验流畅。
但 Datadog 的计费方式需要警惕:按主机 + 按模块收费,50 台主机的 APM + 日志 + 基础设施监控,月费很容易上到 3000-5000 美元。而且数据全部存在 Datadog 的 SaaS 上,国内企业需要考虑数据跨境合规问题。
Dynatrace
Dynatrace 的核心差异化是 Davis AI 引擎——它不是简单的阈值告警,而是基于因果分析自动定位根因。比如一个服务变慢,Davis 能告诉你"因为依赖的数据库查询变慢了,而查询变慢是因为某个索引被删除了"。
OneAgent 是 Dynatrace 的数据采集方式,一个 Agent 自动完成全栈监控——从基础设施到应用代码到用户体验。安装后零配置自动发现,这点对大型企业很有吸引力。
但 Dynatrace 的闭源程度很高,OneAgent 是专有的,数据格式不开放。一旦用上,迁移成本极高。价格方面约 $69/主机/月,对于大规模部署成本不低。
New Relic
New Relic 是 APM 领域的老牌玩家,开发者体验做得好。NRQL(New Relic Query Language)是一套类似 SQL 的查询语言,灵活度很高。而且 New Relic 是最早拥抱 OpenTelemetry 的商业厂商,OTel-native 的架构让数据可移植性更好。
但 New Relic 的基础设施监控和数据库深度监控相对薄弱,MySQL 慢查询分析、Redis 缓存命中率这些 DBA 关心的场景不如 Datadog 完善。大规模部署时按用户计费的模式容易失控。
OpenTelemetry:厂商中立的未来
聊 APM 选型绕不开 OpenTelemetry(简称 OTel)。这是 CNCF 的第二活跃项目(仅次于 Kubernetes),目标是统一可观测性三大支柱(Trace/Metrics/Logs)的数据采集标准。
为什么 OTel 重要
OTel 之前,每家 APM 厂商都有自己的 SDK:Jaeger 用 Jaeger client,Zipkin 用 Brave/Zipkin client,SkyWalking 用自己的 Agent。你选了一家就得用它的 SDK,换厂商就要改所有服务的埋点代码——这是典型的厂商锁定。
OTel 解决的是埋点标准化问题:应用侧只用 OTel SDK 埋点,数据通过 OTLP 协议发出去,后端可以是 Jaeger、SkyWalking、Tempo、Datadog 任何一个。换后端不用改应用代码。
OpenTelemetry 于 2019 年由 OpenTracing 和 OpenCensus 合并而来,继承了 OpenTracing 的厂商中立理念和 OpenCensus 的多信号能力。Traces Spec 2021 年达到 Stable,Metrics Spec 2021 年底 Stable,Logs Spec 2023 年中 Stable。参考来源:OpenTelemetry 实战:云原生可观测性三大支柱统一标准
OTel 架构三层分离
┌─────────────────────────────────────────────────────────┐
│ 应用层(Application) │
│ OTel SDK 自动/手动埋点 → 生成 Span/Metric/Log │
└────────────────────────┬────────────────────────────────┘
│ OTLP 协议(gRPC :4317 / HTTP :4318)
▼
┌─────────────────────────────────────────────────────────┐
│ 采集层(Collector) │
│ 接收 → 处理(过滤/采样/批处理)→ 导出 │
└─────────┬───────────────────────┬───────────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────────────────┐
│ Jaeger / Tempo │ │ Prometheus / SkyWalking / ES │
│ (Trace 后端) │ │ (Metrics / 日志后端) │
└──────────────────┘ └──────────────────────────────────┘
OTel Collector 部署
OTel Collector 是 OTel 架构中的核心组件——它是一个数据中转站,负责接收、处理和导出遥测数据。生产环境强烈建议部署 Collector,而不是让应用直连后端:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# 批处理,减少导出请求次数
batch:
timeout: 5s
send_batch_size: 1024
# 内存限制器,防止突发流量 OOM
memory_limiter:
check_interval: 1s
limit_mib: 512
# 尾部采样:只保留慢请求和错误请求
tail_sampling:
decision_wait: 10s
policies:
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
- name: slow
type: latency
latency:
threshold_ms: 500
- name: random_keep
type: probabilistic
probabilistic:
sampling_percentage: 10
exporters:
# 发送到 Jaeger
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
# 发送到 Prometheus(指标)
prometheus:
endpoint: 0.0.0.0:8889
# 发送到 Loki(日志)
loki:
endpoint: http://loki:3100/loki/api/v1/push
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [loki]
应用侧埋点示例(Go)
package main
import (
"context"
"log"
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func initTracer(ctx context.Context, serviceName string) func() {
// 创建 OTLP gRPC exporter,指向 Collector
exporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
if err != nil {
log.Fatalf("failed to create exporter: %v", err)
}
// 配置资源信息(服务名、实例 ID 等)
res, _ := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(serviceName),
semconv.ServiceVersion("v1.2.0"),
semconv.DeploymentEnvironment("production"),
),
)
// 创建 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
// 头部采样:10% 采样率
sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return func() {
_ = tp.Shutdown(ctx)
}
}
func main() {
ctx := context.Background()
shutdown := initTracer(ctx, "api-gateway")
defer shutdown()
tracer := otel.Tracer("api-gateway")
// 使用 otelhttp 自动埋点 HTTP 服务
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
span := trace.SpanFromContext(ctx)
span.SetAttributes(attribute.String("user.agent", r.UserAgent()))
// 调用下游服务,TraceContext 自动透传
callDownstream(ctx, "http://order-service:8080/api/orders")
w.Write([]byte("OK"))
})
wrapped := otelhttp.NewHandler(handler, "api-gateway")
http.ListenAndServe(":8080", wrapped)
}
func callDownstream(ctx context.Context, url string) {
// otelhttp.NewClient 自动注入 Trace Header
client := http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport),
}
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)
if err != nil {
log.Printf("downstream call failed: %v", err)
return
}
defer resp.Body.Close()
}
选型决策框架
说了这么多工具,到底怎么选?我梳理了一个六维选型框架,按照优先级排序:
维度一:技术栈匹配度
| 你的技术栈 | 推荐方案 | 原因 |
|---|---|---|
| 纯 Java 微服务 | SkyWalking / Pinpoint | 字节码增强,零侵入 |
| 多语言微服务(Go/Python/Java 混合) | Jaeger + OTel SDK | OTel 多语言支持好 |
| 已经用 Grafana 全家桶 | Tempo | 生态一致性,Trace 指标日志三联动 |
| 云原生 K8s 重度用户 | Datadog(有预算)/ SkyWalking(开源) | K8s 集成深度 |
| 纯 Go 技术栈 | Jaeger + OTel SDK | Go 原生支持 gRPC,和 Jaeger 天然契合 |
维度二:运维复杂度
开源 APM 最大的成本不是 License,是运维。你得自己管存储后端、做高可用、搞容量规划:
| 方案 | 运维组件数 | 存储运维难度 | 日常维护工作量 |
|---|---|---|---|
| SkyWalking | OAP + ES/BanyanDB | 中(ES 需调优) | 中 |
| Jaeger + ES | Collector + ES | 中 | 中 |
| Tempo + S3 | Ingester + Querier + S3 | 低(S3 免运维) | 低 |
| Datadog | 0(SaaS) | 0 | 极低 |
| 自建 LGTM 栈 | Prometheus + Grafana + Loki + Tempo | 高(4 个组件) | 高 |
维度三:存储成本
Trace 数据量很大。一个中等规模的微服务集群(50 个服务,日均 1 亿请求),10% 采样率,每天产生的 Trace 数据量在 50-200GB 之间。
| 存储方案 | 月成本估算(50GB/天) | 数据保留 | 查询性能 |
|---|---|---|---|
| Elasticsearch | $200-500(3 节点集群) | 7-14 天 | 强(全文检索) |
| Cassandra | $150-300 | 7-30 天 | 中 |
| S3/对象存储 | $15-30 | 30-90 天 | 中(按 Trace ID 查) |
| Datadog SaaS | 包含在 License 中 | 15-30 天 | 强 |
维度四:合规与数据主权
国内企业的数据合规要求越来越严。如果你在金融、政务、医疗等行业,数据出境是红线:
| 方案 | 合规风险 | 解决方式 |
|---|---|---|
| Datadog | 高(数据跨境) | 无国内节点,数据出境 |
| Dynatrace | 高(数据跨境) | 私有化版本功能受限 |
| New Relic | 高(数据跨境) | 无国内节点 |
| SkyWalking | 无 | 自托管,数据在本地 |
| Jaeger + ES | 无 | 自托管,数据在本地 |
维度五:团队规模与能力
| 团队规模 | 推荐路径 | 理由 |
|---|---|---|
| 5 人以下 SRE/运维 | Datadog 或 New Relic | 没人力运维开源方案 |
| 5-15 人 SRE | SkyWalking 或 Tempo | 有能力维护,成本可控 |
| 15 人以上 | 自建 OTel + LGTM | 可定制性高,长期成本最低 |
维度六:TCO(总拥有成本)
别只看 License 费用。开源方案的 TCO 包括:
- 存储成本(ES 集群 / S3 存储费)
- 运维人力(至少 0.5 个 FTE 专职维护)
- 硬件成本(OAP/Collector 节点)
- 培训成本(团队学习曲线)
一个 50 台主机规模的集群,开源方案年 TCO 约 15-30 万人民币(含人力),商业方案约 30-60 万。规模再大,开源的成本优势才体现出来。
生产环境实战建议
建议1:采样策略别偷懒
全量采样听起来美好,实际线上跑两天存储就爆了。根据我的经验,合理的采样策略是:
# 生产环境推荐采样配置
sampling:
# 正常请求:头部采样 5-10%
head_based:
ratio: 0.05
# 慢请求(> 500ms):全量保留
# 错误请求:全量保留
# 通过 OTel Collector 尾部采样实现
tail_based:
policies:
- type: status_code
status_codes: [ERROR]
- type: latency
threshold_ms: 500
- type: probabilistic
sampling_percentage: 5
这样既控制了数据量,又不会漏掉关键问题。
建议2:Collector 必须高可用
OTel Collector 是数据链路的咽喉。它挂了,所有应用的 Trace 都发不出去。生产部署至少 3 个 Collector 实例 + 负载均衡:
# Kubernetes 部署 OTel Collector(Deployment 模式)
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector
spec:
replicas: 3
selector:
matchLabels:
app: otel-collector
template:
metadata:
labels:
app: otel-collector
spec:
containers:
- name: collector
image: otel/opentelemetry-collector-contrib:0.103.0
ports:
- containerPort: 4317 # OTLP gRPC
- containerPort: 4318 # OTLP HTTP
- containerPort: 8888 # Metrics
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
livenessProbe:
httpGet:
path: /health
port: 13133
readinessProbe:
httpGet:
path: /health
port: 13133
---
apiVersion: v1
kind: Service
metadata:
name: otel-collector
spec:
selector:
app: otel-collector
ports:
- name: otlp-grpc
port: 4317
targetPort: 4317
- name: otlp-http
port: 4318
targetPort: 4318
建议3:别一上来就追求完美
见过太多团队选型时纠结半个月,最后什么都没落地。务实做法:
- 第一周:用 OTel SDK 给最核心的 2-3 个服务埋点,后端先用 Jaeger all-in-one 单机跑起来
- 第二周:验证 Trace 数据质量,看链路是否完整、Span 是否有业务上下文
- 第一月:根据实际数据量决定后端选型——量小用 Jaeger + Badger,量大上 Tempo + S3 或 SkyWalking + ES
- 第三月:接入告警、配置 SLO,让 Trace 数据真正服务于故障排查
建议4:Span 标签要带业务上下文
纯技术 Trace(只有 HTTP 方法 + URL + 耗时)用处有限。真正有价值的是带业务上下文的 Trace:
// 好的做法:Span 里带上业务信息
func processOrder(ctx context.Context, order *Order) error {
ctx, span := tracer.Start(ctx, "processOrder")
defer span.End()
// 关键业务属性
span.SetAttributes(
attribute.String("order.id", order.ID),
attribute.String("order.user_id", order.UserID),
attribute.Float64("order.amount", order.Amount),
attribute.String("order.status", order.Status),
)
// 记录关键事件
span.AddEvent("payment_initiated", trace.WithAttributes(
attribute.String("payment.gateway", order.PaymentGateway),
))
if err := validateOrder(ctx, order); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
}
return nil
}
这样在 Jaeger/SkyWalking UI 里搜索 Trace 时,可以直接用 order.id=xxx 过滤,快速定位到具体订单的完整链路。
建议5:监控你的监控系统
APM 系统本身也是服务,它也需要被监控。至少关注:
| 监控指标 | 告警阈值 | 含义 |
|---|---|---|
| Collector 拒绝率 | > 1% | 应用发了数据但 Collector 处理不过来 |
| Collector 导出延迟 | > 5s | 数据积压,可能后端存储出问题 |
| 存储磁盘使用率 | > 80% | 需要扩容或调整保留时间 |
| Trace 完整率 | < 90% | 部分服务没正确传递 Trace Context |
| 采样率偏差 | > 预期 ±20% | 采样策略可能配置错误 |
# PromQL:监控 OTel Collector 的数据丢弃率
rate(otelcol_processor_refused_spans_total[5m])
/
(rate(otelcol_receiver_accepted_spans_total[5m]) + rate(otelcol_processor_refused_spans_total[5m]))
不同规模团队的推荐方案
小团队(1-5 台服务器)
别折腾了。直接用 Datadog 或 New Relic 的免费额度,或者 Jaeger all-in-one 单机。你的时间应该花在业务上,不是运维监控系统。
中等规模(20-100 台服务器)
推荐方案:OTel SDK + Jaeger(或 Tempo)+ Prometheus + Grafana。
- 埋点用 OTel SDK,保证厂商中立
- Trace 后端用 Jaeger + ES 或 Tempo + S3
- 指标继续用 Prometheus
- 日志用 Loki 或 ELK
- 统一在 Grafana 里展示
这个组合的 TCO 比商业方案低 60-70%,但需要 0.5-1 个 FTE 维护。
大规模(100+ 台服务器)
推荐方案:SkyWalking 或自建 OTel + LGTM 全栈。
- SkyWalking 一站式,运维组件少
- 或自建 OTel Collector + Tempo + Prometheus + Loki + Grafana,可定制性最高
- 部署多区域联邦,解决跨机房查询问题
- 考虑用 Tail Sampling 做精细化采样控制
金融/政务等合规要求高的场景
数据不能出境,商业 SaaS 基本排除了。推荐:
- SkyWalking 私有化部署(国内社区活跃,中文文档全)
- 或自建 OTel + Jaeger + ES,数据完全在本地
- 按等保要求配置日志保留策略(通常 180 天以上)
总结
APM 选型没有银弹。我见过太多团队花了大价钱买了商业 APM,最后只用来看个拓扑图;也见过团队用开源方案搭了很完整的可观测平台,但维护到崩溃。
几个核心判断:
- 先上 OTel 再选后端。不管最终选什么工具,应用侧埋点统一用 OpenTelemetry SDK。这样换后端时不用改业务代码,这是最重要的架构决策。
- 小团队别自建。5 人以下运维团队,直接用商业 SaaS。自建开源 APM 的运维成本远超 License 费用。
- SkyWalking 是国内 Java 团队的默认选择。开箱即用,拓扑图好看,社区支持好。但注意 OAP 吃内存、ES 调优有坑。
- Tempo 适合存储成本敏感的场景。S3 存储 + Grafana 展示,成本只有 ES 方案的 1/10。但查询方式受限,适合"按 Trace ID 查"的场景。
- 商业方案看 Datadog。如果你不在意数据合规且预算充足,Datadog 的集成广度和产品成熟度确实领先。但做好成本管控,别被按量计费反噬。
- 采样策略决定了系统可用性。全量采样会拖垮存储,合理采样是 APM 系统能长期稳定运行的关键。
最后一点经验:APM 不是装上就完事的。它需要持续投入——调整采样率、优化 Span 标签、配合 SLO 做告警。一个用了一年的 APM 系统比刚上线的系统有价值得多,因为积累了足够的历史基线数据。别频繁换工具,选好了就深耕。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- 2026 开源 APM 选型指南: OpenTelemetry 原生方案怎么选 — DataBuff,开源 APM 工具分类与选型维度对比
- 2026 年 Top 5 开源 APM 工具对比指南 — 腾讯云开发者社区,五大开源 APM 工具特性与优劣势分析
- 2026 年可观测厂商选型指南 — CSDN,商业可观测厂商(Datadog/Dynatrace/New Relic)市场数据与能力对比
- APM 工具介绍:Agent 探针、Trace 追踪、Span 片段、Sampling 采样 — CSDN,APM 核心概念与工作原理详解
- OpenTelemetry 实战:云原生可观测性三大支柱统一标准 — CSDN,OpenTelemetry 历史演进与架构设计
- APM 工具选型终极对比: Applications Manager vs Datadog vs New Relic — ManageEngine,商业 APM 工具多维度对比分析