告别散落日志:用华为云 CSS 从零搭建企业级日志分析与全文检索平台

举报
yd_280727371 发表于 2026/11/02 09:39:40 2026/11/02
【摘要】 摘要:微服务化后,日志散落在几十个节点上,grep 翻日志效率极低,出了问题靠“人肉排错”。本文以 Spring Boot 微服务集群为例,基于华为云云搜索服务 CSS(Elasticsearch + Kibana),结合 LTS(日志传输)和 Logstash,搭建集中式日志采集、存储、检索与可视化平台,实现秒级日志查询、错误实时告警和接口性能分析,把运维从“翻日志”升级到“看面板”。标签...
摘要:微服务化后,日志散落在几十个节点上,grep 翻日志效率极低,出了问题靠“人肉排错”。本文以 Spring Boot 微服务集群为例,基于华为云云搜索服务 CSS(Elasticsearch + Kibana),结合 LTS(日志传输)和 Logstash,搭建集中式日志采集、存储、检索与可视化平台,实现秒级日志查询、错误实时告警和接口性能分析,把运维从“翻日志”升级到“看面板”。
标签建议:云搜索服务 CSS、日志分析、Elasticsearch、可观测性、云端实践

一、背景与痛点

订单、用户、库存三个微服务各部署 4 个实例,日志分散在 12 台机器上。日常排错流程:
# 1. 登录跳板机
# 2. 找到对应节点
ssh order-node-03
# 3. grep 关键字
grep "OutOfMemory" /var/log/app/order-service.log | tail -100
# 4. 时间不对还得切文件
grep "2024-01-15 14:3" /var/log/app/order-service.log.2024-01-15-14
痛点清单:

问题
具体表现
定位慢
不知道报错在哪个节点,逐个登录翻,平均 15 分钟起
跨服务追踪难
一个请求经过 3 个服务,靠 traceId 手动串联
无法聚合分析
想知道“过去 1 小时 500 错误最多的接口”,做不到
日志易丢失
节点重启、磁盘满,日志直接被覆盖
无主动告警
错误只有用户反馈了才知道
核心诉求:日志集中存、秒级搜、自动告警、可视化。

二、方案设计

2.1 为什么用 CSS 而不是自建 ELK


维度
自建 ELK
华为云 CSS
部署运维
自己装 ES + Kibana + Logstash
控制台一键创建集群
扩容
手动加节点、改分片
在线扩节点/磁盘
高可用
自己配主从、快照
多 AZ 自动分布
安全
自己搞 TLS + 账号体系
内置 RBAC + HTTPS + 审计
成本
机器 + 人力
按需付费,免运维人力
小团队选 CSS 的核心原因:不用养一个 Elasticsearch 专家。

2.2 日志链路架构

微服务实例(Spring Boot)
   │
   │ Filebeat / LTS 采集
   ▼
日志汇聚层(Logstash / LTS 直接转发)
   │
   ├─ 过滤:grok 解析、字段提取
   ├─ 富化:添加 env、service_name、trace_id
   └─ 路由:按索引模板分发
   │
   ▼
CSS 集群(Elasticsearch)
   │
   ├─ 索引:按月滚动(logs-2024-01)
   ├─ 分片:3 primary + 1 replica
   └─ ILM:热温冷数据生命周期管理
   │
   ▼
Kibana 可视化
   │
   ├─ Discover:秒级全文检索
   ├─ Dashboard:错误率/吞吐量/延迟面板
   └─ Alerting:错误突增告警

2.3 索引设计

按服务 + 时间分索引:
logs-order-service-2024.01
logs-user-service-2024.01
logs-inventory-service-2024.01
好处:
  • 按时间删除旧数据,直接删索引,不影响写入
  • 按服务查时只搜对应索引,性能高
  • 不同服务字段结构不同,mapping 互不干扰

三、实操步骤

3.1 创建 CSS 集群

  1. 进入云搜索服务 CSS 控制台 → 创建集群
  2. 集群类型:Elasticsearch
  3. 版本:7.10.2(稳定版,与 Spring Boot 生态兼容好)
  4. 节点规格:
    • 热节点:ess.spec.m6.large.8(4U32G)× 3
    • 数据盘:超高 I/O,200GB/节点
  5. 网络:选业务同 VPC,安全组开放 9200(内网)
  6. 开启 Kibana:自动分配访问地址,绑定 EIP 或走 VPN 访问
  7. 安全模式:开启 HTTPS + 账号认证
避坑:节点数至少 3 个才能保证主节点选举的高可用。单节点只适合开发测试。

3.2 Spring Boot 应用日志改造

引入 Logback + Logstash Encoder:
<!-- pom.xml -->
<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  <version>7.4</version>
</dependency>
logback-spring.xml 配置:
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
  <destination>logstash.internal:5000</destination>
  <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
    <providers>
      <timestamp/>
      <logLevel/>
      <message/>
      <loggerName/>
      <threadName/>
      <mdc/>          <!-- 输出 MDC 中的 traceId -->
      <stackTrace/>
      <pattern>
        <pattern>
          {
            "service": "order-service",
            "env": "prod",
            "node_ip": "${HOSTNAME}"
          }
        </pattern>
      </pattern>
    </providers>
  </encoder>
</appender>

<root level="INFO">
  <appender-ref ref="CONSOLE"/>
  <appender-ref ref="LOGSTASH"/>
</root>
MDC 中放入 traceId(通过拦截器或 Spring Cloud Sleuth 自动注入):
// 拦截器中
MDC.put("trace_id", UUID.randomUUID().toString());
输出到 Logstash 的 JSON 格式示例:
{
  "timestamp": "2024-01-15T14:32:10.123Z",
  "level": "ERROR",
  "service": "order-service",
  "env": "prod",
  "trace_id": "a1b2c3d4-1234-5678",
  "message": "Failed to create order: inventory deduction timeout",
  "stack_trace": "..."
}

3.3 Logstash 管道配置

Logstash 部署在独立 ECS 或容器中,接收 Filebeat/TCP 日志并写入 CSS:
# pipeline/logs.conf
input {
  tcp {
    port => 5000
    codec => json_lines
  }
}

filter {
  # 提取时间戳
  date {
    match => ["timestamp", "ISO8601"]
    target => "@timestamp"
  }

  # 按服务名路由
  mutate {
    add_field => {
      "index_prefix" => "%{service}"
    }
  }

  # 解析异常堆栈为多行
  if [stack_trace] {
    mutate {
      gsub => ["stack_trace", "\n", " | "]
    }
  }
}

output {
  elasticsearch {
    hosts => ["https://css-cluster-endpoint:9200"]
    user => "admin"
    password => "${CSS_PASSWORD}"
    index => "logs-%{service}-%{+YYYY.MM}"
    ssl_certificate_verification => false
  }
}

3.4 索引模板与 ILM 策略

在 Kibana Dev Tools 中创建索引模板:
PUT _index_template/logs-template
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "5s",
      "index.lifecycle.name": "logs-ilm-policy"
    },
    "mappings": {
      "properties": {
        "timestamp": { "type": "date" },
        "level": { "type": "keyword" },
        "service": { "type": "keyword" },
        "env": { "type": "keyword" },
        "trace_id": { "type": "keyword" },
        "message": { "type": "text", "analyzer": "ik_max_word" },
        "stack_trace": { "type": "text" },
        "node_ip": { "type": "ip" }
      }
    }
  }
}
ILM 生命周期策略(热→温→冷→删除):
PUT _ilm/policy/logs-ilm-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "7d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

3.5 Kibana 可视化与告警

① Discover 搜索
  • 索引模式:logs-*
  • 常用查询:
    • level:ERROR AND service:order-service
    • trace_id:"a1b2c3d4-1234-5678" (全链路追踪)
    • message:"OutOfMemory" AND timestamp:>now-1h
② Dashboard 面板
创建关键图表:

面板
查询
可视化类型
错误率趋势
level:ERROR 按时间聚合
折线图
服务吞吐量
所有日志按 service 分组计数
柱状图
Top 10 错误接口
level:ERROR + 正则提取 URL
数据表
P99 响应时间
从 MDC 提取 duration_ms 字段
直方图
节点日志分布
按 node_ip 聚合
饼图
③ 告警配置
Kibana → Stack Management → Alerts:
  • 规则:Error count threshold
  • 条件:level:ERROR 在 5 分钟内 > 50 条
  • 动作:Webhook 推送到企业微信群
{
  "msgtype": "markdown",
  "markdown": {
    "content": "## 🚨 日志告警\n**服务**:{{context.service}}\n**错误数**:{{context.hits}}\n**时间**:{{context.timestamp}}\n[查看详情]({{context.kibana_url}})"
  }
}

四、验证结果


验证项
操作
预期
日志实时写入
触发一个接口异常
5 秒内 Kibana Discover 可见
全文检索
搜 trace_id
跨 3 个服务的日志全部串联
聚合统计
Dashboard 刷新
错误率/吞吐量实时更新
告警触发
手动制造 60 条 ERROR
企业微信 1 分钟内收到告警
数据过期
等 90 天或手动触发 ILM
旧索引自动删除
节点故障
停一个 CSS 数据节点
集群自动重分配分片,写入不中断
排错效率对比:

指标
改造前
改造后
定位错误所在节点
5-15 分钟
5 秒(Kibana 直接显示 node_ip)
跨服务追踪请求
不可行
trace_id 一键串联
统计过去 1 小时错误分布
手动写脚本
Dashboard 实时展示
发现异常
用户投诉
告警主动推送

五、生产避坑清单

❌ 常见错误
  1. 所有日志写同一个索引:单索引膨胀到 TB 级,查询慢、分片恢复慢
  2. 字段全用 text 类型:text 不分词无法精确聚合,该用 keyword 的用 keyword
  3. 不配 ILM 策略:日志无限堆积,磁盘撑满集群只读
  4. Logstash 单点:Logstash 挂了日志全丢,至少部署 2 个实例 + Filebeat 持久化
  5. JVM 堆内存超过 32GB:ES 禁用压缩指针,反而性能下降
  6. 分片数过多:一个节点上千个分片,集群状态更新卡顿
  7. 没开慢查询日志:ES 自身慢查询不监控,排查时不知道是 ES 慢还是应用慢
  8. Kibana 直接暴露公网:安全风险,应走 VPN 或堡垒机
✅ 正确姿势
  • 索引按服务 + 月份滚动,单索引控制在 50GB 以内
  • mapping 提前设计:日志级别、服务名、trace_id 用 keyword
  • ILM 四阶段:热(7天)→ 温(压缩)→ 冷(冻结)→ 删(90天)
  • Logstash 至少 2 节点,Filebeat 开持久化队列
  • ES 堆内存设物理内存的 50%,不超过 32GB
  • 分片数 = 数据节点数 × 1~3,不要过度分片
  • 监控 ES 自身:CPU、JVM GC、线程池队列、索引延迟
  • Kibana 走内网 + 反向代理鉴权,不直挂公网

六、成本结构


计费项
说明
优化建议
节点计算
按节点规格和数量
热节点用高 IO 型,冷数据阶段用低配节点
存储
高性能云盘
ILM 自动降冷,历史数据用大容量盘
公网带宽
Kibana 访问
走内网或 VPN,省公网流量
备份存储
快照到 OBS
定期备份,OBS 低频存储降成本
经验:日志量 < 50GB/天 的小集群,3 个 4U32G 节点足够扛住。超过 200GB/天 需要单独规划热温架构。

七、总结与扩展

日志平台的价值不只是“存日志”,而是把运维经验数据化:
  • 从被动到主动:告警在用户感知前触发
  • 从模糊到精确:5 秒定位到哪个节点、哪个接口、哪行代码
  • 从孤岛到关联:trace_id 串联全链路,跨服务问题一目了然
后续演进:
  1. 接入 APM Trace 数据:把 SkyWalking/Zipkin span 也写入 ES,日志 + 链路统一检索
  2. 机器学习异常检测:ES ML 自动学习错误率基线,异常偏离自动告警
  3. 安全日志审计:将 WAF、HSS 安全日志也汇入 CSS,做安全事件关联分析
  4. 日志即证据:结合 LTS 做合规审计,满足等保日志留存要求
  5. Serverless 日志:函数工作流日志自动接入,无服务器架构也有完整可观测性

本文为原创日志平台实践,基于华为云云搜索服务 CSS 整理,操作步骤以当前控制台页面为准。转载请注明出处。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。