日志与监控是每一个生产系统的「眼睛」。当业务系统出问题时,第一件事就是去看日志、看指标、看链路。它同时是「海量写入」和「高频查询」的典型矛盾体:每天上亿条日志要便宜地写进来,还要能在几秒内查出来。本文按面试答题结构设计一个集日志、指标、追踪于一体的可观测性平台。
一句话:可观测性系统的核心矛盾是「海量写入 vs 快速查询」,解法是采集端削峰、存储端分层、查询端倒排/时序索引,最后用告警把数据变成行动。
一、需求澄清与量级估算
1.1 需求澄清
- 范围:只做日志,还是日志 + 指标 + 分布式追踪(完整可观测性)?我们做完整三件套。
- 接入对象:自研微服务、第三方云服务、数据库/中间件?
- 查询诉求:关键字检索、聚合统计、链路查看、告警规则?
- 保留期:热数据保留多久,冷数据(合规)保留多久?
- 规模与成本:千万级容器集群?日志量级多大?是否接受采样降成本?
明确假设:
| 需求项 | 假设 |
|---|---|
| 服务数 | 5000 个微服务实例 |
| 日志量 | 峰值 10 亿条/天,约 30 TB/天 |
| 指标量 | 5000 万 个时间序列(series) |
| 追踪量 | 每秒 20 万 span |
| 查询 SLA | 关键字检索 P95 < 3s |
| 告警 | 分钟级规则判定,多通道通知 |
1.2 量级估算
| 指标 | 估算值 | 推导 |
|---|---|---|
| 日志写入 QPS | ~120 万/秒 | 10 亿/86400 × 峰值系数 |
| 每日日志原始量 | 30 TB | 每条约 3 KB × 10 亿 |
| 压缩后存储 | 8 TB/天 | 压缩比 3-4 倍 |
| 年存储 | ~3 PB | 含冷存储分层 |
| 指标写入 | 数百万/秒 | 5000 万 series × 采集周期 15s |
一句话:写带宽上百 GB/s、存储 PB 级,可观测系统「写」必须走管道削峰 + 批量压缩,绝不能逐个请求同步落库。
1.3 非功能需求
| 需求 | 目标 | 说明 |
|---|---|---|
| 可用性 | 99.99% | 故障排查时它必须在线,反向依赖它 |
| 写入可靠性 | 至少一次、尽量不丢 | 采集端缓冲 + 管道重试 |
| 查询延迟 | P95 < 3s | 关键字检索 |
| 数据保留 | 热 7 天 / 冷 1 年+ | 合规与排障 |
| 安全合规 | 脱敏、权限隔离、审计 | 日志可能含 PII |
一句话:可观测系统自身的可用性优先于一切——监控挂了,故障排查就瞎了,所以要格外强调冗余与缓冲。
二、高层架构设计
微服务实例1 微服务实例2 ... 数据库/中间件/网关
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────┐
│ 采集层 (Agent / Exporter) │
│ 日志采集Filebeat/Fluentd 指标采集Prometheus │
│ 追踪采集 OTel SDK + Agent 链路聚合 │
└───────────────┬─────────────────────────────┘
│ (批量/压缩/本地缓冲)
┌───────────────▼─────────────────────────────┐
│ 传输与缓冲层 │
│ Kafka / 管道 (削峰、批量、分区、重试) │
└───────────────┬─────────────────────────────┘
┌───────────────▼─────────────────────────────┐
│ 存储与处理层 │
│ 日志: ES/Loki │ 指标: 时序库(Prometheus/TSDB) │
│ 追踪: 链路存储(Otel/ClickHouse) │
│ 实时处理: Flink(解析/提取/聚合/告警计算) │
└───────────────┬─────────────────────────────┘
│
┌───────────────▼─────────────────────────────┐
│ 查询与应用层 │
│ 搜索/看板(Grafana) │ 告警/通知 │ 追踪查看 │
└─────────────────────────────────────────────┘
2.1 三大数据类型对比
| 类型 | 形态 | 典型存储 | 查询方式 |
|---|---|---|---|
| 日志(Logs) | 非结构化文本 | ES / Loki | 全文检索、正则、字段过滤 |
| 指标(Metrics) | 数值时间序列 | Prometheus TSDB / VictoriaMetrics | 聚合、下采样、同比环比 |
| 追踪(Traces) | 树状调用链 | Tempo / Jaeger / ClickHouse | 按 traceId 关联、瀑布图 |
一句话:日志回答「发生了什么」,指标回答「规模多大、是否健康」,追踪回答「哪一环拖慢」,三者在 traceId / 时间戳上关联成统一视图。
三、核心组件设计
3.1 日志采集 Agent
Agent 以 DaemonSet / Sidecar 部署在每个节点,职责:
- 采集多源日志(stdout、文件、syslog),解析多行与格式。
- 本地缓冲 + 批量发送:失败重试,防止源端抖动导致丢日志。
- 标签注入:添加 service、pod、namespace、node、环境等维度字段。
filebeat.inputs:
- type: container
paths: ["/var/log/containers/*.log"]
fields:
service: payment
env: prod
processors:
- add_host_metadata: {}
- dissect:
tokenizer: "%{ts} [%{level}] %{msg}"
output.kafka:
hosts: ["kafka:9092"]
topic: "raw-logs"
partition.hash:
hash: ["fields.service"]
3.2 传输与缓冲层
- Kafka:天然削峰(写入快、消费慢)、多消费者(存储 + Flink 实时处理)、可回溯。
- 分区策略:按 service 分区,保证单服务日志有序(排查问题友好)。
- 批量与压缩:Agent 端 gzip/lz4 压缩,Kafka 端批量写入,吞吐可提高 3-5 倍。
一句话:日志写入的高峰是突发性的,Kafka 做缓冲池把「写」与「处理」解耦,是海量日志系统的命脉。
3.3 存储选型
| 方案 | 优势 | 劣势 | 适用 |
|---|---|---|---|
| Elasticsearch | 全文检索强、聚合丰富 | 写入成本高、集群运维重 | 中小规模、复杂查询 |
| Loki | 只存索引 + 对象存储,成本低 | 全文能力弱、依赖标签 | 大规模、成本敏感 |
| ClickHouse | 列式、压缩比高、聚合极快 | 全文检索需配合 | 超大规模日志/链路 |
| Prometheus/TSDB | 指标标准生态 | 高基数痛点 | 指标监控 |
| VictoriaMetrics | 高基数优化、低成本 | 社区相对小 | 大规模指标 |
本文选型组合:日志走「Kafka → 实时解析 → ClickHouse(热)+ 对象存储(冷)」,查询层用 ClickHouse + 标签索引;指标走 VictoriaMetrics;追踪走「OTel → Kafka → ClickHouse 表」。
3.4 日志存储 Schema(ClickHouse)
CREATE TABLE logs ON CLUSTER cluster (
timestamp DateTime64(3),
service LowCardinality(String),
level LowCardinality(String),
host LowCardinality(String),
pod LowCardinality(String),
trace_id String,
message String,
fields Map(String, String) -- 半结构化字段
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (service, timestamp, trace_id) -- 分区裁剪 + 范围扫描
TTL toDateTime(timestamp) + INTERVAL 30 DAY TO VOLUME 'cold';
CREATE TABLE metrics ON CLUSTER cluster (
ts DateTime,
metric LowCardinality(String), -- 指标名
labels Map(String, String), -- 维度标签
value Float64
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(ts)
ORDER BY (metric, ts, labels);
3.5 告警系统
指标/日志 → Flink/规则引擎 持续计算 → 命中阈值/异常 → 生成告警事件
→ 聚合去重(同一故障只告一次) → 分派路由 → 通知(PagerDuty/钉钉/邮件/IM)
→ 跟踪确认/恢复 → 降噪(静默/升级)
规则定义示例:
- name: 支付服务高错误率
expr: rate(log_error_total{service="payment"}[5m]) / rate(log_total{service="payment"}[5m]) > 0.05
for: 5m
labels:
severity: P1
annotations:
summary: 支付错误率超过 5%
告警设计要点:避免「告警风暴」(同一故障多条告警)——用分组 + 依赖树 + 静默期;告警必须可行动(给出 runbook 链接);用 SLO 指导告警优先级(先报用户可感知的)。
3.6 日志规范与上下文注入
日志系统的效果上限由「日志写得好不好」决定,采集端要推动三个规范:
- 结构化日志:不要拼字符串,用 JSON 结构化字段(level、ts、service、trace_id、业务字段),便于索引与过滤。
- 上下文注入:请求入口生成
request_id / trace_id,注入日志,让同一次请求的所有日志可一键串联。 - 日志分级:ERROR 必须含可定位信息(异常栈、关键参数);DEBUG 只进调试环境,生产默认 INFO 起;敏感信息脱敏(手机号、卡号打码)。
# 好的日志: 结构化 + 带 trace_id
logger.info(
"order_paid",
extra={
"trace_id": req.trace_id,
"order_id": order.id,
"amount": order.amount_cents,
"channel": order.channel,
},
)
# 坏的日志: 拼字符串, 无上下文
logger.info(f"订单 {order.id} 支付成功, 金额 {order.amount}")
一句话:可观测性平台的「数据质量」由日志规范决定——结构化 + 关联 ID 是海量日志能被高效检索的前提。
四、关键流程
4.1 分布式追踪关联
用户请求 → 网关生成 traceId, 注入 header (X-Trace-Id)
→ A服务 生成 span(耗时) → B服务 子 span → C服务 ...
→ SDK 采样(头部采样/尾采样) → 上报 Kafka
→ 追踪服务 按 traceId 聚合成链路树 → 存储 → 查看瀑布图
→ 日志与指标都带 traceId, 可「日志↔链路↔指标」跳转
采样策略:
| 策略 | 说明 | 适用 |
|---|---|---|
| 头部采样 | 固定比例(如 10%)整链路采样 | 简单,但热点链路可能被漏 |
| 尾采样 | 按结果/耗时条件采样 | 保留慢/错链路,成本更高 |
| 动态采样 | 结合 QPS/错误率调整比例 | 大规模生产 |
4.2 一次故障排查时序
用户投诉 → 看板发现错误率上升 → 告警触发 → 打开链路追踪定位慢/错服务
→ 关键字检索该服务日志(traceId) → 定位根因(如数据库慢查询)
→ 修复 → 告警恢复 → 复盘(事后分析, 补 SLO/告警规则)
一句话:可观测性三件套合起来就是「排查飞轮」——指标发现问题、追踪缩小范围、日志定位根因。
4.3 日志 ↔ 指标 ↔ 追踪的关联视图
数据分型存储,但排查要「一张图」:
- 三个系统共用 时间戳 + service + trace_id 作为关联键。
- 在链路瀑布图里点击一个 span → 直接跳到该时段的日志(按 trace_id 过滤)和该服务的指标曲线。
- 看板以「服务拓扑」组织:每个节点显示健康指标,点击穿透到链路与日志。
关联视图的实现:统一维护「trace_id → 日志/指标的元数据」映射(写入端带 tag,查询端拼条件),本质是「元数据一致 + 查询跳转」,不强制三套数据放同一存储。
五、高可用与成本控制
5.1 高可用
- 采集端:Agent 本地缓冲(磁盘),Kafka 不可用时不丢日志。
- 管道:Kafka 多副本 + 多 broker,消费组重平衡自动恢复。
- 存储:ClickHouse/ES 多副本 + 跨可用区;故障时降级为「只写不查」或「读本地热数据」。
- 告警:规则引擎多实例 + 分布式锁,避免重复告警。
5.2 成本控制(核心面试点)
| 手段 | 收益 | 代价 |
|---|---|---|
| 采样 | 日志/链路减少 50-90% | 覆盖不完全,需保证关键日志全量 |
| 分层存储 | 热 7 天(SSD) → 温 30 天 → 冷(对象存储) | 冷数据查询慢 |
| 压缩 + 批量 | 存储/带宽省 3-5 倍 | 端到端延迟略增 |
| 字段裁剪 | 只存必需字段 | 排查时缺字段 |
| 低基数字段优化 | LowCardinality 省空间 | 高基数需 rehash |
优先级:关键业务日志不采样、INFO 以下采样、调试日志不入生产;告警依赖的指标全量保留。
5.3 采样配置示例
def should_sample(log_event):
# 错误/关键路径全量,INFO 按比例
if log_event.level in ("ERROR", "WARN"): return True
if log_event.trace_id in BLACKLIST_SLOW_TRACES: return True
return hash(log_event.service) % 10 == 0 # 10% INFO
5.4 告警自监控与容量
可观测系统自身也要被观测:
- 管道健康:Kafka 消费 lag、写入积压、采集端缓冲水位,超过阈值自告警。
- 存储健康:磁盘水位、写入拒绝率、查询慢日志。
- 告警系统 HA:规则引擎多副本 + 分布式锁,避免单点挂了无人告警。
- 容量规划:按大促峰值 × 1.5 冗余预留写入与存储,告警系统第一个不能被压垮。
一句话:监控系统的「最后一公里」是自监控——管道积压、存储写满、规则引擎失联都要有兜底告警。
六、查询与可视化
- 查询语言:LogQL(Loki)/ PromQL(指标)/ SQL(ClickHouse)统一 DSL,支持按 service + 关键字 + 时间范围。
- 看板:Grafana 统一面板,按服务/环境/集群分层。
- SLO 看板:可用性、延迟(Apdex)、错误预算消耗,驱动告警优先级。
- 相关性视图:同一 traceId 一键跳转日志、指标,减少排查跳转成本。
6.2 SLO 与错误预算驱动
告警不应该只看「某个指标超阈值」,而要以 SLO(服务目标)+ 错误预算 为纲:
| 概念 | 说明 |
|---|---|
| SLI | 服务质量指标:可用性(成功/总请求)、延迟(Apdex)、饱和度 |
| SLO | 目标:如「月可用性 99.9%」「P95 延迟 < 200ms」 |
| 错误预算 | 1 - SLO:如 99.9% 对应每月允许 43 分钟不可用 |
| 消耗速率 | 错误预算消耗越快,越应触发告警与冻结高风险发布 |
用错误预算消耗率告警(而非绝对阈值)能大幅降噪:短暂抖动不告警,消耗加速才告警,把告警留给「用户可感知」的问题。
七、性能与扩展
- 写入路径:从 Agent → Kafka → 存储全程批量 + 压缩,单节点日志写入可达百万级/秒(ClickHouse)。
- 查询优化:时间分区裁剪、service 低基数字段先过滤、预聚合(rollup)加速看板。
- 水平扩展:Kafka broker、ClickHouse 分片、查询网关全部可加机器扩展。
- 容量规划:按「峰值 × 1.5 冗余」预留写入带宽,按保留期规划存储层。
八、权衡与备选
| 决策点 | 本文选型 | 备选 | 权衡 |
|---|---|---|---|
| 日志存储 | ClickHouse | Elasticsearch / Loki | CH 写入快、成本低;ES 全文强;Loki 最省 |
| 指标存储 | VictoriaMetrics | Prometheus + Thanos | VM 高基数友好;Prom 生态标准 |
| 链路存储 | ClickHouse 表 | Tempo / Jaeger | CH 统一运维;Tempo 与 Grafana 集成佳 |
| 采集 | OTel Agent + Filebeat | 全量自研 | OTel 标准开放;自研可深度定制 |
| 告警引擎 | 自研 + 规则引擎 | Prometheus Alertmanager | 自研可做去重降噪;Alertmanager 开箱即用 |
取舍原则
- 成本 > 极端完整:采样 + 分层是标配,追求 100% 全覆盖不可负担。
- 告警质量 > 告警数量:宁可少而准,不要告警风暴。
- 开放标准 > 锁定:优先 OTel、Prom 生态,避免被单一厂商锁死。
九、扩展场景与面试追问
9.1 全链路压测与容量规划
可观测性系统本身也要「可观测、可压测」:
- 压测时注入海量日志,验证「写入管道不丢不堵、查询不拖垮」。
- 提前规划 Kafka/存储容量:按业务高峰(大促)的 1.5 倍峰值预留,避免告警系统第一个被压垮。
- 告警系统自身的告警:采集积压、Kafka 延迟、存储写满都要有自监控。
9.2 边缘场景:K8s 与 Serverless
- K8s:Agent 以 DaemonSet 部署,Pod 漂移不影响采集;日志带 namespace/pod 标签。
- Serverless:函数冷启动无常驻 Agent → 用平台侧日志转发 / OTel SDK 直发,注意冷启动期间的日志不丢。
9.3 日志合规与脱敏
生产日志可能包含用户隐私(手机号、身份证、卡号),合规设计:
- 采集端脱敏:Agent 侧正则/字段规则打码(
138****1234),源头不落原始 PII。 - 权限隔离:日志按业务线/环境分权限,敏感日志只有指定角色可查(RBAC + 审计)。
- 保留期治理:按数据类型设置不同 TTL,合规要求保留的冷存,其余到期清理。
- 审计追踪:谁在什么时间查了谁的日志,本身要留痕。
9.4 面试常见追问
| 追问 | 关键回答 |
|---|---|
| 日志太多查不快怎么办? | 时间分区裁剪 + 低基数字段前置过滤 + 采样 + 分阶段查询(先粗筛后精确) |
| 采集端宕机会丢日志吗? | Agent 本地缓冲 + 重试,缓冲满才丢弃(可配丢弃策略),关键业务双路冗余 |
| 告警风暴怎么治? | 告警分组 + 聚合去重 + 静默期 + 依赖树(根因告警优先)+ SLO 消耗率 |
| trace 采样会不会漏掉故障? | 尾采样按错误/慢响应优先保留,保证故障链路不丢 |
| 日志与指标选型怎么定? | 日志看全文、指标看趋势:日志量大用 CH/Loki,指标用 TSDB;按查询场景各取所长 |
十、总结
| 模块 | 关键点 | 一句话记忆 |
|---|---|---|
| 采集 | Agent 批量 + 本地缓冲 | 日志不能因传输抖动而丢 |
| 管道 | Kafka 削峰 + 压缩 | 写与处理解耦 |
| 存储 | 日志 CH / 指标 TSDB / 链路 CH | 各司其职、按数据类型选 |
| 追踪 | traceId 贯穿 + 采样 | 故障排查的导航仪 |
| 告警 | 规则 + 去重 + 路由 | 数据变行动 |
| 成本 | 采样 + 分层 + 低基数 | 可观测性的经济学 |
一句话:日志监控题的面试主线是「采集 → 传输 → 存储 → 查询 → 告警」管道 + 「日志/指标/追踪」三件套 + 「采样/分层」成本控制,讲清写放大与查性能的权衡,就抓住了全部考点。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。