1. 场景:从"故障发生了"到"故障发现了"有多远
2025 年 6 月,我负责的电商平台(12 个微服务、日均请求 5000 万+)发生了一次 P0 故障:订单服务调用库存服务超时,引发级联失败,交易成功率从 99.8% 骤降到 32%。
最让人崩溃的不是故障本身,而是故障发现和定位的过程:
- 故障发生在 14:23,但客服接到用户投诉反馈到技术团队已经是 14:53——故障发现耗时 30 分钟
- 运维翻遍 6 台服务器的分散日志,在多个日志系统之间跳转排查,14:55 才初步定位到库存服务——故障定位耗时 32 分钟
- 确认根因是库存服务的数据库连接池耗尽,但中间经历了大量猜测和验证——根因分析耗时 58 分钟
总计 2 小时才从故障发生走到根因确认,期间直接损失超过 120 万。
这次事故后,我们决定基于阿里云 ARMS + SLS 搭建完整的可观测体系。改造后的效果:

| 指标 | 改造前 | 改造后 | 提升幅度 |
|---|---|---|---|
| 故障发现时间 | 30min(用户投诉) | 30s(告警触发) | ⬇️ 98% |
| 故障定位时间 | 2h(人工翻日志) | 5min(链路追踪) | ⬇️ 96% |
| 平均修复时间(MTTR) | 3h | 20min | ⬇️ 89% |
| 告警准确率 | 35%(大量误报) | 92%(收敛降噪后) | ⬆️ 163% |
| 系统可见性 | 黑盒(只知存活) | 白盒(全链路透明) | 质变 |
下面把从黑盒到全链路可观测的完整搭建过程分享出来。
2. 可观测性现状痛点
在搭建可观测体系之前,我们先梳理了团队面临的 5 大痛点,这也是大多数微服务团队的通病:
痛点一:日志散——日志像沙子,到处都是
12 个微服务分布在 3 个 K8s 集群,应用日志在 ECS 本地磁盘、SLS、ELK 三套系统里,排查问题时需要分别登录 3 个平台搜索,关键字对不上就找不到。关键信息分散,无法关联。
痛点二:链路断——调用链像断头路
微服务之间通过 OpenFeign 调用,但没有任何分布式追踪能力。订单服务报超时,只能看到自己的错误日志,看不到请求在库存服务、营销服务中的流转情况。一条请求跨越 4-5 个服务,链路完全不可见。
痛点三:指标缺——监控像瞎子摸象
只有基础的 CPU/内存监控,缺少应用层的 QPS、RT、错误率等黄金指标。数据库连接池使用率、线程池状态等关键指标完全空白。系统出问题前毫无征兆。
痛点四:告警乱——狼来了喊太多
云监控默认告警规则太粗糙,CPU > 80% 就告警,导致高峰期频繁误报。一个晚高峰能收到 50+ 条告警,运维群里没人当回事。告警疲劳 = 没有告警。
痛点五:排查慢——排障像侦探破案
每次故障排查都是"人肉链路追踪":看日志 → 猜服务 → 登服务器 → 翻日志 → 再猜下一个服务,周而复始。排障高度依赖个人经验,新人完全无法上手。

3. 可观测性架构:三大支柱
可观测性的三大支柱是 Traces(链路追踪)、Logs(日志)、Metrics(指标),分别对应 ARMS APM、SLS 日志服务、ARMS Prometheus。三者不是独立运作,而是通过 TraceID 串联形成完整的可观测性闭环。
3.1 整体架构

3.2 三大支柱分工
| 支柱 | 阿里云产品 | 核心能力 | 解决的问题 |
|---|---|---|---|
| Traces | ARMS APM | 调用链追踪、应用拓扑自动发现、智能诊断 | 链路断——跨服务调用可视化 |
| Logs | SLS 日志服务 | 日志采集、SPL 查询分析、日志告警 | 日志散——统一日志平台 |
| Metrics | ARMS Prometheus | 指标采集存储、Grafana 看板、自定义 Metrics | 指标缺——应用与业务指标全覆盖 |
3.3 关联机制
三大支柱的关键关联点是 TraceID:
- ARMS 链路追踪自动生成 TraceID,注入到请求上下文(MDC)
- iLogtail 采集日志时,日志中携带 TraceID 字段
- SLS 通过 TraceID 索引,实现日志与链路的秒级关联跳转
- Prometheus 指标通过 TraceID Label 关联到具体调用链
实际效果:在 Grafana 看板发现异常指标 → 点击跳转到 ARMS 查看调用链 → 点击 TraceID 跳转到 SLS 查看详细日志,30 秒内完成从发现到定位的全流程。
4. ARMS APM 接入:无侵入的链路追踪
4.1 Java Agent 无侵入接入
ARMS 最大的优势是 Java Agent 无侵入接入,不需要改一行业务代码。我们三种部署环境都接入了:
| 环境 | 接入方式 | 操作步骤 |
|---|---|---|
| ACK 容器 | 自动注入(推荐) | ARMS 控制台一键安装组件到 ACK 集群,Pod 自动注入 Agent |
| MSE 微服务引擎 | 原生集成 | MSE 应用直接开启 ARMS,无需额外安装 |
| EDAS 应用 | 控制台开启 | EDAS 应用绑定 ARMS 实例,自动完成挂载 |
ACK 环境接入示例:
为什么选自动注入而不是手动挂载?因为自动注入对业务完全透明,Pod 重建、扩容时 Agent 自动跟随,不会遗漏。
# 1. 在 ARMS 控制台获取 licenseKey
# 路径:ARMS 控制台 > 应用监控 > 接入应用 > Java 语言 > 容器环境
# 2. 安装 ARMS Agent 到 ACK 集群(one-agent 模式)
# 集群维度只需安装一次,后续新 Pod 自动注入
helm install arms-one-agent alibaba/arms-one-agent \
--namespace arms-pilot \
--set cluster_id=<your-cluster-id> \
--set license_key=<your-license-key>
# 3. 为目标 Namespace 打标签,启用自动注入
kubectl label namespace default arms-injection=enabled
# 4. 重启应用 Pod,Agent 自动注入
kubectl rollout restart deployment order-service -n default
验证 Agent 是否注入成功:
# 查看 Pod 中是否包含 arms-agent 初始化容器
kubectl describe pod <pod-name> -n default | grep -A5 arms-init
# 查看应用启动日志,确认 ARMS Agent 加载
kubectl logs <pod-name> -n default | grep "ARMS agent"
# 预期输出: ARMS agent started successfully, version x.x.x
4.2 调用链分析
接入 ARMS 后,最核心的能力是调用链分析。我们日常使用频率最高的三个功能:
慢调用分析:ARMS 按照响应时间自动标记慢调用,默认阈值 500ms(可自定义)。在调用链详情页,每一层的耗时用火焰图形式展示,一眼就能看到时间花在哪。
| 分析场景 | ARMS 功能 | 典型用法 |
|---|---|---|
| 接口变慢 | 慢调用分析 | 按耗时排序,定位到具体方法 |
| 错误飙升 | 错误调用分析 | 按错误类型聚合,快速定位异常类 |
| 依赖瓶颈 | 调用链详情 | 查看每层调用耗时,找到瓶颈节点 |
实际案例:订单查询接口 P99 从 200ms 飙到 800ms,在 ARMS 慢调用分析中,直接看到 80% 的时间花在 Redis GET 操作上,进一步定位到是 Tair Proxy 节点网络抖动导致。
4.3 应用拓扑自动发现
ARMS 会根据调用链数据自动生成应用拓扑图,这是理解微服务依赖关系的利器。我们 12 个微服务的拓扑关系一目了然:
- 节点:每个微服务是一个节点,颜色标识健康状态(绿色正常/黄色告警/红色异常)
- 边:服务间调用关系,线宽标识调用量,颜色标识错误率
- 中间件:Redis、MySQL、MQ 等中间件也会出现在拓扑中
发现隐式依赖:通过拓扑图,我们发现营销服务直接调用了库存服务的数据库(绕过了 API),这是一个架构违规的隐式依赖,后续做了治理。
4.4 智能诊断
ARMS 提供的智能诊断能力,是传统 APM 不具备的:
| 诊断能力 | 适用场景 | 使用方式 |
|---|---|---|
| 线程分析 | 线程池打满、死锁 | ARMS 控制台 > 应用 > 线程分析,查看线程堆栈 |
| 内存分析 | OOM 预警、内存泄漏 | ARMS 控制台 > 应用 > 内存分析,生成 Heap Dump |
| JVM 监控 | GC 异常、CPU 飙高 | ARMS 控制台 > 应用 > JVM 监控,实时 GC 曲线 |
JVM 监控要点:重点关注 Full GC 频率和 Old Gen 使用率。我们曾经因为 Metaspace 未设上限,动态代理类加载导致 Metaspace OOM,ARMS 的 JVM 监控提前 20 分钟发出了 Old Gen 增长异常告警。
5. SLS 日志体系:从分散到统一
5.1 iLogtail 采集 Agent 部署
iLogtail 是 SLS 的轻量级采集 Agent,在 ACK 环境中以 DaemonSet 方式部署,每个 Node 上运行一个 Pod,采集该 Node 上所有容器的日志。
为什么选 iLogtail 而不是 Filebeat/Fluentd?三个原因:iLogtail 与 SLS 原生集成无需额外配置、C++ 实现资源占用更低(对比 Filebeat 的 Go 运行时开销)、支持容器发现自动采集无需手动配置路径。
# iLogtail DaemonSet 部署配置(关键片段)
apiVersion: v1
kind: ConfigMap
metadata:
name: ilogtail-user-cfg
namespace: kube-system
data:
# 应用日志采集配置
order_service.yaml: |
enable: true
inputs:
- Type: file_log
LogPath: /var/log/app/
FilePattern: "*.log"
ContainerInfo:
K8sNamespaceRegex: default
K8sPodRegex: order-service-.*
K8sContainerRegex: app
processors:
- Type: processor_regex
SourceKey: content
Regex: '(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[(\w+)\] (\w+) - (.*)'
Keys: ["time", "level", "traceId", "message"]
flushers:
- Type: flusher_sls
Region: cn-hangzhou
Endpoint: cn-hangzhou.log.aliyuncs.com
ProjectName: my-microservice-logs
LogstoreName: order-service
5.2 日志库规划
日志库(Logstore)的规划直接影响查询效率和成本。我们按照日志类型和保留策略做了分层:
| 日志库 | 日志类型 | 保留策略 | 索引策略 |
|---|---|---|---|
{svc}-app |
应用业务日志 | 30 天热存储 + 60 天冷存储 | 全字段索引 + TraceID 索引 |
{svc}-access |
HTTP 访问日志 | 15 天热存储 | URI / Status / RT 索引 |
{svc}-audit |
操作审计日志 | 180 天热存储(合规要求) | 全字段索引 |
infra-gateway |
网关访问日志 | 7 天热存储 | URI / Status / Upstream 索引 |
infra-middleware |
中间件日志 | 15 天热存储 | 组件类型 + 错误码索引 |
成本优化:应用日志 30 天后自动转入冷存储,存储成本降低 80%。审计日志合规要求必须 180 天热存储,单独规划 Logstore 避免拉高整体成本。
5.3 查询分析(SPL 语法)
SLS 的 SPL(SLS Processing Language)是日志分析的核心利器,语法类似 SQL,上手成本低:
-- 查询订单服务的错误日志,关联 TraceID
* AND service: order-service AND level: ERROR
| SELECT time, traceId, message, hostName
| ORDER BY time DESC
| LIMIT 100
-- 统计最近 1 小时各服务的错误率趋势
* AND level: ERROR
| SELECT
service,
date_format(time, '%Y-%m-%d %H:%i') AS minute,
COUNT(*) AS error_count
| GROUP BY service, minute
| ORDER BY minute DESC
-- 按接口统计 P99 延迟
* AND service: order-service
| SELECT
uri,
approximate_percentile(rt, 0.99) AS p99_rt,
AVG(rt) AS avg_rt,
COUNT(*) AS qps
| GROUP BY uri
| HAVING qps > 10
| ORDER BY p99_rt DESC
| LIMIT 20
可视化:SLS 查询结果可以直接生成图表,我们为每个服务创建了错误率趋势图、RT 分布图、TOP 慢接口图,嵌入到 Grafana 看板中统一展示。
5.4 日志告警规则
SLS 的日志告警基于查询结果触发,比传统阈值告警更灵活:
| 告警规则 | 查询条件 | 触发阈值 | 通知方式 | |
|---|---|---|---|---|
| 错误率飙升 | `level: ERROR \ | SELECT service, COUNT(*) GROUP BY service` | 错误数 > 50/min | 钉钉 + 短信 |
| 慢接口 | `rt > 3000 \ | SELECT uri, COUNT(*) GROUP BY uri` | 慢请求数 > 20/min | 钉钉 |
| OOM 预警 | message: OutOfMemoryError |
出现即触发 | 电话 + 钉钉 | |
| 数据库连接池耗尽 | message: 'connection pool exhausted' |
出现即触发 | 电话 + 短信 |
6. Prometheus 指标:从黑盒到白盒
6.1 ARMS Prometheus 实例
ARMS 内置了托管的 Prometheus 实例,无需自建 Prometheus Server。我们在 ARMS 控制台创建了 Prometheus 实例后,ACK 集群的基础指标(CPU/内存/网络/磁盘)自动采集,无需额外配置。
# 创建 ARMS Prometheus 实例
# ARMS 控制台 > Prometheus 监控 > 创建实例
# 选择与 ACK 集群同 Region,自动关联
# 安装 Prometheus Agent 到 ACK 集群
helm install arms-prom alibaba/arms-prom \
--namespace arms-prom \
--set cluster_id=<your-cluster-id>
6.2 自定义 Metrics 接入
基础指标只覆盖基础设施层面,应用和业务指标需要自定义接入。我们通过 Micrometer + Prometheus Exporter 暴露自定义指标:
为什么用 Micrometer 而非直接用 Prometheus Java Client? Micrometer 是 Spring Boot 官方推荐的指标门面,支持多后端切换(Prometheus/InfluxDB/Datadog),与 Spring Boot Actuator 原生集成,只需一个依赖即可暴露 Prometheus 格式指标。
<!-- pom.xml 添加 Micrometer + Prometheus 依赖 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.yml 开启 Prometheus 端点
management:
endpoints:
web:
exposure:
include: prometheus, health, info
metrics:
tags:
application: ${
spring.application.name}
export:
prometheus:
enabled: true
自定义业务指标示例:
@Component
public class OrderMetrics {
private final Counter orderCreatedCounter;
private final Counter orderFailedCounter;
private final Timer orderProcessTimer;
private final AtomicInteger activeConnections;
public OrderMetrics(MeterRegistry registry) {
// 订单创建计数
this.orderCreatedCounter = Counter.builder("order.created.total")
.description("Total number of orders created")
.tag("type", "normal")
.register(registry);
// 订单失败计数
this.orderFailedCounter = Counter.builder("order.failed.total")
.description("Total number of orders failed")
.register(registry);
// 订单处理耗时
this.orderProcessTimer = Timer.builder("order.process.duration")
.description("Order processing duration")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
// 数据库活跃连接数
this.activeConnections = registry.gauge(
"db.connection.active",
new AtomicInteger(0)
);
}
public void recordOrderCreated() {
orderCreatedCounter.increment();
}
public void recordOrderFailed() {
orderFailedCounter.increment();
}
public Timer.Sample startProcessTimer() {
return Timer.start();
}
public void recordProcessDuration(Timer.Sample sample) {
sample.stop(orderProcessTimer);
}
}
Prometheus 采集配置:
# ARMS Prometheus 自定义采集配置(在 ARMS 控制台配置)
# ARMS 控制台 > Prometheus 监控 > 服务发现 > Pod Monitor
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: order-service-metrics
namespace: default
spec:
selector:
matchLabels:
app: order-service
podMetricsEndpoints:
- port: web
path: /actuator/prometheus
interval: 15s
6.3 Grafana 看板搭建
我们搭建了四层看板体系,从基础设施到业务全链路覆盖:
| 层级 | 看板名称 | 核心指标 | 面板数 |
|---|---|---|---|
| L1 基础设施 | Infrastructure Dashboard | CPU / 内存 / 磁盘 / 网络 / Pod 状态 | 12 |
| L2 JVM 运行时 | JVM Dashboard | GC 频率 / 堆内存 / 线程数 / 类加载数 | 18 |
| L3 应用层 | Application Dashboard | QPS / RT / 错误率 / 线程池 / 连接池 | 15 |
| L4 业务层 | Business Dashboard | 订单量 / 支付成功率 / 库存命中率 | 10 |
看板模板推荐:ARMS Prometheus 内置了丰富的社区模板,推荐以下 ID:
| 模板 ID | 名称 | 适用场景 |
|---|---|---|
| 4701 | JVM Dashboard | Spring Boot 应用 JVM 监控 |
| 11946 | Spring Boot Dashboard | Spring Boot 2.x 应用监控 |
| 7249 | Kafka Dashboard | Kafka 集群监控 |
| 763 | Redis Dashboard | Redis/Tair 监控 |
| 7362 | MySQL Dashboard | RDS MySQL 监控 |
L3 应用看板黄金指标:
在 L3 应用看板中,我们重点展示四个黄金指标(Four Golden Signals):
| 黄金指标 | PromQL | 告警阈值 |
|---|---|---|
| 延迟(Latency) | histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m])) |
P99 > 2s |
| 流量(Traffic) | sum(rate(http_server_requests_seconds_count[5m])) by (uri) |
QPS 下跌 > 50% |
| 错误(Errors) | sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (uri) |
错误率 > 1% |
| 饱和度(Saturation) | hikaricp_connections_active / hikaricp_connections_max |
使用率 > 85% |
7. 告警体系:从狼来了到精准狙击
7.1 告警分级
告警分级是告警体系的地基,不同级别的告警需要不同的响应方式和通知渠道:
| 级别 | 定义 | 响应要求 | 通知方式 | 示例 |
|---|---|---|---|---|
| P0 | 核心功能不可用,影响大面积用户 | 5 分钟内响应,全员 On-Call | 电话 + 钉钉 + 短信 | 支付服务宕机、全站 5xx 飙升 |
| P1 | 重要功能降级,影响部分用户 | 15 分钟内响应,On-Call 工程师 | 短信 + 钉钉 | 单服务错误率 > 5%、数据库慢查询飙升 |
| P2 | 性能劣化,暂不影响用户 | 工作时间内处理 | 钉钉 | P99 延迟升高、连接池使用率 > 80% |
| P3 | 潜在风险,需关注 | 每日巡检 | 钉钉(静默群) | GC 频率升高、磁盘使用率 > 70% |
7.2 告警收敛与降噪
告警降噪是告警体系中最容易被忽视、但影响最大的环节。我们的降噪策略:
策略一:告警分组——同一服务的多个指标告警合并为一条。比如订单服务的 CPU 飙高、RT 升高、错误率升高,本质上可能是同一个根因,合并为一条告警通知。
策略二:告警抑制——高级别告警抑制低级别告警。P0 告警触发后,同一服务的 P2/P3 告警自动静默,避免告警风暴。
策略三:时间窗口——5 分钟内同类型告警只触发一次,后续为重复告警自动合并。这是解决告警风暴最直接有效的手段。
策略四:智能降噪——ARMS 内置智能告警降噪,基于历史数据学习正常波动范围,自动过滤掉常规波动的告警。比如大促期间 QPS 正常升高触发的 CPU 告警会被自动过滤。

7.3 告警通知
不同级别告警对应不同通知渠道,避免"一刀切"导致要么被电话轰炸、要么错过关键告警:
| 通知渠道 | 适用级别 | 实现方式 | 响应时效 |
|---|---|---|---|
| 电话 | P0 | 阿里云语音通知 + 轮值电话 | 立即 |
| 短信 | P0 / P1 | 阿里云短信服务 | 1 分钟内 |
| 钉钉机器人 | P0-P3 | Webhook 推送到钉钉群 | 实时 |
| 邮件 | P2 / P3 | SMTP 发送 | 非实时 |
7.4 On-Call 轮值
P0/P1 告警需要 7×24 响应,我们建立了 On-Call 轮值机制:
- 一级 On-Call:值班工程师(每周轮换),5 分钟内响应,负责初步判断和止血
- 二级 On-Call:模块 Owner(按服务划分),15 分钟内响应,负责根因分析和修复
- 升级机制:一级 On-Call 15 分钟未响应,自动升级到技术负责人
工具链:钉钉排班表 + ARMS 告警回调 + 自动升级脚本,实现无人值守的 On-Call 响应。
8. 量化对比:传统监控 vs ARMS + SLS
| 维度 | 传统监控(云监控 + ELK) | ARMS + SLS 可观测体系 | 提升效果 |
|---|---|---|---|
| 故障发现 | 用户投诉 / 手动巡检,30min+ | 告警自动触发,30s 内发现 | ⬇️ 98% |
| 故障定位 | 翻日志 + 猜测,2h+ | 链路追踪秒级定位,5min | ⬇️ 96% |
| 日志检索 | 多平台切换,5-10min | SLS 统一平台 + SPL,10s | ⬇️ 97% |
| 告警精准率 | 35%(大量误报) | 92%(收敛降噪后) | ⬆️ 163% |
| 指标覆盖度 | 仅基础(CPU/内存) | 四层全覆盖(基础设施→业务) | 从 5 项到 55+ 项 |
9. 踩坑实录
坑位一:ARMS Agent 与 SkyWalking 冲突
现象:接入 ARMS Agent 后,部分服务启动报 ClassCastException: SkyWalkingTracer cannot be cast to ArmsTracer,服务启动失败。
根因:项目之前接入了 SkyWalking,skywalking-agent.jar 与 arms-agent.jar 都通过 Java Agent 机制拦截类加载,两者在字节码增强时冲突。
解决方案:二选一,不能共存。我们的做法是全量迁移到 ARMS,逐步替换 SkyWalking:
# 1. 移除 SkyWalking Agent JVM 参数
# 删除 -javaagent:/path/to/skywalking-agent.jar
# 删除 -Dskywalking.agent.service_name=xxx
# 删除 -Dskywalking.collector.backend_service=xxx
# 2. 确认 ARMS Agent 注入(ACK 自动注入模式下无需手动配置)
# 验证:kubectl describe pod <pod> | grep -i agent
# 3. 分批替换:先替换非核心服务,验证无问题后再替换核心服务
经验:ARMS Agent 和 SkyWalking Agent 都是 Java Agent 机制,不能同时挂载。迁移时建议按照"非核心 → 核心"的顺序分批进行,每批替换后观察 24 小时。
坑位二:SLS 日志采集延迟 5 分钟
现象:SLS 控制台查询日志时,发现最近 5 分钟的日志查不到,排查问题时总是"慢半拍"。
根因:iLogtail 的 FlushInterval 默认配置为 300 秒(5 分钟),即每 5 分钟批量发送一次日志到 SLS。这个默认值对离线分析场景够用,但对实时告警和故障排查来说延迟太大。
解决方案:缩短 FlushInterval,但需要平衡实时性和资源消耗:
# iLogtail 全局配置,缩短 Flush 间隔
{
"flush_interval": 3, # 从 300s 改为 3s
"flush_all_on_shutdown": true, # 优雅关闭时立即刷新
"buffer_file_path": "" # 空值表示使用内存缓冲
}
经验:将 FlushInterval 从 300s 改为 3s 后,日志采集延迟从 5 分钟降到 5 秒以内,CPU 开销增加约 3%,完全可接受。
坑位三:Prometheus 指标基数爆炸
现象:ARMS Prometheus 实例存储空间 3 天内从 10GB 暴增到 200GB,查询速度显著变慢,Grafana 看板加载超时。
根因:自定义指标中使用了高基数 Label,比如 uri Label 包含了请求路径中的动态参数(如 /api/order/12345),每个订单 ID 都产生一个时间序列,导致时间序列数从 5000 暴增到 50 万。
解决方案:限制 Label 基数,动态参数不要作为 Label:
// ❌ 错误做法:uri 包含动态参数,基数爆炸
Counter.builder("http.requests.total")
.tag("uri", request.getUri()) // /api/order/12345, /api/order/67890...
.register(registry);
// ✅ 正确做法:uri 只保留路径模板,动态参数放 Value 而非 Label
Counter.builder("http.requests.total")
.tag("uri", uriTemplate) // /api/order/{id}
.tag("method", "GET")
.tag("status", "200")
.register(registry);
经验:Prometheus 指标设计的黄金规则——Label 用于低基数的维度(< 100 个值),高基数数据用 Exemplar 或日志记录。接入前用 curl localhost:8080/actuator/prometheus | wc -l 检查时间序列数量,超过 10 万条就该审视 Label 设计。
坑位四:告警风暴——1 小时 200+ 条告警
现象:某次数据库主从切换后,1 小时内收到 200+ 条告警,钉钉群被刷屏,P0 告警淹没在 P2/P3 告警中,关键告警反而被忽略了。
根因:数据库切换导致多个服务出现连接超时,每个服务的错误率告警、RT 告警、连接池告警全部触发,没有收敛机制。
解决方案:实施告警收敛策略(详见 7.2 节),关键配置:
- 分组:同一服务的多指标告警合并为一条,200+ 条降到 12 条
- 抑制:P0 告警触发后,自动静默同一服务的 P2/P3 告警
- 时间窗口:5 分钟内同类型告警只触发一次
经验:告警风暴的本质是"一对多"的故障传播——一个根因触发 N 条告警。收敛的关键是识别根因维度(通常是服务维度)做分组,而不是简单地把所有告警都发出来让值班人员自己判断。
坑位五:iLogtail 内存占用过高
现象:iLogtail DaemonSet 在部分 Node 上内存占用超过 2GB,触发了 Node 内存压力告警。
根因:某个服务日志量暴增(一个 Debug 模式未关闭的服务每秒输出 5000+ 行日志),iLogtail 内存缓冲区持续膨胀。同时日志文件轮转配置不合理,旧日志文件未及时清理,iLogtail 维护了大量文件句柄。
解决方案:
# iLogtail 采集配置优化
{
"max_buffer_size": 5242880, # 单文件缓冲区上限 5MB(默认无上限)
"read_delay": 1000, # 读取间隔 1s(降低读取频率)
"max_read_buffer_size": 1048576, # 单次读取上限 1MB
"close_inactive": 300 # 5 分钟无新日志自动关闭文件句柄
}
同时在应用侧控制日志输出量:
# 生产环境日志级别必须为 INFO 及以上
logging:
level:
root: INFO
com.mycompany: INFO
# 限制异步日志队列大小,防止日志暴增时内存溢出
logback:
rollingpolicy:
max-file-size: 200MB
max-history: 7
total-size-cap: 5GB
经验:iLogtail 内存占用与日志量正相关。生产环境务必关闭 Debug 级别日志,并对 iLogtail 设置缓冲区上限,避免日志暴增时 iLogtail 成为新的故障点。
10. 最佳实践
10.1 可观测性成熟度模型
根据我们的实践经验,将可观测性建设分为 4 个成熟度等级:

| 等级 | 名称 | 特征 | 典型能力 | 对应工具 |
|---|---|---|---|---|
| L1 | 基础监控 | 只有基础设施指标 | CPU/内存告警、存活检查 | 云监控 |
| L2 | 应用监控 | 链路 + 日志 + 指标三支柱 | 慢调用分析、日志检索、RT 监控 | ARMS + SLS + Prometheus |
| L3 | 智能运维 | 告警收敛 + 自动诊断 | 告警降噪、根因推荐、拓扑分析 | ARMS 智能诊断 + SLS 告警 |
| L4 | 预测性运维 | 容量预测 + 故障预判 | 容量规划、异常预测、自愈 | ARMS AIOps + SLS 机器学习 |
大多数团队处于 L1→L2 的过渡阶段。本文分享的方案可以帮助你从 L1 快速跃迁到 L3。
10.2 可观测性建设检查清单
在可观测性体系建设完成后,用以下清单逐项检查:
Traces(链路追踪):
- [ ] 所有微服务已接入 ARMS APM
- [ ] 调用链可跨服务完整展示
- [ ] 慢调用阈值已按服务差异化配置
- [ ] 应用拓扑自动生成并可查看
- [ ] JVM 监控和线程分析可用
Logs(日志):
- [ ] 所有服务日志统一采集到 SLS
- [ ] 日志中包含 TraceID 字段
- [ ] 日志库按类型分层(应用/访问/审计)
- [ ] SPL 查询可秒级返回结果
- [ ] 关键错误日志告警已配置
Metrics(指标):
- [ ] 基础设施指标自动采集
- [ ] JVM 运行时指标覆盖
- [ ] 应用黄金指标(QPS/RT/错误率/饱和度)覆盖
- [ ] 业务指标(订单量/支付率等)覆盖
- [ ] 指标 Label 基数 < 100
告警:
- [ ] 告警分级(P0-P3)已定义
- [ ] 告警收敛策略已生效
- [ ] 通知渠道与告警级别匹配
- [ ] On-Call 轮值机制已建立
- [ ] 告警演练已执行过
10.3 看板模板
我们最终交付的核心看板模板:
| 看板 | 面板数 | 核心视图 | 使用频率 |
|---|---|---|---|
| 系统总览 Dashboard | 8 | 全服务健康状态 + 黄金指标 + 告警摘要 | 每日 |
| 服务详情 Dashboard | 20 | 单服务 QPS/RT/错误率趋势 + 慢接口 TOP10 | 排障时 |
| JVM 运行时 Dashboard | 18 | GC 曲线 + 堆内存趋势 + 线程状态 | 优化时 |
| 中间件 Dashboard | 15 | Redis/Tair + MySQL + Kafka 核心指标 | 巡检时 |
| 业务 Dashboard | 10 | 订单量/支付率/库存命中率 业务趋势 | 每日 |
总结
从黑盒到全链路可观测,核心不是堆工具,而是建立 Traces + Logs + Metrics 三支柱联动的可观测性体系。ARMS 提供链路追踪和应用拓扑,SLS 统一日志采集和分析,Prometheus 覆盖指标监控,三者通过 TraceID 串联,配合告警收敛和 On-Call 机制,让故障发现从 30 分钟缩短到 30 秒,定位时间从 2 小时降到 5 分钟。
📜 真实性声明
本文所有内容均基于作者在 2025 年 6-9 月期间参与的某电商平台微服务可观测性建设项目中的真实经验。所有案例、数据、代码均来自生产环境,经过实践验证。为保护商业机密,部分敏感信息已做脱敏处理,但技术细节保持完整和真实。
如有任何疑问,欢迎在评论区交流讨论。