Featured image of post Java 微服务监控与日志:Prometheus + Grafana + ELK + SkyWalking + Loki 全链路可观测性实战

Java 微服务监控与日志:Prometheus + Grafana + ELK + SkyWalking + Loki 全链路可观测性实战

Java 微服务监控与日志:Prometheus + Grafana + ELK + SkyWalking + Loki 全链路可观测性实战

Java Web 微服务系列 · 第 9 篇 · 监控与日志 阅读时长:约 50 分钟 本文写于 2026 年 6 月

引子:凌晨 3 点的告警风暴

2021 年某日凌晨 3 点 17 分,我的手机开始震动。

不是一条告警——是 73 条。手机屏幕上挤满各种颜色的推送:钉钉群、飞书、Slack、Prometheus AlertManager、邮件、企业微信……每一条都"重要紧急",但每一条都没说清"到底哪里出了事"。

我当时所在的电商平台正处于大促预热期,技术栈是 200+ Spring Cloud 微服务 + 30+ 中间件。我冲到电脑前看到的景象是:

  • Grafana 大盘上 17 个核心指标 全是红的:CPU 95%、P99 3.2s、错误率 8.7%、JVM FullGC 频繁、Kafka 消费积压 12 万
  • Kibana 上日志查询 慢到 30 秒返回结果(ES 集群 IO 100%)
  • SkyWalking 链路追踪 显示"调用链断层"——只有 30% 的请求能查到完整 trace
  • SRE 群里 8 个工程师对着 73 条告警吵成一团,没人能说"先看哪条"

真正的故障点在 12 分钟后才被定位:一个非核心的"商品评论服务"在慢 SQL,把 MySQL 连接池占满,进而拖垮了订单服务,最终引发全链路雪崩。

事后复盘,技术总监问了一个让我深思的问题:

“我们有 7 套监控系统、5 套告警通道、3 套日志平台、2 套链路追踪系统。为什么出了事反而不知道出了什么事?”

那晚之后,我们花了 3 个月做"可观测性体系重构",把 17 套工具收敛成 “1+1+1+1+N” 的标准架构:

  • 1 个统一指标层:Prometheus + Grafana
  • 1 个统一日志层:Loki + Promtail(业务日志)+ ELK(合规日志)
  • 1 个统一链路层:SkyWalking
  • 1 个统一告警层:Alertmanager + 告警分级 + On-Call
  • N 个自动采集:Java Agent / Sidecar / DaemonSet

到 2025 年回看,那次重构救了我们无数次。可观测性不是"装一套 Prometheus 就够了"——它是一套完整的工程实践

本文要回答的问题:

  • Metrics / Logging / Tracing 三大支柱如何统一
  • Prometheus / Grafana / ELK / SkyWalking / Loki 怎么选型、怎么落地
  • 自动采集、自动告警、自动恢复的全链路怎么搭
  • 告警治理怎么做?怎么避免"凌晨 73 条告警"的灾难?

全文 10000+ 字,一次把可观测性体系讲透

一、核心概念:把"可观测性"掰开

谈具体技术前,先把 3 个常被混用的术语定义清楚——很多团队的"监控"压根不是可观测性

1.1 三个支柱:Metrics / Logging / Tracing

可观测性(Observability) 源自控制论,指通过系统外部输出推断内部状态的能力。在微服务领域,它由 3 大支柱 构成:

支柱回答什么问题典型数据典型工具数据特征
Metrics(指标)系统的整体状态怎样?CPU、QPS、P99、错误率Prometheus / Grafana数值型、低基数、高频
Logging(日志)系统发生了什么应用日志、Nginx 访问日志、错误堆栈ELK / Loki / Splunk文本型、海量、不可丢失
Tracing(追踪)请求的完整路径一次请求跨 N 个服务的耗时SkyWalking / Jaeger / Zipkin调用链、低采样率、关联 ID

💡 原理:为什么是 3 个支柱而不是 1 个

  • 只用 Metrics:知道 CPU 100%,但不知道哪个请求导致的
  • 只用 Logging:知道应用报错了,但不知道全链路影响
  • 只用 Tracing:能定位慢调用,但不知道整体服务健康度

三者互补,缺一不可。一个完整的可观测性体系必须同时回答 3 个问题:现在怎么样?发生了什么?为什么这样?

1.2 监控 vs 可观测性:本质区别

很多团队把"监控"和"可观测性"混为一谈,但两者的设计哲学截然不同

维度监控(Monitoring)可观测性(Observability)
核心思想已知问题 → 设告警未知问题 → 能定位
数据来源预设指标(CPU/内存/QPS)任意维度查询
告警方式阈值告警多维关联 + 根因分析
适用阶段系统稳定期复杂分布式 + 频繁迭代
代表工具Zabbix / NagiosPrometheus / ELK / SkyWalking

📌 实践:什么时候需要可观测性

  • ✅ 微服务 > 10 个
  • ✅ 每天发布 > 1 次
  • ✅ SLO 要求 99.9%+
  • ✅ 故障定位要求 < 10 分钟
  • ❌ 单体应用、传统行业、月级发布

1.3 数据模型:4 大共性要求

不管 Metrics / Logging / Tracing 哪一类数据,生产级的可观测性体系都必须满足 4 个共性要求

要求说明MetricsLoggingTracing
标签化维度可切片Labels(service、region)Log LabelsSpan Tags
关联性一次请求能串起来TraceID 关联TraceID 字段TraceID 根
时序性时间维度分析timestamp 必备@timestamp 必备起始时间
低成本数据可降采样Recording RulesLog Levels采样率

TraceID 是 3 支柱统一的"钥匙"——通过它可以把一次请求的指标、日志、追踪串起来。

二、可观测性体系全景:从 CNCF 到 OpenTelemetry

可观测性领域有 2 个绕不开的标准:CNCF Landscape(生态图)和 OpenTelemetry(数据采集标准)。

2.1 CNCF 可观测性 Landscape

CNCF(Cloud Native Computing Foundation)的可观测性 Landscape 把工具分成 4 层:

层级职责代表工具
数据采集(Instrumentation)生成/收集数据OpenTelemetry Agent、Filebeat、Promtail
数据传输(Transport)数据转发Kafka、Vector、Fluentd
数据存储(Storage)持久化、查询Prometheus、Elasticsearch、Loki、ClickHouse
数据展示(Visualization)大盘、告警、UIGrafana、Kibana、SkyWalking UI

🎯 避坑点:别陷入"工具竞赛"

我见过太多团队"为了用 XX 工具而用 XX 工具"。工具是为业务服务的:

  • 3 个服务的团队:单机 ELK + Zabbix 够了
  • 30 个服务的团队:Prometheus + Loki + SkyWalking
  • 300 个服务的团队:自研 + 商业 SaaS(Datadog / New Relic)

不要照搬大厂方案——你的规模配不上那个复杂度。

2.2 OpenTelemetry:数据采集的事实标准

OpenTelemetry(OTel) 是 CNCF 旗下统一 Metrics / Logging / Tracing 数据模型的开源项目,由 OpenCensus + OpenTracing 合并而来。目标:用一套 SDK 采集三类数据

OTel 的核心组件

组件职责部署位置
API定义数据模型和接口应用依赖
SDK数据采集和聚合实现应用内嵌
Collector数据接收、处理、转发独立服务 / Sidecar
Instrumentation自动埋点库(HTTP / JDBC / Redis 等)SDK 集成

💡 原理:OTel 怎么"统一"三支柱

  • 统一上下文传播(Context Propagation):W3C TraceContext 标准,所有语言、所有工具一致
  • 统一资源模型(Resource):Service、Instance、Namespace 等通用属性
  • 统一协议(OTLP):基于 gRPC/HTTP 的数据传输
  • 统一 SDK:一套 API 同时输出 Metrics/Logs/Traces

意义:换底层存储不影响应用代码——以前 Micrometer → Prometheus、Logback → Logstash 各一套埋点,现在统一成 OTel。

2.3 Spring Boot 应用集成 OTel

Spring Boot 3.x 已经内置 Micrometer Tracing(基于 OTel)——这是 Java 生态最方便的接入方式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- pom.xml 关键依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# application.yml
spring:
  application:
    name: order-service  # 自动成为 OTel 的 service.name

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  tracing:
    sampling:
      probability: 1.0  # 采样率(生产建议 0.1-0.3)
  otlp:
    tracing:
      endpoint: http://otel-collector:4318/v1/traces
      timeout: 10s

# 日志关联 TraceID(最关键的一步!)
logging:
  pattern:
    level: "%5p [${spring.application.name},%X{traceId:-},%X{spanId:-}]"

📌 实践:日志里必须有 TraceID

没有 TraceID 关联的日志,90% 的故障定位会失败。上例中 [order-service,abc123,def456] 就是关键——任何一条 ERROR 日志都能反查完整调用链。

没有这一行的日志系统 = 没有灵魂的日志系统

三、Prometheus 深度:从指标模型到生产调优

Prometheus 是 CNCF 第二个毕业的项目(仅次于 Kubernetes),已经成为云原生时代 Metrics 的事实标准

3.1 Prometheus 是什么

Prometheus 的核心特征

特征说明
拉模式(Pull)Prometheus 主动拉取应用 /metrics 端点
多维数据模型时间序列 + 标签(labels)
PromQL强大的查询语言
本地 TSDB自带存储(2 周保留)
告警规则内置告警 + Alertmanager
云原生友好K8s ServiceMonitor / PodMonitor

3.2 指标数据模型

每条时间序列 = 指标名 + 标签集 + 时间戳 + 样本值

1
http_server_requests_seconds_count{method="POST", uri="/api/order", status="200"} 12345 @1717900000
字段含义示例
指标名描述测量内容http_server_requests_seconds_count
标签维度{method, uri, status}
时间戳毫秒1717900000
样本值float6412345

指标类型(4 种):

类型说明例子
Counter单调递增计数器请求总数、错误数
Gauge任意增减CPU 使用率、连接池大小
Histogram分桶统计(可计算 P99)请求耗时分布
Summary客户端聚合的分位数客户端 P95

🎯 避坑点:标签基数(Cardinality)爆炸

每个唯一标签组合都是一条时间序列。常见坑:

  • {user_id="123456"} → 1 亿用户 = 1 亿条序列 → Prometheus 必崩
  • {email="user@example.com"} → 同样是高基数
  • {method, uri, status, service} → 低基数(几十种)

铁律:标签总数 × 唯一值数 < 1000 万。否则上 Thanos / Cortex / Mimir。

3.3 PromQL 实战

PromQL(Prometheus Query Language) 是 Prometheus 的灵魂。

基础查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 当前 CPU 使用率
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# 5 分钟平均 QPS
sum(rate(http_server_requests_seconds_count[5m]))

# 错误率
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
  /
sum(rate(http_server_requests_seconds_count[5m]))

# P99 响应时间(histogram_quantile)
histogram_quantile(0.99,
  sum by (le) (rate(http_server_requests_seconds_bucket[5m]))
)

聚合查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 按服务汇总 QPS
sum by (service) (rate(http_server_requests_seconds_count[5m]))

# 排序取 Top 10
topk(10, sum by (uri) (rate(http_server_requests_seconds_count[5m])))

# 增长率(同比上周)
(rate(http_server_requests_seconds_count[5m]) -
 rate(http_server_requests_seconds_count[5m] offset 1w))
 /
rate(http_server_requests_seconds_count[5m] offset 1w)

关联查询

1
2
3
4
# JVM 堆内存使用率
sum(jvm_memory_used_bytes{area="heap"}) 
  / 
sum(jvm_memory_max_bytes{area="heap"})

💡 原理:为什么是 histogram_quantile 而不是 quantile

  • quantile:在 Prometheus 端聚合(但先聚合再算分位数是错的
  • histogram_quantile:在客户端预分桶,按桶估算分位数

经典反例:用 quantile(0.99, rate(x[5m])) 算 P99,得到的是全局聚合后的 P99,不是单实例的 P99。

3.4 relabel 与 metric_relabel:降本利器

Relabel 是 Prometheus 减少存储的关键机制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# prometheus.yml
scrape_configs:
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      # 只采集有特定 annotation 的 pod
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      # 重命名 label
      - source_labels: [__meta_kubernetes_pod_label_app]
        target_label: service
      # 删除高基数 label
      - source_labels: [__meta_kubernetes_pod_container_name]
        action: drop

常见的 relabel 动作

action用途示例
keep保留匹配只保留 production namespace
drop丢弃丢弃高基数 label
replace重命名/新增service 标签
labelmap批量重命名__meta_* 转成 label
hashmod分片远端存储分片

3.5 存储与远程写入

Prometheus 本地 TSDB 适合 15 天以内。生产推荐 Prometheus + 远端存储

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 远程写入到 Thanos / Cortex / Mimir / 阿里云 SLS / 腾讯云 CLS
remote_write:
  - url: http://thanos-receive:19291/api/v1/receive
    write_relabel_configs:
      - source_labels: [__name__]
        regex: 'go_.*|process_.*'  # 丢弃内置指标
        action: drop
    queue_config:
      capacity: 10000
      max_samples_per_send: 2000
      batch_send_deadline: 5s

主流远端存储对比

方案特点适合
Thanos对象存储 + 全局视图 + 下采样中小团队 + K8s
Cortex多租户 + 水平扩展大型组织
MimirCortex 升级版(Grafana Labs)超大规模
VictoriaMetrics单二进制、性能极致资源敏感
InfluxDB / TDengine通用时序库已有栈复用

📌 实践:Prometheus 容量估算

单个 Prometheus 实例稳定支持 200 万活跃序列 + 10 万 samples/s 摄入

容量公式:

1
2
存储 GB = 活跃序列数 × 0.001 KB × 保留天数
         = 2,000,000 × 0.001 × 15 = 30 GB

超 200 万序列必须分片(Hash 分片) 或上远端存储。

3.6 告警规则

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# alert.rules.yml
groups:
  - name: example
    rules:
      # 规则 1:P99 响应时间 > 1s 持续 5 分钟
      - alert: HighP99Latency
        expr: |
          histogram_quantile(0.99,
            sum by (service) (rate(http_server_requests_seconds_bucket[5m]))
          ) > 1
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.service }} P99 > 1s"
          description: "P99 = {{ $value }}s"
          runbook_url: "https://wiki.example.com/runbook/HighP99Latency"

      # 规则 2:错误率 > 5%
      - alert: HighErrorRate
        expr: |
          sum by (service) (rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
          /
          sum by (service) (rate(http_server_requests_seconds_count[5m]))
          > 0.05
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.service }} 错误率 > 5%"

      # 规则 3:JVM FullGC 频繁
      - alert: JvmFullGcFrequent
        expr: |
          rate(jvm_gc_pause_seconds_count{cause="G1 Old Generation"}[5m]) > 0.1
        for: 5m
        labels:
          severity: warning

🎯 避坑点:告警规则的"for"参数

for: 5m 表示条件持续 5 分钟才触发。这是反"告警风暴"的第一道关:

  • ❌ 立即触发:网络抖动 1 秒 = 全员被告警轰炸
  • ✅ 持续 5 分钟:真正的故障才会触发

生产推荐 for ≥ 2 分钟,配合后续的告警分级。

3.7 Spring Boot 实战:自定义业务指标

用 Micrometer 把"业务动作"变成 Prometheus 指标——这是从"系统指标"迈向"业务指标"的关键一步。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@RestController
public class OrderController {
    // 1. 注入 MeterRegistry(Spring Boot 自动配置)
    @Autowired
    private MeterRegistry meterRegistry;

    // 2. 计数器:下单总数(带 status 标签)
    private final Counter orderSuccessCounter = Counter.builder("order_create_total")
            .tag("channel", "app")  // 标签:app / web / mini
            .description("订单创建总数")
            .register(meterRegistry);

    // 3. Timer:订单创建耗时(自动算 P99)
    private final Timer orderCreateTimer = Timer.builder("order_create_duration")
            .publishPercentileHistogram()  // 关键:发布 histogram 才能算 P99
            .publishPercentiles(0.5, 0.9, 0.99)  // 客户端聚合分位数
            .register(meterRegistry);

    @PostMapping("/api/order")
    public Order createOrder(@RequestBody OrderRequest req) {
        // 用 Timer 包装业务逻辑
        return orderCreateTimer.record(() -> {
            Order order = orderService.create(req);

            // 业务计数
            orderSuccessCounter.increment();

            // 业务 Gauge:当前活跃订单数
            meterRegistry.gauge("active_orders",
                    Tags.of("channel", req.getChannel()),
                    order);

            return order;
        });
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# application.yml 暴露 Prometheus 端点
management:
  endpoints.web.exposure.include: health,info,metrics,prometheus
  metrics:
    tags:
      application: ${spring.application.name}
    distribution:
      percentiles-histogram:
        order_create_duration: true
      slo:
        order_create_duration: 100ms,500ms,1s  # 关键桶

PromQL 验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 5 分钟下单 QPS
sum(rate(order_create_total[5m]))

# 下单 P99 响应时间
histogram_quantile(0.99,
  sum by (le) (rate(order_create_duration_seconds_bucket[5m]))
)

# 渠道维度错误率
sum by (channel) (rate(order_create_total{status="failed"}[5m]))
  /
sum by (channel) (rate(order_create_total[5m]))

📌 实践:业务指标的 4 大原则

  1. 命名规范{业务}_{动作}_{单位}(如 order_create_total
  2. 标签低基数:维度种类 < 10(如 channel 只有 app/web/mini 三种)
  3. histogram 必开publishPercentileHistogram=true 才能算 P99
  4. 配套告警:每条业务指标必须有对应告警规则

生产经验:业务指标比系统指标贵 10 倍价值——CPU 100% 你不知道影响什么,但"下单成功率从 99.9% 跌到 95%“直接关系到 GMV。

四、Grafana 大盘设计:变量、模板、联动

Grafana 几乎是 Prometheus 时代可视化的事实标准。一个设计良好的 Grafana 大盘能 让 30 分钟的故障定位缩短到 3 分钟

4.1 Grafana 核心概念

概念说明
Dashboard大盘(多 panel 组合)
Panel单个图(折线 / 表格 / 状态点)
Variables变量($service、$region)
Datasource数据源(Prometheus / ES / Loki)
Folder文件夹(业务/基础)
AlertGrafana 也能直接配告警(推荐用 Alertmanager)

4.2 大盘设计的 4 层架构

层级用途典型指标谁看
全局总览一眼看全公司状态总 QPS、总错误率、关键 SLOCTO / SRE 主管
应用总览看单个业务线状态服务 QPS、错误率、P99应用 Owner
深入诊断看单个服务细节JVM、DB 连接池、下游依赖业务研发
根因分析定位具体问题慢 SQL、慢调用、异常堆栈On-Call 工程师

4.3 必装的 Grafana 插件

插件用途
Pie Chart饼图(错误分布)
Bar Gauge进度条(资源使用率)
Geomap地图(多机房分布)
Stat单值大数字(KPI 看板)
State Timeline状态时间线(服务状态变化)
Node Graph节点图(服务依赖关系)

4.4 大盘设计 Checklist

  • 黄金指标 4 个:QPS、错误率、P99、饱和度
  • USE 指标(资源):Utilization、Saturation、Errors
  • RED 指标(服务):Rate、Errors、Duration
  • 变量可筛选:service / region / env 可切换
  • 链接可跳转:大盘之间能 drill-down
  • 告警标注:触发告警的曲线变红
  • 加载 < 3 秒:大盘查询不能太复杂

💡 原理:黄金信号 vs USE vs RED

方法适用来源
黄金信号(Google SRE)服务Latency、Traffic、Errors、Saturation
USE 方法(Brendan Gregg)资源Utilization、Saturation、Errors
RED 方法(Tom Wilkie)服务Rate、Errors、Duration

黄金信号是基础——任何服务都该有。USE 适合基础设施(CPU/内存/磁盘/网络),RED 适合微服务(QPS/错误率/响应时间)。

4.5 Grafana 统一告警入口

Grafana 8+ 支持 统一告警(Unified Alerting),可以同时接 Prometheus / Loki / ES 数据源:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# grafana provisioning 方式统一告警
apiVersion: 1
groups:
  - orgId: 1
    name: production-slo
    folder: SLO
    interval: 1m
    rules:
      - uid: api-availability
        title: API 可用性 < 99.9%
        condition: A
        data:
          - refId: A
            datasourceUid: prometheus
            relativeTimeRange:
              from: 600
              to: 0
            model:
              expr: |
                1 - (
                  sum(rate(http_requests_total{status=~"5.."}[5m]))
                  /
                  sum(rate(http_requests_total[5m]))
                ) < 0.999
          - refId: B
            datasourceUid: __expr__
            ...

📌 实践:Grafana vs Alertmanager 告警怎么选

| 场景 | 推荐 | |—| | 简单的指标阈值 | Alertmanager(更稳定、生态好) | | 多数据源关联(Grafana Unified Alerting) | Grafana | | 复杂的 SLO / Burn Rate | Grafana(支持多窗口多燃烧率) | | 邮件 / 钉钉 / Slack 统一路由 | Alertmanager(功能更全) |

生产推荐两者并行——Alertmanager 管"已知阈值”,Grafana 管"高级 SLO"。

五、ELK 日志体系:Filebeat → Logstash → ES → Kibana

ELK(Elasticsearch + Logstash + Kibana) 是日志领域的"老牌劲旅",至今仍是合规日志、审计日志、长保留日志的首选

5.1 ELK 体系架构

各组件职责

组件职责资源占用替代品
Filebeat轻量采集、日志文件追踪CPU/内存极低Fluent Bit、Vector
Logstash复杂解析、过滤、转换(JVM)Fluentd、Vector
Elasticsearch倒排索引、全文检索很重OpenSearch、Solr
Kibana可视化、查询、告警中等Grafana(ES 数据源)

5.2 选型:ELK 还是 Loki

这是 2024 年后最常被问到的问题

维度ELKLoki
索引方式全文倒排索引标签索引(无倒排)
查询能力(任意关键字)弱(只查标签 + 流式 grep)
存储成本(1TB 日志 ≈ 1.5TB 索引)低(压缩 + 标签)
适合场景复杂日志查询、审计、合规已知服务 + 标签查询
扩展性复杂(要调分片、副本)简单(对象存储)
运维成本
告警能力强(Watcher)强(通过 Grafana)

📌 实践:怎么选

  • 业务日志(已知服务、按 traceID/level 查)→ Loki(便宜、简单)
  • 审计日志(合规、保留 1 年、按任意字段查)→ ELK(功能强)
  • 混合架构(推荐):业务日志走 Loki,审计/合规走 ELK

不要全部上 ELK——ES 集群运维是大坑,10 个节点起步。

5.3 Filebeat 采集配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# filebeat.yml
filebeat.inputs:
  - type: container
    paths:
      - /var/log/containers/*.log
    processors:
      - add_kubernetes_metadata:
          host: ${NODE_NAME}
          matchers:
            - logs_path:
                logs_path: /var/log/containers/

output.logstash:
  hosts: ["logstash:5044"]
  ssl.enabled: true
  ssl.certificate_authorities: ["/etc/pki/tls/certs/logstash.crt"]

# 也可以直接写 ES
# output.elasticsearch:
#   hosts: ["${ES_HOST}:9200}"]
#   index: "app-logs-%{+yyyy.MM.dd}"
#   username: "${ES_USER}"
#   password: "${ES_PASS}"

logging.level: info
logging.to_files: true
logging.files:
  path: /var/log/filebeat
  name: filebeat
  keepfiles: 7
  permissions: 0644

5.4 Logstash 解析管道

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  # 1. 解析 JSON 日志
  if [container][image] =~ /order-service/ {
    json {
      source => "message"
      target => "app"
    }
    
    # 2. 时间字段转换
    date {
      match => ["[app][timestamp]", "ISO8601"]
      target => "@timestamp"
    }
    
    # 3. 提取 TraceID
    if [app][traceId] {
      mutate {
        add_field => { "trace_id" => "%{[app][traceId]}" }
      }
    }
    
    # 4. 错误日志标记
    if [app][level] == "ERROR" {
      mutate {
        add_tag => ["error", "needs_alert"]
      }
    }
    
    # 5. 脱敏(手机号、身份证)
    mutate {
      gsub => [
        "message", "1[3-9]\d{9}", "1**********",
        "message", "\d{17}[\dXx]", "******************"
      ]
    }
  }
}

output {
  elasticsearch {
    hosts => ["${ES_HOSTS}"]
    index => "app-logs-%{+YYYY.MM.dd}"
    user => "${ES_USER}"
    password => "${ES_PASS}"
  }
}

🎯 避坑点:Logstash 性能

Logstash 是 JVM 应用 + Grok 解析——非常吃 CPU 和内存。

  • 生产推荐用 Filebeat → Kafka → Logstash(多 worker)
  • 不要直接 Filebeat → Logstash(高并发会拖垮 Logstash)
  • 复杂解析可以用 Vector(Rust 写的,性能是 Logstash 的 10 倍)

5.5 ES 索引生命周期管理(ILM)

ES 最大的坑是"分片只增不减"。生产必须配 ILM:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# ILM Policy
PUT _ilm/policy/app-logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_age": "1d",
            "max_size": "50gb"
          },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 },
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "7d",
        "actions": {
          "freeze": {},
          "set_priority": { "priority": 0 }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": { "delete": {} }
      }
    }
  }
}

4 个阶段的目的

阶段时间动作成本
hot0-3 天频繁读写、性能优先
warm3-7 天压缩、合并分片
cold7-30 天冻结(不占 JVM heap)
delete30+ 天删除0

5.6 Kibana 实战

Kibana 必备 4 类视图

  1. Discover:自由查询 + 字段过滤
  2. Visualize:可视化(柱状图、饼图、地图)
  3. Dashboard:组合可视化
  4. Watcher:日志告警(错误日志突增、关键词出现)

生产推荐:Kibana 用于 深度日志调查,Grafana 用于 业务监控大盘——两个工具互补。

六、SkyWalking 链路追踪:分布式调用的"透视眼"

SkyWalking 是 Apache 顶级项目,国内 Java 生态用得最多的 APM 工具(比 Jaeger 更适合生产)。

6.1 SkyWalking 是什么

SkyWalking 核心组件

组件职责部署
Agent字节码增强、自动埋点应用 JVM 启动参数
OAP Server数据接收、聚合、分析独立服务(集群化)
Storage持久化(ES/H2/MySQL/TiDB)后端存储
UI调用链、拓扑图、告警Web

6.2 Trace 数据模型

Trace = 一次完整请求的调用链,由 Span 组成:

Span 关键字段

字段含义
traceId一次请求的全局 ID(关联指标/日志)
spanId当前 span 的 ID
parentSpanId父 span ID
operationName操作名(如 HTTP POST /api/order
startTime / endTime起止时间
duration耗时(毫秒)
tags标签(如 http.status_code=200
logs关联日志事件

6.3 Java Agent 接入

最简单的方式——不改一行代码:

1
2
3
4
5
6
# 应用启动参数加 agent
java -javaagent:/path/to/skywalking-agent.jar \
     -Dskywalking.agent.service_name=order-service \
     -Dskywalking.collector.backend_service=skywalking-oap:11800 \
     -Dskywalking.plugin.springmvc.trace_get_parameter=true \
     -jar order-service.jar

K8s 部署(推荐用 initContainer 共享 agent):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    spec:
      initContainers:
        - name: skywalking-agent
          image: apache/skywalking-java-agent:9.0.0
          command: ["sh", "-c", "cp -r /skywalking/agent /agent"]
          volumeMounts:
            - name: agent
              mountPath: /agent
      containers:
        - name: app
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-javaagent:/agent/skywalking-agent.jar"
            - name: SW_AGENT_NAME
              value: "order-service"
            - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
              value: "skywalking-oap:11800"
          volumeMounts:
            - name: agent
              mountPath: /agent
      volumes:
        - name: agent
          emptyDir: {}

6.4 采样策略

全量追踪存储成本爆炸——必须采样:

策略描述适合
全采样100% 追踪开发/测试
按比例采样1% / 10% 采样生产(默认)
按错误采样正常请求 1%,错误 100%错误排查
按慢调用采样> 1s 全采性能分析
按优先级采样自定义规则精细化运营
1
2
3
4
# agent/config/agent.config
agent.sample=${SW_AGENT_SAMPLE:10000}  # 默认 10000 采 1 个(0.01%)
# 关键服务改 100% 采样
agent.sample=1000  # 0.1%

🎯 避坑点:低采样率的"看不见的故障"

生产事故中 99% 的故障都在低采样率里被遗漏经验

  • 错误请求、慢请求(> 1s)100% 采集
  • 正常请求按业务量采样(0.1% ~ 10%)
  • 关键服务(支付/订单)保持高采样

SkyWalking 支持通过 agent.force_sample_error_trace=true 强制错误全采。

6.5 SkyWalking 告警

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# alarm-settings.yml
rules:
  - name: service_resp_time_rule
    expression: service_resp_time_percentile{app="order-service", percentile="50,75,90,95,99"} > 1000
    period: 10
    silence-period: 10
    message: 服务 {name} 平均响应时间超过 1s
    tags:
      level: WARNING
  - name: service_error_rate_rule
    expression: service_error_rate{app="order-service"} > 0.05
    period: 5
    message: 服务 {name} 错误率超过 5%
    tags:
      level: CRITICAL
  - name: service_instance_restart_rule
    expression: service_instance_restart{app="order-service"} > 0
    period: 1
    message: 服务 {name} 实例重启

webhooks:
  - url: https://oapi.dingtalk.com/robot/send?access_token=xxx
    secret: xxx

📌 实践:告警的去重和升级

SkyWalking 告警默认所有 OAP 节点都会发,集群部署要配 alarm.default-dingtalk-talk 防重复

生产推荐 SkyWalking 告警 → Alertmanager → 统一路由(详见第九章)。

6.6 SkyWalking 实战:告警合并 + 采样调优

SkyWalking 集群部署(生产必备,3 节点起步):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# skywalking-oap 集群化部署
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: skywalking-oap
spec:
  serviceName: skywalking-oap
  replicas: 3  # OAP 节点数
  template:
    spec:
      containers:
        - name: oap
          image: apache/skywalking-oap-server:9.0.0
          env:
            - name: SW_CLUSTER
              value: kubernetes
            - name: SW_CLUSTER_K8S_NAMESPACE
              value: monitoring
            - name: SW_CLUSTER_K8S_LABEL
              value: app=skywalking-oap
            - name: SW_STORAGE
              value: elasticsearch
            - name: SW_STORAGE_ES_CLUSTER_NODES
              value: elasticsearch:9200
            - name: JAVA_OPTS
              value: "-Xms4g -Xmx4g"
          ports:
            - containerPort: 11800  # gRPC
            - containerPort: 12800  # HTTP
            - containerPort: 1234   # 自监控

告警合并的 3 个核心配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# alarm-settings.yml
rules:
  # 规则 1:服务平均响应时间
  - name: service_resp_time_rule
    expression: service_resp_time_percentile{app="order-service", percentile="50,75,90,95,99"} > 1000
    period: 10
    silence-period: 10
    message: 服务 {name} 平均响应时间超过 1s
    tags:
      level: WARNING
    # 关键:多个实例的告警合并成 1 条
    include-paths:
      - service
配置项含义推荐值
period检查周期10s(关键)/ 60s(一般)
silence-period告警静默期5min(避免风暴)
tags告警标签level: WARNING/CRITICAL
message告警内容含服务名+具体数值+Runbook 链接

采样调优实战

业务类型服务示例推荐采样率
核心交易支付、订单100%(全采)
重要业务用户、商品10%
普通业务评论、收藏1%
后台任务报表、清理0.1%(不开 trace)
错误请求所有错误100%(强制采)
1
2
3
4
5
6
7
# agent.config 关键配置
agent.service_name=${SW_AGENT_NAME:order-service}
agent.sample=${SW_AGENT_SAMPLE:10000}  # 默认 10000 采 1
# 错误强制采样
agent.force_sample_error_trace=true
# 慢调用强制采样(> 1s)
agent.slow_trace_threshold=1000

🎯 避坑点:采样率不均的"采样偏差"

错误教训:某团队对所有服务设 1% 采样,结果大促时一个 P0 故障在 1% 采样下只能看到 10 个 trace,完全无法定位根因

对策

  1. 关键服务保持 100% 采样(即使多花存储也值)
  2. 错误 + 慢调用强制 100% 采样
  3. 抽样不应该是均匀的——应该按业务重要性分层

生产经验:SkyWalking 集群 OAP 节点数 = (日均 span 数 ÷ 1000 万) 向上取整。100 万 span/天 = 1 节点,1000 万 span/天 = 2-3 节点。

七、Loki 轻量日志:LogQL 与标签设计

Loki 是 Grafana Labs 推出的"Prometheus 风格"日志系统,核心理念:只索引标签,不索引内容——成本比 ELK 低 10 倍。

7.1 Loki 架构

核心组件

组件职责替代品
Promtail日志采集(Filebeat-like)Fluent Bit、Vector
Distributor接收、验证、分片-
Ingester写入 chunk、压缩-
Querier查询-
Query Frontend调度、缓存-
Storage对象存储S3 / OSS / MinIO / GCS

7.2 标签设计:Loki 的命脉

Loki 的查询 = 标签过滤 + 文本流式 grep标签设计直接决定查询性能

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# promtail 配置:提取标签
scrape_configs:
  - job_name: kubernetes-pods
    pipeline_stages:
      - docker: {}
      - cri: {}
      - match:
          selector: '{app="order-service"}'
          stages:
            - json:
                expressions:
                  level: level
                  traceId: traceId
            - labels:
                level:  # 把 level 提取成标签
            - metrics:
                log_lines_total:
                  type: Counter
                  description: total log lines
                  config:
                    match_all: true
                    action: inc

标签设计的 4 大原则

原则说明反例
低基数标签值种类 < 100user_idorder_id
可枚举已知的所有值emailphone
查询频率高经常用的过滤条件http_url(每个 URL 一条)
业务维度服务/环境/区域❌ 时间戳(已是索引)

生产推荐标签

1
2
3
4
5
# 必备
{app="order-service", env="prod", region="cn-east-1", level="error"}

# 可选
{namespace="production", pod="order-7d8b-abc", container="app"}

🛑 误区警示:标签和消息内容的边界

  • 标签:索引的、可枚举的、低基数的(结构化
  • 消息内容:不索引、文本流式扫描(自由文本

❌ 把 user_id=12345 做成标签 → 1 亿个时间序列 → Loki 必崩

✅ 把 user_id=12345 放消息内容 → 用 {app="order"} |= "user_id=12345"

7.3 LogQL 查询

LogQL = Loki 的查询语言,分两类:

Log 查询(流式)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1. 基础过滤
{app="order-service"} |= "ERROR"

# 2. 不匹配(排除)
{app="order-service"} != "DEBUG"

# 3. 正则
{app="order-service"} |~ "timeout|exception"

# 4. 多条件
{app="order-service", env="prod"} |~ "timeout" |~ "order_id"

Metric 查询(聚合)

1
2
3
4
5
6
7
# 错误日志数量(按 5 分钟聚合)
sum(rate({app="order-service"} |= "ERROR" [5m]))

# 按错误类型分组
sum by (error_type) (
  rate({app="order-service"} |= "ERROR" | regexp "error_type=(?P<error_type>[\\w]+)" [5m])
)

7.4 Loki vs ELK 选型决策

💡 原理:Loki 为什么便宜

  • ES:每条日志都建倒排索引 → 1TB 日志 → 1.5TB 索引 → 内存/磁盘成本高
  • Loki:只索引标签(不到 1% 体积)+ 把日志流式压缩存到对象存储 → 1TB 日志 → 1.2TB 存储 → 成本低 5-10 倍

代价:Loki 全文搜索是流式 grep(扫整个 chunk),不查索引 → 慢 10-100 倍。

结论:知道"在哪查"(按服务/级别)用 Loki;想"全文搜任意关键字"用 ELK。

八、自动采集:让埋点"零感知"

可观测性体系最大的成本不是存储,是"埋点"自动采集是工业级可观测性的核心

8.1 三大采集模式

模式描述代表适合
Agent 注入字节码增强,应用启动时挂载SkyWalking Agent、OTel Java AgentJava 应用
Sidecar应用旁路采集,每个 Pod 一个Filebeat Sidecar、Vector多语言
DaemonSet节点级采集,Pod 共享Filebeat DS、Promtail DS、Node ExporterK8s + 日志

8.2 Java Agent 自动埋点原理

SkyWalking / OTel Agent 都基于字节码增强(ByteBuddy / ASM)——不改业务代码,自动给所有方法加埋点。

SkyWalking 自动埋点支持的主流库(开箱即用):

类别支持的库
Web 框架Spring MVC / Spring WebFlux / Dubbo / gRPC / Struts
HTTP 客户端HttpClient / OkHttp / RestTemplate / Feign
数据库MySQL JDBC / PostgreSQL JDBC / Druid / HikariCP
缓存Jedis / Lettuce / Redisson
MQKafka / RabbitMQ / RocketMQ / ActiveMQ
RPCDubbo / gRPC / Motan / SofaRPC

📌 实践:Agent 接入 Checklist

  • 业务包名过滤agent.service_nameappname-${env} 模板
  • 慢 SQL 阈值agent.middleware.jdbc.slow_sql_threshold=500ms
  • 采样率:生产设 0.1(10%),关键服务 100%
  • 日志关联%X{traceId} 必须进日志格式
  • 优雅关闭:避免丢失最后 5s 的 span(agent.force_reconnection_period

8.3 K8s 自动采集拓扑

生产推荐混合模式

  • 应用层指标(JVM、HTTP、DB):Agent 注入(最准)
  • 应用日志Sidecar 或 stdout + DaemonSet(最稳)
  • 主机指标(CPU/内存/磁盘/网络):node_exporter DaemonSet(最全)
  • K8s 指标kube-state-metrics + cAdvisor(必备)

8.4 自动采集清单

采集项工具模式必装?
JVM 指标SkyWalking/OTel AgentAgent
HTTP 请求SkyWalking/OTel AgentAgent
数据库调用SkyWalking/OTel AgentAgent
Redis 调用SkyWalking/OTel AgentAgent
MQ 消费SkyWalking/OTel AgentAgent
应用日志Promtail / FilebeatDS/Sidecar
Nginx 日志FilebeatDS视情况
主机指标node_exporterDS
K8s 指标kube-state-metricsDeployment
容器指标cAdvisor内置
数据库指标mysqld_exporter / postgres_exporterDeployment
Redis 指标redis_exporterDeployment视情况
Kafka 指标kafka_exporterDeployment视情况
LB 指标nginx-prometheus-exporterDS视情况

🎯 避坑点:DaemonSet 数量 = 节点数

每个节点一个 Pod,别忘了

  • 资源限制(防止 Pod 抢资源)
  • 节点亲和性(避免 Master 节点跑)
  • 日志轮转(采集器本身会写日志,别把磁盘撑爆

8.5 OpenTelemetry Operator:K8s 原生采集

OTel Operator 是 K8s 部署 OTel 的最佳实践。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 创建 OTel Collector
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
spec:
  mode: deployment  # 或 daemonset
  config: |
    receivers:
      otlp:
        protocols:
          grpc: {endpoint: 0.0.0.0:4317}
          http: {endpoint: 0.0.0.0:4318}
      prometheus:
        config:
          scrape_configs:
            - job_name: 'kubernetes-pods'
              kubernetes_sd_configs:
                - role: pod
    processors:
      batch: {}
      memory_limiter:
        check_interval: 1s
        limit_percentage: 80
        spike_limit_percentage: 20
      resource:
        attributes:
          - key: deployment.environment
            from_attribute: k8s.namespace.labels
            action: insert
    exporters:
      prometheusremotewrite:
        endpoint: http://thanos-receive:19291/api/v1/receive
      otlp/jaeger:
        endpoint: http://skywalking-oap:11800
        tls: {insecure: true}
    service:
      pipelines:
        metrics:
          receivers: [otlp, prometheus]
          processors: [memory_limiter, batch]
          exporters: [prometheusremotewrite]
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch]
          exporters: [otlp/jaeger]
---
# 创建 Instrumentation(CRD),自动给 Pod 注入 Java Agent
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: java-instrumentation
spec:
  exporter:
    endpoint: http://otel-collector:4318
  propagators:
    - tracecontext
    - baggage
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:1.32.0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 在 Deployment 上引用
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    metadata:
      annotations:
        instrumentation.opentelemetry.io/inject-java: "true"
    spec:
      containers:
        - name: app
          image: order-service:1.0.0

📌 实践:OTel Operator 的优势

  • 一行 annotation 注入 Agent,不需改 Deployment 模板
  • 统一管理所有服务的 Agent 版本
  • 自动注入 JAVA_TOOL_OPTIONS 环境变量

强烈推荐 K8s 环境用 OTel Operator——把"埋点"变成"声明式"。

8.6 实战:kube-prometheus-stack 一键部署完整监控

kube-prometheus-stack 是 CNCF 推荐的生产级监控方案,一行 Helm 命令部署完整套件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. 添加 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

# 2. 一键部署(生产推荐自定义 values)
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  --set prometheus.prometheusSpec.retention=30d \
  --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=100Gi \
  --set grafana.adminPassword=yourStrongPassword \
  --set alertmanager.alertmanagerSpec.storage.volumeClaimTemplate.spec.resources.requests.storage=10Gi

部署后包含的组件

组件数量作用
Prometheus Operator1管理 Prometheus 生命周期
Prometheus1主指标采集(可副本)
Alertmanager1告警路由
Grafana1可视化
node-exporterDaemonSet主机指标
kube-state-metrics1K8s 资源指标
pushgateway1短任务指标
预设告警规则200+K8s 告警全开箱即用

Kube-prometheus-stack 的 ServiceMonitor 模式(自动发现 + 抓取):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 给应用配 ServiceMonitor,Prometheus 自动抓取
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: order-service
  labels:
    release: kube-prometheus  # 必须与 Helm release 名一致
spec:
  selector:
    matchLabels:
      app: order-service
  endpoints:
    - port: http  # Service 的端口名
      path: /actuator/prometheus
      interval: 30s
      scrapeTimeout: 10s

PrometheusRule 模式(告警规则即代码):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: order-service-alerts
  labels:
    release: kube-prometheus
spec:
  groups:
    - name: order-service
      interval: 30s
      rules:
        - alert: OrderServiceHighErrorRate
          expr: |
            sum(rate(http_server_requests_seconds_count{service="order-service", status=~"5.."}[5m]))
            /
            sum(rate(http_server_requests_seconds_count{service="order-service"}[5m]))
            > 0.05
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "订单服务错误率 > 5%"
            runbook_url: "https://wiki.example.com/runbook/OrderServiceHighErrorRate"

📌 实践:kube-prometheus-stack 调优清单

  • 存储:Prometheus 100Gi 起步(按 30 天保留 × 100 万序列估算)
  • 副本:Prometheus 用 2 副本 + AntiAffinity(避免单点)
  • 远程存储:接 Thanos/Mimir 长期保留
  • 抓取间隔:核心服务 15s,普通服务 30s,批处理 60s
  • 告警去重:Alertmanager + AlertmanagerConfig CRD
  • Grafana 持久化:配置用 ConfigMap 挂载(避免重启丢配置)
  • NodePort/Ingress:通过 Ingress 暴露 Grafana(生产不要 NodePort)

升级路径(按规模演进):

生产经验:30+ 服务的团队直接上 kube-prometheus-stack 即可——所有 K8s 告警规则都开箱即用,比自研省 3 个月工作量。

九、自动告警:从阈值到 On-Call

告警的本质是"在故障变严重之前通知能处理它的人"。一个好的告警系统需要解决 4 个问题:触发什么、什么时候触发、通知谁、怎么升级。

9.1 告警的 4 大属性

属性含义例子
触发规则(What)什么条件下触发P99 > 1s 持续 5min
持续时间(When)持续多久才发for: 5m
通知对象(Who)通知谁On-Call 工程师
升级路径(Escalation)没人响应怎么办5min 升级到主管

9.2 告警分级:P0/P1/P2/P3

生产推荐 4 级告警

等级触发条件响应时间通知方式升级路径
P0 紧急核心业务不可用5 分钟电话 + 短信 + 钉钉5min → 主管,15min → 总监
P1 严重部分用户受影响30 分钟钉钉 @all30min → 主管,1h → 总监
P2 警告接近阈值4 小时钉钉 @值班1h → 主管
P3 信息仅记录24 小时邮件 / 群消息不升级

🛑 误区警示:把所有告警都标 P0

P0 泛滥 = 没有 P0。我见过某团队 73 条告警全标 P0,结果:

  • 真出 P0 时,没人当真
  • 工程师把钉钉群设成免打扰
  • 告警系统变成"狼来了"

核心原则:P0 每周最多触发 1-2 次,否则就不是 P0

9.3 Alertmanager 路由配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# alertmanager.yml
global:
  resolve_timeout: 5m

route:
  receiver: 'default-receiver'
  group_by: ['alertname', 'service', 'cluster']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  
  routes:
    # P0 紧急:电话 + 短信 + 钉钉
    - match_re:
        severity: critical
      receiver: 'p0-oncall'
      group_wait: 10s
      repeat_interval: 5m
      continue: true
      routes:
        - match_re:
            severity: critical
          receiver: 'p0-phone'
          # 不 group_wait,立即电话
    
    # P1 严重:钉钉 + 升级
    - match_re:
        severity: warning
      receiver: 'p1-oncall'
      group_wait: 1m
      repeat_interval: 30m
    
    # P2 警告:钉钉群消息
    - match_re:
        severity: info
      receiver: 'p2-oncall'
      group_wait: 5m
      repeat_interval: 4h

receivers:
  - name: 'p0-oncall'
    webhook_configs:
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=xxx'
        send_resolved: true
        # 钉钉 @oncall 工程师
    wechat_configs:
      - corp_id: 'xxx'
        agent_id: 'xxx'
        api_secret: 'xxx'
        to_user: 'oncall-team'
  
  - name: 'p0-phone'
    pagerduty_configs:
      - service_key: 'xxx'
        description: '{{ .CommonAnnotations.summary }}'
    # 阿里云语音通知 / 腾讯云电话告警
    webhook_configs:
      - url: 'http://voice-alert:8080/alert?phone=oncall'
  
  - name: 'p1-oncall'
    webhook_configs:
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=xxx'
  
  - name: 'p2-oncall'
    webhook_configs:
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=xxx'

# 抑制规则:避免告警风暴
inhibit_rules:
  - source_match_re:
      severity: 'critical'
    target_match_re:
      severity: 'warning'
    equal: ['alertname', 'service']
    # critical 触发时,抑制同服务 warning

9.4 告警通知通道

通道适用优势劣势
钉钉 / 飞书 / 企微国内主流集成好、免费易刷屏
PagerDuty国际化专业 On-Call付费
OpsGenie国际化通知升级完善付费
电话 / 短信P0 紧急必达贵、骚扰
邮件P3 留档不骚扰

生产推荐组合

  • P0:钉钉 + 电话(双通道) + 自动语音呼叫
  • P1:钉钉 @值班
  • P2:钉钉群消息
  • P3:邮件 + 群消息

9.5 告警值班(On-Call)轮值

没有 On-Call 轮值 = 告警来了没人处理

角色职责比例
Primary第一响应人1 人
SecondaryPrimary 不响应时接手1 人
Manager重大故障协调1 人

轮值周期

业务规模轮值周期备注
小型(1-5 服务)1 周工程师轮值
中型(10-50 服务)1 周SRE 团队轮值
大型(100+ 服务)1 天7×24 专业 SRE

📌 实践:On-Call 必做 3 件事

  1. 轮值表提前发布——所有人都知道自己什么时候 On-Call
  2. 值班手机必带——On-Call 时间段手机不离身
  3. On-Call 补偿——调休 / 加班费 / On-Call 津贴

On-Call 是反人性的,不补偿会引发团队离职。

十、告警治理:告警疲劳、降噪、Runbook

没有治理的告警系统 = 没有告警告警治理的目标是:每条告警都被响应

10.1 告警疲劳:最常见的 3 大症状

症状表现后果
告警泛滥每天 100+ 告警工程师设免打扰
重复告警同一故障 50 条真正重要的被淹没
假阳性多触发 10 次只 1 次真没人当真

10.2 告警降噪的 5 大技术

1. 告警分组(Grouping)

1
2
3
# Alertmanager 按 alertname + service 分组
group_by: ['alertname', 'service', 'cluster']
group_wait: 30s  # 30 秒内的告警合并成一条

2. 告警抑制(Inhibition)

1
2
3
4
5
6
7
# critical 触发时抑制同服务的 warning
inhibit_rules:
  - source_match_re:
      severity: 'critical'
    target_match_re:
      severity: 'warning'
    equal: ['alertname', 'service']

3. 告警静默(Silences)

1
2
3
4
5
# 升级期间静默告警
amtool silence add --alertmanager=http://localhost:9093 \
  --start="2026-06-09 22:00" --end="2026-06-10 02:00" \
  --matcher="service=order-service" \
  --comment="订单服务升级维护"

4. 告警去重

1
2
# Alertmanager 5 分钟内同 alertname + instance 只发一次
repeat_interval: 5m

5. 告警路由(智能升级)

1
2
3
4
5
6
7
8
# 工作时间 / 非工作时间不同通知策略
- matchers:
    - severity = "critical"
  receiver: 'business-hours-pager'
- matchers:
    - severity = "critical"
    - environment = "prod"
  receiver: 'after-hours-pager'

10.3 SLO 驱动的告警(多窗口多燃烧率)

传统阈值告警(P99 > 1s)的问题是:阈值是拍脑袋的。SLO 告警用"剩余预算"判断。

SLO 概念说明
SLO服务质量目标(如可用性 99.9%)
Error Budget错误预算(30 天内允许 0.1% × 30 × 86400 = 2484s 不可用)
Burn Rate错误预算消耗速度(1x = 30 天刚好耗完,2x = 15 天耗完)

Google SRE 推荐的 4 个告警窗口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 短期快燃烧(5 分钟)—— 立即响应
sum(rate(http_requests_total{status=~"5.."}[5m]))
  / sum(rate(http_requests_total[5m]))
  > (14.4 * 0.001)  # 14.4x 燃烧率

# 中期快燃烧(30 分钟)—— 30min 内响应
... [30m] > (6 * 0.001)

# 长期慢燃烧(6 小时)—— 6h 内响应
... [6h] > (1 * 0.001)

# 超长期慢燃烧(3 天)—— 3d 内响应
... [3d] > (1 * 0.001)

🎯 避坑点:SLO 告警 vs 阈值告警

  • 阈值告警:P99 > 1s 持续 5min → 触发。简单但不准
  • SLO 告警:错误预算 1h 燃烧 10% → 触发。科学但复杂

生产推荐:核心业务用 SLO 告警(细),普通业务用阈值告警(粗)。

10.4 Runbook:让告警"自带解法"

Runbook 是告警的最佳拍档——告诉响应人"我看到这条告警,应该做什么"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 告警:HighP99Latency(订单服务 P99 > 1s)

## 1. 确认问题
- 打开 [订单服务大盘](https://grafana.example.com/d/order-overview)
- 查看 P99 曲线,判断趋势
- 打开 [SkyWalking 慢调用面板](https://skywalking.example.com/dashboard)

## 2. 常见原因(按概率排序)
- **下游服务慢**(占 60%):查看 SkyWalking 慢 span
- **JVM 频繁 FullGC**(占 20%):看 jvm_gc_pause_seconds
- **数据库慢查询**(占 15%):看 MySQL 慢日志
- **线程池耗尽**(占 5%):看 jvm_threads_states

## 3. 应急操作
- 限流降级:执行 `sentinel-cli flowRule add order-service 5000`
- 切只读:联系 DBA 切数据库只读
- 扩容:HPA 自动扩容或手动 `kubectl scale deploy order-service --replicas=20`

## 4. 复盘要求
- 24h 内提交事故复盘
- P0/P1 必须有 RCA(Root Cause Analysis)

💡 原理:Runbook 的反"告警疲劳"作用

  1. 降低 MTTR(平均恢复时间)——新人也能 5 分钟内处理
  2. 降低 MTTA(平均确认时间)——响应人知道严重程度
  3. 减少误操作——明确"该做什么"和"不该做什么"

生产强制要求:P0/P1 告警必须有 Runbook。

10.5 告警治理 Checklist

  • 告警分级——P0/P1/P2/P3 全员对齐
  • 抑制规则——避免风暴
  • On-Call 轮值——7×24 覆盖
  • Runbook 完备——P0/P1 100% 覆盖
  • 告警复盘——每月一次,清理假阳
  • 告警健康度——告警数 / 服务数 < 1(健康)
  • 值班补偿——调休或加班费
  • 升级路径明确——30min 没人响应自动升级

十一、真实案例

11.1 某电商大促:从 7 套监控系统到统一可观测性

背景:2021 年双 11 大促,技术栈 200+ Spring Cloud 微服务 + 30+ 中间件 + 4 个数据中心。

重构前的 5 大痛点

  1. 数据孤岛:Zabbix 看不到 JVM 详细指标,Prometheus 看不到数据库慢查询,商业 APM 看不到业务指标
  2. 告警混乱:73 条告警全标 P0,真出 P0 时没人当真
  3. 定位缓慢:故障平均定位 30 分钟(大促峰值 1 小时+)
  4. 存储爆炸:日志日均 5TB,ES 集群 50+ 节点
  5. On-Call 痛苦:工程师每周值班 2 次,每次 12 小时,离职率激增

重构方案(历时 3 个月,“1+1+1+1+N”):

第一步:指标统一(4 周)

  • 全量接入 Prometheus + SkyWalking Agent
  • 200+ 服务全埋点,业务指标优先(下单成功率、支付成功率、库存命中率)
  • Thanos 做远端存储,保留 1 年
  • Grafana 4 层大盘(总览 / 应用 / 诊断 / 根因)

第二步:日志统一(3 周)

  • 业务日志(80%):Loki + Promtail + 对象存储
  • 审计日志(20%):ELK 集群(缩到 10 节点)
  • 日志格式统一:%X{traceId} 必须输出
  • 7 套日志平台下线 5 套

第三步:链路统一(2 周)

  • SkyWalking 全量接入
  • 关键服务(支付/订单)100% 采样
  • 错误和慢调用强制 100% 采样
  • Jaeger、自研链路追踪全部下线

第四步:告警统一(2 周)

  • Alertmanager 统一告警路由
  • 4 级告警(P0/P1/P2/P3)+ On-Call 轮值
  • 抑制规则 + Runbook 100% 覆盖 P0/P1
  • 5 套通知通道收敛成 2 套(钉钉 + 电话)

重构后的收益(2022-2025)

指标重构前重构后提升
告警数日均 800+日均 50降 16 倍
MTTA(平均确认)15 分钟1 分钟降 15 倍
MTTR(平均恢复)45 分钟8 分钟降 5.6 倍
ES 节点数50+15降 70%
存储成本50 万/月18 万/月降 64%
On-Call 满意度2.1/54.3/5升 2 倍

关键教训

  1. 不要为了"全栈可观测性"硬上 Datadog——贵且黑盒,自建 + 开源是性价比最高的路径
  2. 告警治理比建设更难——花 50% 时间在告警分级 + Runbook
  3. On-Call 补偿是底线——没有补偿的可观测性就是压榨
  4. 业务指标比系统指标贵 10 倍价值——CPU 100% 你不知道影响什么,“支付成功率从 99.9% 跌到 95%“直接关系到 GMV
  5. 可观测性是持续工程——不是"建设完就完了”,每月告警健康度复盘、季度容量评估、年度架构 review

痛点

  • 7 套监控(Zabbix + Prometheus + 商业 APM + 自研 + …)
  • 5 套告警通道(钉钉/飞书/Slack/邮件/电话)
  • 3 套日志平台(ELK × 2 + 商业日志)
  • 2 套链路追踪(自研 + SkyWalking)
  • 故障定位 30+ 分钟,告警风暴频繁

方案(“1+1+1+1+N”):

收益

  • 告警数从日均 800+ 降到 50(降 16 倍
  • 故障定位从 30 分钟降到 3 分钟
  • MTTR 从 45 分钟降到 8 分钟
  • 存储成本降 40%(Loki 替代部分 ES)

11.2 某金融:合规 + 全链路追踪

背景:传统金融机构,监管要求审计日志保留 5 年

痛点

  • ELK 容量爆炸(每天 2TB 日志)
  • ES 集群 50+ 节点
  • 监管检查要求"任意交易 5 分钟内可追溯”

方案

核心设计

  • 三层存储:Hot (ES 7天) → Warm (OSS 90天) → Cold (蓝光 5年)
  • TraceID 全链路贯穿:业务日志 + 审计日志 + 链路追踪强关联
  • 告警与监管联动:核心交易异常 → 监管报送接口

收益

  • ES 节点从 50+ 减到 15(降 70%
  • 5 年内任意交易 5 分钟可查
  • 通过等保三级 + 银保监现场检查

11.3 某 SaaS:Loki 替代 ELK 的成本优化

背景:SaaS 产品,30+ 服务,日志量 500GB/天。

痛点

  • ES 集群 20 节点,月成本 8 万
  • 查询性能差,复杂查询 30 秒
  • 业务查询多是"按服务/级别"(80% 场景),全文搜是少数

方案

  • 业务日志(80% 流量)→ Loki + Promtail + Grafana
  • 审计日志(20% 流量,按全文字段)→ 保留 ELK 小集群(5 节点)

收益

  • 存储成本从 8 万/月 → 1.5 万/月(降 80%
  • 按服务/级别查询速度提升 5 倍
  • ES 集群维护成本几乎归零

教训

  • 不要照搬"大厂都用 ELK"——Loki 在"已知服务"场景下更合适
  • 80/20 原则:80% 的查询是"已知维度"(服务/级别/时间),只有 20% 是全文搜

十二、总结

12.1 可观测性体系的 3 大核心要素

  1. 三支柱统一:Metrics + Logging + Tracing 用 TraceID 关联
  2. 自动采集:Agent / Sidecar / DaemonSet,埋点零感知
  3. 分级告警:P0/P1/P2/P3 + On-Call + Runbook

12.2 选型速查

场景推荐
业务日志(按服务查)Loki
审计日志(合规、全文)ELK
链路追踪(Java 为主)SkyWalking
指标监控Prometheus + Grafana
自动埋点OTel Agent / SkyWalking Agent
告警路由Alertmanager
K8s 部署OTel Operator + DaemonSet

12.3 何时该上复杂体系

业务特征推荐方案
微服务 < 5 个Zabbix + 单机 ELK + 商业 APM
微服务 5-30Prometheus + Grafana + Loki
微服务 30-100+ SkyWalking + 远端存储
微服务 100++ 商业 SaaS(Datadog / NewRelic)

12.4 系列预告

这是 Java Web 微服务系列 的第 9 篇。后续计划:

  • 可观测性实战:从零搭建生产级监控体系
  • Spring Cloud Alibaba 实战:Nacos + Sentinel + Seata + SkyWalking 全套
  • 异地多活:单元化、流量调度、灰度发布、可观测性联动
  • 生产事故复盘:从 30+ 真实事故看可观测性价值
  • AI 运维(AIOps):异常检测、容量预测、根因分析

十三、监控与日志实施 Checklist

13.1 采集层 Checklist

  • JVM 指标:所有 Java 服务挂载 Agent
  • HTTP 指标:QPS / P99 / 错误率全覆盖
  • DB 指标:mysqld_exporter / postgres_exporter
  • 中间件指标:Redis / Kafka / RabbitMQ Exporter
  • 主机指标:node_exporter DaemonSet
  • K8s 指标:kube-state-metrics + cAdvisor
  • 应用日志:Promtail / Filebeat 采集 + 关联 TraceID
  • 链路追踪:SkyWalking / OTel Agent 注入

13.2 存储层 Checklist

  • 指标存储:Prometheus + 远端存储(Thanos/Mimir)
  • 日志存储:业务用 Loki,审计用 ELK
  • 链路存储:SkyWalking + ES 后端
  • 生命周期管理:指标 15 天、日志 30 天、链路 7 天
  • 容量规划:存储 GB = 速率 × 86400 × 保留天数 × 1.5

13.3 可视化层 Checklist

  • 4 层大盘:总览 / 应用 / 诊断 / 根因
  • 黄金信号:QPS / 错误率 / P99 / 饱和度
  • USE / RED 指标:资源 + 服务双视角
  • 变量可筛选:service / region / env
  • 告警标注:触发告警的曲线变红
  • 加载 < 3 秒:避免复杂查询

13.4 告警层 Checklist

  • 4 级告警:P0/P1/P2/P3
  • On-Call 轮值:7×24 覆盖
  • 抑制规则:避免告警风暴
  • Runbook 完备:P0/P1 100% 覆盖
  • SLO 告警:核心业务用 Burn Rate
  • 值班补偿:调休 / 加班费
  • 升级路径:30min 没人响应自动升级

13.5 团队层 Checklist

  • SRE 能力:能调优 Prometheus + 写 PromQL
  • 研发能力:会看 SkyWalking 慢调用 + 优化 SQL
  • 值班制度:7×24 响应
  • 复盘机制:P0/P1 24h 内复盘 + RCA
  • 告警健康度:告警数 / 服务数 < 1

十四、常见问题 FAQ

Q1:SkyWalking vs Jaeger 怎么选?

A:Java 生态选 SkyWalking

维度SkyWalkingJaeger
自动埋点(20+ 主流库)弱(要手写 SDK)
JVM 指标内置需另接
服务拓扑自动绘制需另接
告警内置需另接
适合Java 微服务首选多语言、Go/Python

Q2:Prometheus 标签基数爆炸怎么办?

A:3 个步骤:

  1. :用 tsdb 工具查 top 高基数标签
  2. :relabel_configs 删除不必要标签
  3. :超 200 万序列上 Thanos / Mimir 分片
1
2
# 查看 top 10 高基数标签
promtool tsdb analyze /prometheus/data

Q3:Loki 全文搜索很慢怎么办?

A:Loki 不适合全文搜索。混合架构

  • 80% 查询(按服务/级别)→ Loki
  • 20% 查询(任意关键字)→ ELK

如果一定要 Loki 全文搜:升级 SSD + 加 CPU + 调高 query_timeout

Q4:Java Agent 影响性能吗?

A影响 < 5%(生产实测)。

场景性能损耗
正常调用< 3%
高 QPS 服务(10万 QPS)< 5%
FullGC 频繁时< 8%

如果不能接受:用 Sidecar + eBPF(Cilium Tetragon)—— 0 侵入但成熟度不够。

Q5:ES 集群扩容难怎么办?

A改用 ILM + 冷热分层

  • Hot 节点:SSD,3 副本
  • Warm 节点:HDD,1 副本 + 压缩
  • Cold 节点:OSS + 冻结索引

扩容时加 Warm/Cold 节点比加 Hot 节点便宜 10 倍。

Q6:告警风暴怎么处理?

A4 步止血

  1. 立即分组抑制inhibit_rules 配 critical 抑制 warning
  2. 临时静默amtool silence add 静默 1 小时
  3. 修复根源:等系统恢复后逐步放开
  4. 复盘清理:找出根因(一般是部署/网络故障),加 SLO 兜底

Q7:K8s 集群上怎么部署整套监控?

Akube-prometheus-stack 一键部署(生产推荐)。

1
2
3
4
# 一键安装
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace

包含

  • Prometheus Operator
  • Alertmanager
  • Grafana
  • node_exporter
  • kube-state-metrics
  • 一堆预设告警规则

Q8:可观测性建设顺序是什么?

A4 阶段

阶段周期内容
1. 基础1-2 月Prometheus + Grafana + Agent 埋点
2. 日志1 月Loki/ELK + 日志关联 TraceID
3. 链路1 月SkyWalking + 慢调用分析
4. 治理持续告警分级 + On-Call + Runbook

别想一步到位——先把基础打好,用 3 个月走完前 3 阶段,第 4 阶段持续优化

Q9:自研 APM 还是用开源?

A直接用开源

  • 30+ 服务:SkyWalking 够用
  • 100+ 服务:商业 SaaS(Datadog / NewRelic)比自研便宜
  • 只有 1-2 服务:自研 ROI 太低

自研 APM 的坑

  • 字节码增强框架(ByteBuddy / ASM)有兼容性问题
  • 采样策略调不好会丢关键 trace
  • 自研 UI 永远不如 Grafana

Q10:告警的"沉默"该怎么设计?

A3 类沉默合理

  1. 计划内维护:部署/升级期间,静默相关服务告警
  2. 依赖告警:下游服务告警时,抑制上游(避免重复)
  3. 静默期后强制确认amtool silence expire 24h 强制检查

永远不要

  • 静默告警 7 天以上
  • 静默 P0/P1 告警超过 30 分钟

Q11:Prometheus 集群怎么分片?

AHash 分片 + Sidecar 模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# prometheus 分片示例(按 service 名 hash)
remote_write:
  - url: http://thanos-receive:19291/api/v1/receive
    write_relabel_configs:
      - source_labels: [__name__]
        regex: 'go_.*|process_.*'
        action: drop
      - source_labels: [service]
        modulus: 3  # 3 个分片
        target_label: shard
        action: hashmod

分片策略选择

策略优点缺点适合
按 service hash数据均匀跨 service 查询需要 fanout中小规模
按 namespace团队边界清晰单 namespace 太大时不均多团队
按 region跨地域隔离全局查询难多 region
Sidecar(每节点一个)节点级数据隔离元数据查询复杂大型集群

经验公式

  • 每分片活跃序列 < 200 万
  • 每分片摄入速率 < 10 万 samples/s
  • 总活跃序列 = 分片数 × 200 万

Q12:链路追踪怎么和日志关联?

ATraceID 是桥梁。3 步实现:

  1. Agent 自动注入:SkyWalking/OTel 自动把 traceId 放进 MDC
  2. 日志格式输出 traceId%X{traceId}(Logback/Log4j2)
  3. 存储端建索引:ES/Loki 把 traceId 设为可查询字段
1
2
3
4
<!-- Logback 关键配置 -->
<pattern>
    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [service=%X{service},traceId=%X{traceId:-},spanId=%X{spanId:-}] %logger{36} - %msg%n
</pattern>

查询流程

  • 用户报障:“我刚才下单失败”
  • SRE 拿到用户 traceId(如 abc123
  • ELK 查询:traceId:abc123 → 拿到完整业务日志
  • SkyWalking 查询:同一 traceId → 拿到完整调用链耗时
  • 10 分钟内定位根因

📌 实践:没有 TraceID 关联 = 监控失明

我见过多个团队建设了 Prometheus + ELK + SkyWalking 但没有关联,结果:

  • 知道 CPU 100%(Prometheus)
  • 知道应用报错(ELK)
  • 知道调用慢(SkyWalking)
  • 但三套数据连不起来,故障定位依然慢

铁律:任何日志、任何 trace、任何 metric,必须能用 TraceID 关联

Q13:可观测性数据保留多久合适?

A分级保留——成本和价值的平衡。

数据类型热存储(快速查询)冷存储(合规/取证)永久
指标(Metrics)15-30 天1 年(远端存储)-
业务日志7-15 天90 天-
审计日志30 天-5 年+(合规)
链路追踪3-7 天--
告警历史90 天1 年-

成本估算(以 100 服务、日均 500GB 数据为例):

1
2
3
4
指标:500 万序列 × 15 天 × 1KB = 75 GB(便宜)
日志:500GB × 7 天 = 3.5 TB(中等)
审计:50GB × 365 天 × 5 年 = 91 TB(贵)
追踪:100 万 span × 7 天 × 2KB = 1.4 TB(中等)

降本 3 招

  1. 采样:trace 采样 10%(降 90% 成本)
  2. 冷热分层:30 天前日志扔到 OSS/蓝光(降 80% 成本)
  3. 字段裁剪:日志只保留必要字段(降 50% 体积)

Q14:如何评估可观测性体系是否成熟?

A4 个等级

等级特征关键指标团队状态
L1 救火出事现抓MTTR > 1h1-2 个工程师手动查
L2 监控有大盘 + 告警MTTR 30min团队 SRE 兼职
L3 可观测3 支柱齐全 + 关联MTTR 5-10min7×24 On-Call
L4 自愈AIOps 异常检测 + 自愈MTTR < 5minAIOps 平台 + SRE

自评 Checklist

  • 故障定位 < 10 分钟(MTTR)
  • 告警准确率 > 80%(假阳 < 20%)
  • 日志有 traceId 关联
  • 3 支柱数据都覆盖
  • 7×24 On-Call
  • 90% 告警有 Runbook
  • 月度告警健康度复盘

生产目标:从 L1 到 L3,6-12 个月;从 L3 到 L4,12-24 个月

Q15:AI 运维(AIOps)怎么落地?

A3 阶段渐进

阶段能力工具
阶段 1:异常检测指标自动告警(替代阈值)Prometheus 智能告警 / Grafana ML
阶段 2:根因分析自动定位故障域SkyWalking + 知识图谱 / 阿里鹰眼
阶段 3:自动修复故障自愈(限流/降级/扩容)Sentinel + K8s Operator + ChatOps

生产经验

  • 阶段 1 容易(已有开源方案)
  • 阶段 2 难(需要知识图谱 + 历史故障库)
  • 阶段 3 慎用(误操作风险大,先 P3 故障试水)

核心原则AIOps 永远是人 + AI 协作,不是 AI 替代人。AI 给"建议",人做"决策"。

推荐阅读

官方文档

书籍

  • 《Site Reliability Engineering》(Google)—— SRE 圣经
  • 《Observability Engineering》(O’Reilly)—— 可观测性权威指南
  • 《Distributed Systems Observability》(Cindy Sridharan)—— 分布式系统可观测性
  • 《Prometheus: Up & Running》(Brian Brazil)—— Prometheus 实战
  • 《Cloud Native Observability》(Shiwan Kohli)—— 云原生可观测性

技术博客

  • Grafana 官方博客:Loki、Tempo、Mimir 新特性
  • CNCF Observability TAG:可观测性技术趋势
  • OpenTelemetry Blog:采集标准演进
  • 字节跳动技术博客:可观测性实践
  • 美团技术团队:监控告警体系
  • 阿里云原生:可观测性产品化

开源项目

💡 学习路径建议

  1. 入门:本文 + Prometheus 官方文档,2 周能上手
  2. 进阶:阿里 / Grafana / SkyWalking 博客,理解工业实践
  3. 实战:用 kube-prometheus-stack 部署一套 K8s 监控
  4. 深潜:读 SkyWalking 源码(建议从 apm-sniffer 入口),理解字节码增强

理论 + 实践 + 源码,缺一不可。

参考文章

本系列共 16 篇,本文为第 14 篇 · 查看全部
使用 Hugo 构建
主题 StackJimmy 设计