集中式日志平台生产实践:Loki与OpenSearch日志治理

日志平台不是把所有文本集中到一个搜索框。生产系统需要同时解决采集可靠性、字段规范、敏感信息、索引与标签基数、存储成本、查询性能、权限隔离、保留与删除,以及平台自身故障后的恢复。缺少治理时,日志量通常增长得比业务更快,真正发生故障时反而查不到关键证据。

本文以Grafana Loki和OpenSearch两类常见路线为例,建立从日志源到查询、告警和归档的完整设计。两者的数据模型不同,不应照搬同一套字段与容量经验。具体配置应结合当前版本官方文档验证。

一、先定义日志使用场景

每类日志先明确主要用途:

类型 主要用途 典型保留
应用运行日志 故障定位、发布验证 数天至数周
访问日志 性能、错误、流量分析 数周至数月
审计日志 责任追踪、合规 按制度长期保存
安全日志 威胁检测、溯源 按风险和法规确定
数据库慢日志 SQL优化、容量分析 数周至数月
基础设施日志 节点、网络、存储排障 数周
调试日志 临时诊断 短期、默认关闭

不能只设置一个统一保留期。审计日志需要防篡改,调试日志则应快速过期;两者放在同一策略中会造成成本和合规风险。

二、日志平台的端到端链路

标准链路包括:

  1. 应用或系统产生日志;
  2. 节点Agent、Sidecar或Collector读取;
  3. 解析、补充元数据、过滤和脱敏;
  4. 缓冲并发送到集中入口;
  5. 平台进行索引、分块或持久化;
  6. 查询层完成检索、聚合和权限控制;
  7. 告警规则生成事件;
  8. 生命周期策略完成降级、归档或删除。

每个阶段都可能丢数据。需要给日志记录一个可追踪的时间线:产生时间、采集时间、接收时间、可查询时间,从而区分应用延迟、Agent积压和平台写入延迟。

三、统一日志格式

新应用优先输出结构化JSON,而不是依赖复杂正则解析。

{
  "timestamp": "2026-10-22T09:30:12.314+08:00",
  "level": "ERROR",
  "service": "order-api",
  "environment": "prod",
  "message": "payment provider timeout",
  "trace_id": "f9c...",
  "request_id": "r8a...",
  "error_type": "upstream_timeout",
  "duration_ms": 3021
}

字段规范至少约定:

  • 时间使用带时区的ISO 8601或统一UTC;
  • level集合统一;
  • service、environment、cluster、namespace命名稳定;
  • trace_id/request_id保留在字段中,不盲目成为索引或标签;
  • 错误类型使用可聚合值,message保留人类可读上下文;
  • 禁止直接记录密码、Token、Cookie、身份证号和完整密钥。

多行堆栈应在采集端正确合并,否则每一行都会变成独立事件。

四、采集Agent如何选择

常见选择包括Grafana Alloy、OpenTelemetry Collector、Fluent Bit、Vector和厂商Agent。评估维度:

  • 支持的输入、输出和解析能力;
  • 本地磁盘缓冲;
  • 背压与重试;
  • 多行日志;
  • Kubernetes元数据;
  • 脱敏和采样;
  • CPU、内存与磁盘占用;
  • 配置热加载;
  • 自监控指标;
  • 升级和兼容策略。

Agent使用DaemonSet时要配置资源上限和优先级,但不能因内存上限过低反复OOM。节点磁盘缓冲必须有容量上限,防止日志平台故障拖满业务节点。

五、采集可靠性与背压

日志平台短暂不可用时,Agent应缓冲并重试;长时间不可用时,需要明确取舍:

  • 最多缓存多少小时;
  • 磁盘达到水位后删除最旧还是停止读取;
  • 哪些日志优先保留;
  • 是否允许对低价值日志采样;
  • 恢复后以多大速率回放,避免冲垮平台。

必须监控输入速率、输出速率、缓冲字节、最老未发送日志时间、重试、丢弃和解析失败。只监控Agent进程存活无法发现静默丢日志。

六、Loki的数据模型

Loki主要索引标签,日志正文压缩存入chunk。每组唯一标签组合形成一条stream。标签应描述稳定、低基数的来源,例如:

cluster=prod-shanghai
namespace=payments
app=order-api
environment=prod

不要把Pod UID、请求ID、用户ID、客户端IP或trace_id作为普通索引标签,否则会产生大量stream并显著影响写入、查询和存储。高基数字段可使用结构化元数据或在查询时解析。

LogQL查询示例:

{cluster="prod-shanghai", namespace="payments", app="order-api"}
  | json
  | level="ERROR"
  | duration_ms > 2000

查询先用标签缩小stream,再解析正文,避免对全部日志执行正则。

七、Loki部署模式

常见模式包括:

  • 单体模式:实验和小规模;
  • 简单可扩展模式:读、写、backend分组;
  • 微服务模式:组件独立扩展,适合更大规模。

生产架构需关注Distributor、Ingester、Querier、Query Frontend、Compactor、对象存储和ring。不要因为组件能水平扩展就忽略故障域:副本应跨节点或可用区,ring状态和对象存储必须可观测。

Loki通常把索引和chunk持久化到对象存储。TSDB索引是当前主路线,新增部署应以官方最新推荐为准。

八、Loki保留与删除

保留通常由Compactor执行,而不是等待Ingester自动删除。示意配置:

compactor:
  working_directory: /data/loki/compactor
  retention_enabled: true
  delete_request_store: s3

limits_config:
  retention_period: 360h

需要验证:

  • Compactor实际运行且只有正确的工作模式;
  • 对象存储生命周期不会提前删除数据;
  • 不同租户和stream是否需要不同保留;
  • 删除请求是否有审计和延迟窗口;
  • 法务保留是否能阻止自动删除。

保留策略改变后,空间不会立即下降,应观察Compactor指标和对象删除进度。

九、OpenSearch的数据模型

OpenSearch对文档字段建立倒排索引,适合复杂全文搜索、字段聚合和安全分析,但映射、分片和索引数量必须治理。

推荐使用索引模板统一:

  • 时间字段类型;
  • keyword与text的边界;
  • IP、数值、布尔和嵌套字段;
  • 动态映射策略;
  • 分片、副本与刷新间隔;
  • 生命周期策略和别名。

不要让任意JSON字段自动无限扩张。字段爆炸会增加集群状态、内存和查询成本。对不需要检索的原文可关闭索引,仅保留_source。

十、分片不是越多越好

OpenSearch分片过大会降低迁移和恢复速度,过小则增加集群状态、文件句柄和堆内存开销。容量规划应基于每日写入量、保留期、压缩比、节点磁盘、查询并发和恢复时间测试。

建议:

  • 使用rollover按大小或时间滚动;
  • 让主分片大小落在经压测验证的范围;
  • 为热写入、温查询和冷归档设计不同层;
  • 副本数按可用性和查询吞吐决定;
  • 控制每节点分片数量;
  • 预留节点故障后的重新分配空间。

不要把所有日志按“每天一个索引”机械拆分。低流量业务会产生大量小索引,可通过数据流、共享索引与租户字段治理。

十一、生命周期与冷热分层

典型策略:

  1. 热层:高性能磁盘,接收写入和近期查询;
  2. 温层:停止写入、降低副本或迁移到普通磁盘;
  3. 冷层:低频查询、快照或远程存储;
  4. 删除:超过保留期且无保留令。

生命周期动作必须在测试索引验证。发生分片未分配、磁盘水位或快照失败时,自动策略可能停滞,需要告警而不是静默等待。

十二、租户、权限与审计

日志常包含内部地址、用户标识和业务上下文。至少实现:

  • 单点登录与MFA;
  • 团队/环境级最小权限;
  • 生产日志与测试日志隔离;
  • 查询和导出审计;
  • 敏感字段脱敏;
  • API Token短期化和轮换;
  • 批量下载限制;
  • 离职和项目结束后的权限回收。

多租户不只是一列tenant字段。必须确保写入入口不能伪造租户、查询层强制过滤、对象存储前缀和缓存不会跨租户泄露。

十三、日志脱敏与隐私

脱敏尽量在数据离开源环境前完成。常见规则:

  • 密码、Token、Authorization头直接删除;
  • 手机号、邮箱、身份证号按制度掩码;
  • URL查询参数使用白名单;
  • SQL绑定参数按风险决定是否保留;
  • 堆栈中的请求对象避免完整输出;
  • 测试脱敏规则对Unicode、换行和编码的处理。

脱敏失败应计数和告警,但不要把原始敏感文本再次写入错误日志。规则变更需要样本回归测试。

十四、日志、指标和追踪关联

统一service、environment、cluster等资源属性。应用日志记录trace_id,在日志界面链接到追踪;从追踪跳转回同一时间范围的日志;告警携带服务、集群和Runbook。

关联字段不等于全部做成索引。Loki中高基数trace_id更适合结构化元数据或查询时解析;OpenSearch可将trace_id设为keyword,但要评估写入和存储成本。

十五、从日志生成告警

适合日志告警的场景:

  • 明确错误码或安全事件;
  • 没有指标暴露的遗留系统;
  • 审计操作;
  • 低频但严重的异常。

不适合把所有日志关键字都转成告警。高频错误应先转为指标并按错误率、影响面和持续时间告警。规则要处理“没有日志”和“采集停止”的区别。

十六、查询治理

建立以下保护:

  • 默认查询时间范围;
  • 最大扫描量和并发;
  • 慢查询记录;
  • 正则和通配符限制;
  • 租户公平调度;
  • 查询缓存;
  • 大批量导出审批;
  • 仪表盘固定字段和索引范围。

故障期间用户会同时扩大时间范围和刷新频率,查询峰值可能高于平时数倍。应为应急查询预留容量,同时保护写入链路。

十七、容量与成本模型

每日容量可从源日志字节、采集开销、压缩比、副本、索引、保留期和安全余量估算。还要计算对象存储请求、跨区流量、查询缓存和快照成本。

治理顺序通常是:

  1. 禁止无界debug;
  2. 修正高基数标签和动态字段;
  3. 按价值设置保留;
  4. 对低价值重复日志采样;
  5. 优化格式和压缩;
  6. 最后才是增加存储。

成本看板应按团队、服务、环境和日志类型归属,让产生者看到自身增量。

十八、平台自身监控

至少监控:

  • 每租户写入速率与拒绝;
  • Agent积压、丢弃和解析失败;
  • Loki stream、chunk、ring和Compactor;
  • OpenSearch集群状态、堆、GC、分片和磁盘水位;
  • 对象存储错误和延迟;
  • 查询QPS、P95/P99、超时和取消;
  • 生命周期任务与快照;
  • 从生成到可查询的端到端延迟。

建立合成日志:定时写入唯一标识,持续验证它能在目标时间内被查询到。

十九、备份与灾难恢复

需要保护:

  • Agent与平台配置;
  • 索引模板、生命周期和解析规则;
  • 租户、权限与审计配置;
  • 仪表盘、保存查询和告警;
  • 对象存储或OpenSearch快照;
  • 加密密钥与凭据;
  • 恢复Runbook。

恢复演练要验证查询结果、时间范围、字段映射、权限和告警,不是只确认对象或快照存在。

二十、迁移路线

  1. 盘点日志源、日增量、保留和使用者;
  2. 建立字段与敏感信息规范;
  3. 选择少量服务双写;
  4. 比对条数、延迟、字段、查询和告警;
  5. 建立容量与成本基线;
  6. 分批迁移仪表盘和告警;
  7. 保留旧平台只读窗口;
  8. 验证审计和恢复后再下线。

双写会增加应用和网络负担,应在Collector层完成并监控两个出口,避免由应用重复发送。

二十一、常见故障

日志条数突然减少

检查应用是否仍输出、Agent文件偏移、容器轮转、解析失败、限流、网络和平台拒绝。不能只看平台入口。

Loki查询非常慢

先缩小标签和时间范围,检查高基数stream、无界正则、查询拆分、缓存、Store Gateway与对象存储。

OpenSearch集群变黄或红

检查未分配分片原因、节点磁盘水位、分片副本、节点角色和恢复限速。不要直接删除索引腾空间。

磁盘每天都增长

核对生命周期是否实际执行、Compactor或ISM任务状态、对象存储删除延迟、快照和副本占用。

二十二、生产验收清单

  • [ ] 日志源、用途、负责人和保留策略已登记;
  • [ ] 结构化字段、时间和level规范统一;
  • [ ] Agent具备磁盘缓冲、背压和丢弃告警;
  • [ ] Loki标签或OpenSearch映射经过基数审查;
  • [ ] 敏感信息在采集前脱敏;
  • [ ] 租户、环境和导出权限隔离;
  • [ ] 生命周期、对象存储和快照策略一致;
  • [ ] 写入、查询和保留任务均可观测;
  • [ ] 日志与指标、追踪可以关联;
  • [ ] 已完成隔离恢复和权限验证。

二十三、官方参考

可靠的日志平台必须回答三个问题:日志有没有完整到达、用户能否在需要时快速找到、保存和删除是否符合制度。只有把采集、模型、权限、成本和恢复统一治理,集中日志才会真正成为生产证据。

Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-10-22 21:12:24

results matching ""

    No results matching ""