应用性能监控最常见的尴尬是:CPU和内存看着都正常,但接口响应时间却很不稳定。传统日志只能告诉你某个请求在某个时间点打印了什么,无法还原一次请求跨服务、跨中间件的完整路径。Elasticsearch APM(Elastic Application Performance Monitoring)针对这个问题,把一次请求拆成事务和Span,通过Agent自动采集、APM Server接收、Elasticsearch存储、Kibana展示,形成可搜索、可聚合的调用链。它不仅能定位慢请求,还能按服务版本、环境、标签分析错误率与延迟分布。

接入成本是很多团队评估的首要因素。Elastic APM支持Java、Node.js、Python、Go、.NET、Ruby等主流语言,通常只需在应用启动参数或依赖里加入Agent,不需要改动业务代码。下面从数据模型、接入方式、存储调优和生产注意事项几个方面展开。
一、Elastic APM的数据模型与组件协作
先把概念理清。Elastic APM由四个部分构成:APM Agent、APM Server、Elasticsearch、Kibana。Agent随应用一起运行,负责采集性能数据和错误;APM Server接收各Agent上报的数据,做校验和转换后批量写入Elasticsearch;Kibana通过APM UI读取Elasticsearch中的索引并展示服务地图、事务瀑布图、错误列表等。这个分工意味着存储和查询都依赖于Elasticsearch的索引能力,天然适合已经使用Elastic Stack的团队。
一条请求进入应用后,Agent会创建一个事务(transaction),表示一个完整业务操作,例如 POST /order/create。事务内部包含多个Span,每个Span表示一次外部调用或内部耗时操作,例如执行SQL、请求Redis、调用下游HTTP接口。事务和Span都有 duration、timestamp、trace.id、transaction.id、parent.id 等字段。父Span与子Span通过parent.id关联,从而组成完整调用链。除事务和Span外,Agent还会采集错误(error)和指标(metrics),错误包含异常堆栈、HTTP状态码、事务上下文,指标则包含CPU、内存、GC次数、事务吞吐量等。
理解这些对象在Elasticsearch中的存储形态,对后续查询和调优很有帮助。一个事务写入后大致可以对应一个文档,Span是独立文档还是嵌套文档取决于版本和配置。实际使用中,不需要手动关心每个文档结构,但要明白如果Span数量非常大会直接增加索引体积,这也是后面要谈采样和ILM策略的原因。
二、应用接入与手动埋点
以Node.js服务为例,先安装Agent:
npm install elastic-apm-node --save
在入口文件顶部启动Agent:
// app.js
const apm = require('elastic-apm-node').start({
serviceName: 'order-service',
serviceVersion: '1.4.2',
environment: process.env.NODE_ENV || 'development',
serverUrl: 'http://127.0.0.1:8200',
captureBody: 'off',
transactionSampleRate: 1.0
});
const express = require('express');
const app = express();
app.get('/api/orders/:id', async (req, res) => {
const span = apm.startSpan('query_order_detail', 'db.mysql');
try {
const order = await db.query('SELECT * FROM orders WHERE id = ?', [req.params.id]);
span.setLabel('order_id', req.params.id);
res.json(order);
} catch (err) {
apm.captureError(err);
res.status(500).json({ error: 'internal error' });
} finally {
if (span) span.end();
}
});
app.listen(3000);
这里 serviceName 是Kibana中聚合服务的维度,environment 用来区分测试、预发和生产。Agent启动后会自动捕获Express路由层的事务,以及MySQL、Redis、HTTP等受支持库的Span。上面代码额外用 apm.startSpan 自定义了一个Span,因为有些业务逻辑无法被自动识别,但又想观测它的耗时。手动Span必须调用 end(),否则会造成内存泄漏和Span数据丢失。
如果使用Python,接入方式类似:
from elasticapm import Client
client = Client({
'SERVICE_NAME': 'inventory-service',
'SERVER_URL': 'http://127.0.0.1:8200',
'ENVIRONMENT': 'production',
'TRANSACTION_SAMPLE_RATE': 0.5,
})
try:
process_order()
except Exception:
client.capture_exception()
不同语言的Agent会自动识别常见框架与驱动,例如Java对Servlet、Spring、JDBC的支持,Go对net/http和gRPC的支持。自动埋点覆盖不到的场景,可以结合OpenTelemetry或者Agent提供的API手动创建事务和Span。手动埋点不要滥用,优先监控数据库、外部HTTP、消息队列这些高耗时边界,否则Span过多会增加上报压力和存储成本。
三、写入Elasticsearch后的索引与查询调优
APM Server默认把数据写入以 apm-<version>-transaction、apm-<version>-span 等命名的索引。实际部署中通常启用ILM(Index Lifecycle Management),按天或按大小滚动索引,并在数据超过保留天数后自动删除。比如事务数据保留7天,错误数据保留30天,指标数据保留90天。这样可以控制磁盘占用,同时保证短期排障所需的明细数据不丢失。
关于存储膨胀,Span的数量通常比事务高一个数量级。如果一个订单创建接口平均产生15个Span,而接口每秒100次,那么一天就会产生约1.3亿个Span文档。即使有压缩,也会占用大量磁盘。生产环境一般会把 transactionSampleRate 设置为0.2到0.5,并开启尾部采样或基于规则的采样,让错误和慢请求保留更高比例,普通快速请求降低采样。不同Agent的采样配置名略有差异,但原理相同。
如果要直接从Elasticsearch查询最近慢事务,可以用如下DSL:
{
"query": {
"bool": {
"must": [
{ "term": { "service.name": "order-service" } },
{ "range": { "transaction.duration.us": { "gte": 2000000 } } }
]
}
},
"sort": [
{ "transaction.duration.us": { "order": "desc" } }
],
"size": 20
}
这里的 transaction.duration.us 是微秒单位的数值字段,查询时可以直接比较大小。存储字段中还有 labels、url.full、http.request.method 等,适合做过滤和聚合。需要注意,直接查询APM索引时字段名带点号,不要和Elasticsearch对象嵌套语义混淆。Kibana Index Pattern会自动识别这些字段,但如果手动查询,应使用带点号的扁平字段名。
另外,索引模板会映射大量字段,如果某些Agent版本或插件上报了未预定义字段,可能导致映射膨胀。定期在Kibana的索引管理里查看字段数量和磁盘占用,比等到查询变慢再处理更稳妥。对不需要全文检索的字段可以修改为keyword或禁用index,但这属于更深入的调优,初次接入可以先使用默认模板。
四、在Kibana中定位性能瓶颈
数据进入Elasticsearch后,Kibana的APM应用会展示服务列表、延迟分布、吞吐量和错误率。进入某个事务后,可以看到从请求入口到数据库的瀑布图,每个Span按照执行时间横向排列,灰色等待段和彩色执行段区分明确。最常见的定位动作是先按 transaction.duration 排序,找到最慢的几个请求,再展开Span瀑布图观察哪个环节占时最长。
比如一个 /api/orders 接口平均耗时800ms,但某个时段涨到3秒。事务详情里如果显示 MySQL SELECT Span耗时2.4秒,而Redis和业务逻辑都很短,基本就能锁定是数据库查询变慢。结合错误标签或同批请求的SQL文本,可以进一步确认是缺少索引、锁等待还是数据量增长。Kibana还提供服务地图,把服务间调用关系可视化,配合错误率颜色标记,可以在微服务数量较多时快速缩小范围。
和单独使用日志系统相比,APM的优势在于上下文完整。日志通常按服务分散,排查一条失败请求可能要同时打开多个系统的日志界面;而APM通过trace id串联所有服务,可以直接跳转到关联日志。Elastic Stack中如果集成了Filebeat或直接写日志到Elasticsearch,可以在事务视图中关联相关日志,缩短从现象到原因的路径。
五、生产环境的采样、安全与资源控制
采样策略是生产部署中最容易忽略的问题。全量采样可以得到最完整的链路,但存储和APM Server CPU压力会显著增加。可以从1.0开始验证功能,逐步降到0.3左右,同时开启 tail-based sampling。尾部采样会先缓冲一段时间内的所有Span,再根据整条链路的最终状态决定保留哪些,这样能优先保留错误和慢事务,避免随机采样丢掉关键问题。该功能对APM Server的配置和集群资源有要求,建议在稳定后再切换。
安全方面,Agent和APM Server之间的通信建议使用TLS,APM Server到Elasticsearch也需要配置认证。若暴露公网,APM Server的 secret_token 或API Key必须开启,不能依赖内网IP自动信任。可以在apm-server配置中开启限制,例如最大事件大小、允许的Content-Size,避免异常应用打爆通道。
资源控制方面,上述ILM策略和采样率是主要手段。另外,APM Server可以配置多个Elasticsearch输出节点,写入压力过大时可以横向扩展。不要把所有环境的APM数据都写入同一个集群,测试环境频繁构造异常会产生大量错误文档,影响生产排障效率。用不同的 environment 标签配合ILM分开管理是较简单的做法。

